337 lines
11 KiB
C#
337 lines
11 KiB
C#
// ==++==
|
|
//
|
|
// Copyright (c) Microsoft Corporation. All rights reserved.
|
|
//
|
|
// ==--==
|
|
/*============================================================
|
|
**
|
|
** Class: ResourceSet
|
|
**
|
|
** <OWNER>[....]</OWNER>
|
|
**
|
|
**
|
|
** Purpose: Culture-specific collection of resources.
|
|
**
|
|
**
|
|
===========================================================*/
|
|
namespace System.Resources {
|
|
using System;
|
|
using System.Collections;
|
|
using System.IO;
|
|
using System.Globalization;
|
|
using System.Security.Permissions;
|
|
using System.Runtime.InteropServices;
|
|
using System.Reflection;
|
|
using System.Runtime.Serialization;
|
|
using System.Runtime.Versioning;
|
|
using System.Diagnostics.Contracts;
|
|
|
|
// A ResourceSet stores all the resources defined in one particular CultureInfo.
|
|
//
|
|
// The method used to load resources is straightforward - this class
|
|
// enumerates over an IResourceReader, loading every name and value, and
|
|
// stores them in a hash table. Custom IResourceReaders can be used.
|
|
//
|
|
[Serializable]
|
|
[System.Runtime.InteropServices.ComVisible(true)]
|
|
public class ResourceSet : IDisposable, IEnumerable
|
|
{
|
|
[NonSerialized] protected IResourceReader Reader;
|
|
#if FEATURE_CORECLR
|
|
internal Hashtable Table;
|
|
#else
|
|
protected Hashtable Table;
|
|
#endif
|
|
|
|
private Hashtable _caseInsensitiveTable; // For case-insensitive lookups.
|
|
|
|
#if LOOSELY_LINKED_RESOURCE_REFERENCE
|
|
[OptionalField]
|
|
private Assembly _assembly; // For LooselyLinkedResourceReferences
|
|
#endif // LOOSELY_LINKED_RESOURCE_REFERENCE
|
|
|
|
protected ResourceSet()
|
|
{
|
|
// To not inconvenience people subclassing us, we should allocate a new
|
|
// hashtable here just so that Table is set to something.
|
|
CommonInit();
|
|
}
|
|
|
|
// For RuntimeResourceSet, ignore the Table parameter - it's a wasted
|
|
// allocation.
|
|
internal ResourceSet(bool junk)
|
|
{
|
|
}
|
|
|
|
// Creates a ResourceSet using the system default ResourceReader
|
|
// implementation. Use this constructor to open & read from a file
|
|
// on disk.
|
|
//
|
|
#if FEATURE_CORECLR
|
|
[System.Security.SecurityCritical] // auto-generated
|
|
#endif
|
|
[ResourceExposure(ResourceScope.Machine)]
|
|
[ResourceConsumption(ResourceScope.Machine)]
|
|
public ResourceSet(String fileName)
|
|
{
|
|
Reader = new ResourceReader(fileName);
|
|
CommonInit();
|
|
ReadResources();
|
|
}
|
|
|
|
#if LOOSELY_LINKED_RESOURCE_REFERENCE
|
|
public ResourceSet(String fileName, Assembly assembly)
|
|
{
|
|
Reader = new ResourceReader(fileName);
|
|
CommonInit();
|
|
_assembly = assembly;
|
|
ReadResources();
|
|
}
|
|
#endif // LOOSELY_LINKED_RESOURCE_REFERENCE
|
|
|
|
// Creates a ResourceSet using the system default ResourceReader
|
|
// implementation. Use this constructor to read from an open stream
|
|
// of data.
|
|
//
|
|
[System.Security.SecurityCritical] // auto-generated_required
|
|
public ResourceSet(Stream stream)
|
|
{
|
|
Reader = new ResourceReader(stream);
|
|
CommonInit();
|
|
ReadResources();
|
|
}
|
|
|
|
#if LOOSELY_LINKED_RESOURCE_REFERENCE
|
|
[System.Security.SecurityCritical] // auto_generated_required
|
|
public ResourceSet(Stream stream, Assembly assembly)
|
|
{
|
|
Reader = new ResourceReader(stream);
|
|
CommonInit();
|
|
_assembly = assembly;
|
|
ReadResources();
|
|
}
|
|
#endif // LOOSELY_LINKED_RESOURCE_REFERENCE
|
|
|
|
public ResourceSet(IResourceReader reader)
|
|
{
|
|
if (reader == null)
|
|
throw new ArgumentNullException("reader");
|
|
Contract.EndContractBlock();
|
|
Reader = reader;
|
|
CommonInit();
|
|
ReadResources();
|
|
}
|
|
|
|
#if LOOSELY_LINKED_RESOURCE_REFERENCE
|
|
public ResourceSet(IResourceReader reader, Assembly assembly)
|
|
{
|
|
if (reader == null)
|
|
throw new ArgumentNullException("reader");
|
|
Contract.EndContractBlock();
|
|
Reader = reader;
|
|
CommonInit();
|
|
_assembly = assembly;
|
|
ReadResources();
|
|
}
|
|
#endif // LOOSELY_LINKED_RESOURCE_REFERENCE
|
|
|
|
private void CommonInit()
|
|
{
|
|
Table = new Hashtable();
|
|
}
|
|
|
|
// Closes and releases any resources used by this ResourceSet, if any.
|
|
// All calls to methods on the ResourceSet after a call to close may
|
|
// fail. Close is guaranteed to be safely callable multiple times on a
|
|
// particular ResourceSet, and all subclasses must support these semantics.
|
|
public virtual void Close()
|
|
{
|
|
Dispose(true);
|
|
}
|
|
|
|
protected virtual void Dispose(bool disposing)
|
|
{
|
|
if (disposing) {
|
|
// Close the Reader in a thread-safe way.
|
|
IResourceReader copyOfReader = Reader;
|
|
Reader = null;
|
|
if (copyOfReader != null)
|
|
copyOfReader.Close();
|
|
}
|
|
Reader = null;
|
|
_caseInsensitiveTable = null;
|
|
Table = null;
|
|
}
|
|
|
|
public void Dispose()
|
|
{
|
|
Dispose(true);
|
|
}
|
|
|
|
#if LOOSELY_LINKED_RESOURCE_REFERENCE
|
|
// Optional - used for resolving assembly manifest resource references.
|
|
// This can safely be null.
|
|
[ComVisible(false)]
|
|
public Assembly Assembly {
|
|
get { return _assembly; }
|
|
/*protected*/ set { _assembly = value; }
|
|
}
|
|
#endif // LOOSELY_LINKED_RESOURCE_REFERENCE
|
|
|
|
// Returns the preferred IResourceReader class for this kind of ResourceSet.
|
|
// Subclasses of ResourceSet using their own Readers &; should override
|
|
// GetDefaultReader and GetDefaultWriter.
|
|
public virtual Type GetDefaultReader()
|
|
{
|
|
return typeof(ResourceReader);
|
|
}
|
|
|
|
#if !FEATURE_CORECLR
|
|
// Returns the preferred IResourceWriter class for this kind of ResourceSet.
|
|
// Subclasses of ResourceSet using their own Readers &; should override
|
|
// GetDefaultReader and GetDefaultWriter.
|
|
public virtual Type GetDefaultWriter()
|
|
{
|
|
return typeof(ResourceWriter);
|
|
}
|
|
#endif // !FEATURE_CORECLR
|
|
|
|
[ComVisible(false)]
|
|
public virtual IDictionaryEnumerator GetEnumerator()
|
|
{
|
|
return GetEnumeratorHelper();
|
|
}
|
|
|
|
/// <internalonly/>
|
|
IEnumerator IEnumerable.GetEnumerator()
|
|
{
|
|
return GetEnumeratorHelper();
|
|
}
|
|
|
|
private IDictionaryEnumerator GetEnumeratorHelper()
|
|
{
|
|
Hashtable copyOfTable = Table; // Avoid a race with Dispose
|
|
if (copyOfTable == null)
|
|
throw new ObjectDisposedException(null, Environment.GetResourceString("ObjectDisposed_ResourceSet"));
|
|
return copyOfTable.GetEnumerator();
|
|
}
|
|
|
|
// Look up a string value for a resource given its name.
|
|
//
|
|
public virtual String GetString(String name)
|
|
{
|
|
Object obj = GetObjectInternal(name);
|
|
try {
|
|
return (String)obj;
|
|
}
|
|
catch (InvalidCastException) {
|
|
throw new InvalidOperationException(Environment.GetResourceString("InvalidOperation_ResourceNotString_Name", name));
|
|
}
|
|
}
|
|
|
|
public virtual String GetString(String name, bool ignoreCase)
|
|
{
|
|
Object obj;
|
|
String s;
|
|
|
|
// Case-sensitive lookup
|
|
obj = GetObjectInternal(name);
|
|
try {
|
|
s = (String)obj;
|
|
}
|
|
catch (InvalidCastException) {
|
|
throw new InvalidOperationException(Environment.GetResourceString("InvalidOperation_ResourceNotString_Name", name));
|
|
}
|
|
|
|
// case-sensitive lookup succeeded
|
|
if (s != null || !ignoreCase) {
|
|
return s;
|
|
}
|
|
|
|
// Try doing a case-insensitive lookup
|
|
obj = GetCaseInsensitiveObjectInternal(name);
|
|
try {
|
|
return (String)obj;
|
|
}
|
|
catch (InvalidCastException) {
|
|
throw new InvalidOperationException(Environment.GetResourceString("InvalidOperation_ResourceNotString_Name", name));
|
|
}
|
|
}
|
|
|
|
// Look up an object value for a resource given its name.
|
|
//
|
|
public virtual Object GetObject(String name)
|
|
{
|
|
return GetObjectInternal(name);
|
|
}
|
|
|
|
public virtual Object GetObject(String name, bool ignoreCase)
|
|
{
|
|
Object obj = GetObjectInternal(name);
|
|
|
|
if (obj != null || !ignoreCase)
|
|
return obj;
|
|
|
|
return GetCaseInsensitiveObjectInternal(name);
|
|
}
|
|
|
|
protected virtual void ReadResources()
|
|
{
|
|
IDictionaryEnumerator en = Reader.GetEnumerator();
|
|
while (en.MoveNext()) {
|
|
Object value = en.Value;
|
|
#if LOOSELY_LINKED_RESOURCE_REFERENCE
|
|
if (Assembly != null && value is LooselyLinkedResourceReference) {
|
|
LooselyLinkedResourceReference assRef = (LooselyLinkedResourceReference) value;
|
|
value = assRef.Resolve(Assembly);
|
|
}
|
|
#endif //LOOSELYLINKEDRESOURCEREFERENCE
|
|
Table.Add(en.Key, value);
|
|
}
|
|
// While technically possible to close the Reader here, don't close it
|
|
// to help with some WinRes lifetime issues.
|
|
}
|
|
|
|
private Object GetObjectInternal(String name)
|
|
{
|
|
if (name == null)
|
|
throw new ArgumentNullException("name");
|
|
Contract.EndContractBlock();
|
|
|
|
Hashtable copyOfTable = Table; // Avoid a race with Dispose
|
|
|
|
if (copyOfTable == null)
|
|
throw new ObjectDisposedException(null, Environment.GetResourceString("ObjectDisposed_ResourceSet"));
|
|
|
|
return copyOfTable[name];
|
|
}
|
|
|
|
private Object GetCaseInsensitiveObjectInternal(String name)
|
|
{
|
|
Hashtable copyOfTable = Table; // Avoid a race with Dispose
|
|
|
|
if (copyOfTable == null)
|
|
throw new ObjectDisposedException(null, Environment.GetResourceString("ObjectDisposed_ResourceSet"));
|
|
|
|
Hashtable caseTable = _caseInsensitiveTable; // Avoid ---- with Close
|
|
if (caseTable == null)
|
|
{
|
|
caseTable = new Hashtable(StringComparer.OrdinalIgnoreCase);
|
|
#if _DEBUG
|
|
//Console.WriteLine("ResourceSet::GetObject loading up case-insensitive data");
|
|
BCLDebug.Perf(false, "Using case-insensitive lookups is bad perf-wise. Consider capitalizing "+name+" correctly in your source");
|
|
#endif
|
|
|
|
IDictionaryEnumerator en = copyOfTable.GetEnumerator();
|
|
while (en.MoveNext())
|
|
{
|
|
caseTable.Add(en.Key, en.Value);
|
|
}
|
|
_caseInsensitiveTable = caseTable;
|
|
}
|
|
|
|
return caseTable[name];
|
|
}
|
|
}
|
|
}
|