ResourceSet.cs 8.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262
  1. // Licensed to the .NET Foundation under one or more agreements.
  2. // The .NET Foundation licenses this file to you under the MIT license.
  3. // See the LICENSE file in the project root for more information.
  4. /*============================================================
  5. **
  6. **
  7. **
  8. **
  9. **
  10. ** Purpose: Culture-specific collection of resources.
  11. **
  12. **
  13. ===========================================================*/
  14. using System.Collections;
  15. using System.IO;
  16. using System.Reflection;
  17. namespace System.Resources
  18. {
  19. // A ResourceSet stores all the resources defined in one particular CultureInfo.
  20. //
  21. // The method used to load resources is straightforward - this class
  22. // enumerates over an IResourceReader, loading every name and value, and
  23. // stores them in a hash table. Custom IResourceReaders can be used.
  24. //
  25. public class ResourceSet : IDisposable, IEnumerable
  26. {
  27. protected IResourceReader Reader;
  28. internal Hashtable Table;
  29. private Hashtable _caseInsensitiveTable; // For case-insensitive lookups.
  30. protected ResourceSet()
  31. {
  32. // To not inconvenience people subclassing us, we should allocate a new
  33. // hashtable here just so that Table is set to something.
  34. CommonInit();
  35. }
  36. // For RuntimeResourceSet, ignore the Table parameter - it's a wasted
  37. // allocation.
  38. internal ResourceSet(bool junk)
  39. {
  40. }
  41. // Creates a ResourceSet using the system default ResourceReader
  42. // implementation. Use this constructor to open & read from a file
  43. // on disk.
  44. //
  45. public ResourceSet(string fileName)
  46. {
  47. Reader = new ResourceReader(fileName);
  48. CommonInit();
  49. ReadResources();
  50. }
  51. // Creates a ResourceSet using the system default ResourceReader
  52. // implementation. Use this constructor to read from an open stream
  53. // of data.
  54. //
  55. public ResourceSet(Stream stream)
  56. {
  57. Reader = new ResourceReader(stream);
  58. CommonInit();
  59. ReadResources();
  60. }
  61. public ResourceSet(IResourceReader reader)
  62. {
  63. if (reader == null)
  64. throw new ArgumentNullException(nameof(reader));
  65. Reader = reader;
  66. CommonInit();
  67. ReadResources();
  68. }
  69. private void CommonInit()
  70. {
  71. Table = new Hashtable();
  72. }
  73. // Closes and releases any resources used by this ResourceSet, if any.
  74. // All calls to methods on the ResourceSet after a call to close may
  75. // fail. Close is guaranteed to be safely callable multiple times on a
  76. // particular ResourceSet, and all subclasses must support these semantics.
  77. public virtual void Close()
  78. {
  79. Dispose(true);
  80. }
  81. protected virtual void Dispose(bool disposing)
  82. {
  83. if (disposing)
  84. {
  85. // Close the Reader in a thread-safe way.
  86. IResourceReader copyOfReader = Reader;
  87. Reader = null;
  88. if (copyOfReader != null)
  89. copyOfReader.Close();
  90. }
  91. Reader = null;
  92. _caseInsensitiveTable = null;
  93. Table = null;
  94. }
  95. public void Dispose()
  96. {
  97. Dispose(true);
  98. }
  99. // Returns the preferred IResourceReader class for this kind of ResourceSet.
  100. // Subclasses of ResourceSet using their own Readers &; should override
  101. // GetDefaultReader and GetDefaultWriter.
  102. public virtual Type GetDefaultReader()
  103. {
  104. return typeof(ResourceReader);
  105. }
  106. // Returns the preferred IResourceWriter class for this kind of ResourceSet.
  107. // Subclasses of ResourceSet using their own Readers &; should override
  108. // GetDefaultReader and GetDefaultWriter.
  109. public virtual Type GetDefaultWriter()
  110. {
  111. Assembly resourceWriterAssembly = Assembly.Load("System.Resources.Writer, Version=4.0.1.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a");
  112. return resourceWriterAssembly.GetType("System.Resources.ResourceWriter", true);
  113. }
  114. public virtual IDictionaryEnumerator GetEnumerator()
  115. {
  116. return GetEnumeratorHelper();
  117. }
  118. IEnumerator IEnumerable.GetEnumerator()
  119. {
  120. return GetEnumeratorHelper();
  121. }
  122. private IDictionaryEnumerator GetEnumeratorHelper()
  123. {
  124. Hashtable copyOfTable = Table; // Avoid a race with Dispose
  125. if (copyOfTable == null)
  126. throw new ObjectDisposedException(null, SR.ObjectDisposed_ResourceSet);
  127. return copyOfTable.GetEnumerator();
  128. }
  129. // Look up a string value for a resource given its name.
  130. //
  131. public virtual string GetString(string name)
  132. {
  133. object obj = GetObjectInternal(name);
  134. try
  135. {
  136. return (string)obj;
  137. }
  138. catch (InvalidCastException)
  139. {
  140. throw new InvalidOperationException(SR.Format(SR.InvalidOperation_ResourceNotString_Name, name));
  141. }
  142. }
  143. public virtual string GetString(string name, bool ignoreCase)
  144. {
  145. object obj;
  146. string s;
  147. // Case-sensitive lookup
  148. obj = GetObjectInternal(name);
  149. try
  150. {
  151. s = (string)obj;
  152. }
  153. catch (InvalidCastException)
  154. {
  155. throw new InvalidOperationException(SR.Format(SR.InvalidOperation_ResourceNotString_Name, name));
  156. }
  157. // case-sensitive lookup succeeded
  158. if (s != null || !ignoreCase)
  159. {
  160. return s;
  161. }
  162. // Try doing a case-insensitive lookup
  163. obj = GetCaseInsensitiveObjectInternal(name);
  164. try
  165. {
  166. return (string)obj;
  167. }
  168. catch (InvalidCastException)
  169. {
  170. throw new InvalidOperationException(SR.Format(SR.InvalidOperation_ResourceNotString_Name, name));
  171. }
  172. }
  173. // Look up an object value for a resource given its name.
  174. //
  175. public virtual object GetObject(string name)
  176. {
  177. return GetObjectInternal(name);
  178. }
  179. public virtual object GetObject(string name, bool ignoreCase)
  180. {
  181. object obj = GetObjectInternal(name);
  182. if (obj != null || !ignoreCase)
  183. return obj;
  184. return GetCaseInsensitiveObjectInternal(name);
  185. }
  186. protected virtual void ReadResources()
  187. {
  188. IDictionaryEnumerator en = Reader.GetEnumerator();
  189. while (en.MoveNext())
  190. {
  191. object value = en.Value;
  192. Table.Add(en.Key, value);
  193. }
  194. // While technically possible to close the Reader here, don't close it
  195. // to help with some WinRes lifetime issues.
  196. }
  197. private object GetObjectInternal(string name)
  198. {
  199. if (name == null)
  200. throw new ArgumentNullException(nameof(name));
  201. Hashtable copyOfTable = Table; // Avoid a race with Dispose
  202. if (copyOfTable == null)
  203. throw new ObjectDisposedException(null, SR.ObjectDisposed_ResourceSet);
  204. return copyOfTable[name];
  205. }
  206. private object GetCaseInsensitiveObjectInternal(string name)
  207. {
  208. Hashtable copyOfTable = Table; // Avoid a race with Dispose
  209. if (copyOfTable == null)
  210. throw new ObjectDisposedException(null, SR.ObjectDisposed_ResourceSet);
  211. Hashtable caseTable = _caseInsensitiveTable; // Avoid a race condition with Close
  212. if (caseTable == null)
  213. {
  214. caseTable = new Hashtable(StringComparer.OrdinalIgnoreCase);
  215. IDictionaryEnumerator en = copyOfTable.GetEnumerator();
  216. while (en.MoveNext())
  217. {
  218. caseTable.Add(en.Key, en.Value);
  219. }
  220. _caseInsensitiveTable = caseTable;
  221. }
  222. return caseTable[name];
  223. }
  224. }
  225. }