Update Reference Sources to .NET Framework 4.6.1
[mono.git] / mcs / class / referencesource / mscorlib / system / resources / resourceset.cs
1 // ==++==
2 // 
3 //   Copyright (c) Microsoft Corporation.  All rights reserved.
4 // 
5 // ==--==
6 /*============================================================
7 **
8 ** Class:  ResourceSet
9 **
10 ** <OWNER>[....]</OWNER>
11 **
12 **
13 ** Purpose: Culture-specific collection of resources.
14 **
15 ** 
16 ===========================================================*/
17 namespace System.Resources {
18     using System;
19     using System.Collections;
20     using System.IO;
21     using System.Globalization;
22     using System.Security.Permissions;
23     using System.Runtime.InteropServices;
24     using System.Reflection;
25     using System.Runtime.Serialization;
26     using System.Runtime.Versioning;
27     using System.Diagnostics.Contracts;
28
29     // A ResourceSet stores all the resources defined in one particular CultureInfo.
30     // 
31     // The method used to load resources is straightforward - this class
32     // enumerates over an IResourceReader, loading every name and value, and 
33     // stores them in a hash table.  Custom IResourceReaders can be used.
34     // 
35     [Serializable]
36 [System.Runtime.InteropServices.ComVisible(true)]
37     public class ResourceSet : IDisposable, IEnumerable
38     {
39         [NonSerialized] protected IResourceReader Reader;
40 #if FEATURE_CORECLR
41         internal Hashtable Table;
42 #else
43         protected Hashtable Table;
44 #endif
45
46         private Hashtable _caseInsensitiveTable;  // For case-insensitive lookups.
47
48 #if LOOSELY_LINKED_RESOURCE_REFERENCE
49         [OptionalField]
50         private Assembly _assembly;  // For LooselyLinkedResourceReferences
51 #endif // LOOSELY_LINKED_RESOURCE_REFERENCE
52
53         protected ResourceSet()
54         {
55             // To not inconvenience people subclassing us, we should allocate a new
56             // hashtable here just so that Table is set to something.
57             CommonInit();
58         }
59
60         // For RuntimeResourceSet, ignore the Table parameter - it's a wasted 
61         // allocation.
62         internal ResourceSet(bool junk)
63         {
64         }
65
66         // Creates a ResourceSet using the system default ResourceReader
67         // implementation.  Use this constructor to open & read from a file 
68         // on disk.
69         // 
70         #if FEATURE_CORECLR
71         [System.Security.SecurityCritical] // auto-generated
72         #endif
73         [ResourceExposure(ResourceScope.Machine)]
74         [ResourceConsumption(ResourceScope.Machine)]
75         public ResourceSet(String fileName)
76         {
77             Reader = new ResourceReader(fileName);
78             CommonInit();
79             ReadResources();
80         }
81
82 #if LOOSELY_LINKED_RESOURCE_REFERENCE
83         public ResourceSet(String fileName, Assembly assembly)
84         {
85             Reader = new ResourceReader(fileName);
86             CommonInit();
87             _assembly = assembly;
88             ReadResources();
89         }
90 #endif // LOOSELY_LINKED_RESOURCE_REFERENCE
91     
92         // Creates a ResourceSet using the system default ResourceReader
93         // implementation.  Use this constructor to read from an open stream 
94         // of data.
95         // 
96         [System.Security.SecurityCritical]  // auto-generated_required
97         public ResourceSet(Stream stream)
98         {
99             Reader = new ResourceReader(stream);
100             CommonInit();
101             ReadResources();
102         }
103
104 #if LOOSELY_LINKED_RESOURCE_REFERENCE
105         [System.Security.SecurityCritical]  // auto_generated_required
106         public ResourceSet(Stream stream, Assembly assembly)
107         {
108             Reader = new ResourceReader(stream);
109             CommonInit();
110             _assembly = assembly;
111             ReadResources();
112         }
113 #endif // LOOSELY_LINKED_RESOURCE_REFERENCE
114
115         public ResourceSet(IResourceReader reader)
116         {
117             if (reader == null)
118                 throw new ArgumentNullException("reader");
119             Contract.EndContractBlock();
120             Reader = reader;
121             CommonInit();
122             ReadResources();
123         }
124
125 #if LOOSELY_LINKED_RESOURCE_REFERENCE
126         public ResourceSet(IResourceReader reader, Assembly assembly)
127         {
128             if (reader == null)
129                 throw new ArgumentNullException("reader");
130             Contract.EndContractBlock();
131             Reader = reader;
132             CommonInit();
133             _assembly = assembly;
134             ReadResources();
135         }
136 #endif // LOOSELY_LINKED_RESOURCE_REFERENCE
137     
138         private void CommonInit()
139         {
140             Table = new Hashtable();
141         }
142
143         // Closes and releases any resources used by this ResourceSet, if any.
144         // All calls to methods on the ResourceSet after a call to close may 
145         // fail.  Close is guaranteed to be safely callable multiple times on a 
146         // particular ResourceSet, and all subclasses must support these semantics.
147         public virtual void Close()
148         {
149             Dispose(true);
150         }
151         
152         protected virtual void Dispose(bool disposing)
153         {
154             if (disposing) {
155                 // Close the Reader in a thread-safe way.
156                 IResourceReader copyOfReader = Reader;
157                 Reader = null;
158                 if (copyOfReader != null)
159                     copyOfReader.Close();
160             }
161             Reader = null;
162             _caseInsensitiveTable = null;
163             Table = null;
164         }
165
166         public void Dispose()
167         {
168             Dispose(true);
169         }
170
171 #if LOOSELY_LINKED_RESOURCE_REFERENCE
172         // Optional - used for resolving assembly manifest resource references.
173         // This can safely be null.
174         [ComVisible(false)]
175         public Assembly Assembly {
176             get { return _assembly; }
177             /*protected*/ set { _assembly = value; }
178         }
179 #endif // LOOSELY_LINKED_RESOURCE_REFERENCE
180
181         // Returns the preferred IResourceReader class for this kind of ResourceSet.
182         // Subclasses of ResourceSet using their own Readers &; should override
183         // GetDefaultReader and GetDefaultWriter.
184         public virtual Type GetDefaultReader()
185         {
186             return typeof(ResourceReader);
187         }
188     
189 #if !FEATURE_CORECLR
190         // Returns the preferred IResourceWriter class for this kind of ResourceSet.
191         // Subclasses of ResourceSet using their own Readers &; should override
192         // GetDefaultReader and GetDefaultWriter.
193         public virtual Type GetDefaultWriter()
194         {
195             return typeof(ResourceWriter);
196         }
197 #endif // !FEATURE_CORECLR
198
199         [ComVisible(false)]
200         public virtual IDictionaryEnumerator GetEnumerator()
201         {
202             return GetEnumeratorHelper();
203         }
204
205         /// <internalonly/>
206         IEnumerator IEnumerable.GetEnumerator()
207         {
208             return GetEnumeratorHelper();
209         }
210
211         private IDictionaryEnumerator GetEnumeratorHelper()
212         {
213             Hashtable copyOfTable = Table;  // Avoid a race with Dispose
214             if (copyOfTable == null)
215                 throw new ObjectDisposedException(null, Environment.GetResourceString("ObjectDisposed_ResourceSet"));
216             return copyOfTable.GetEnumerator();
217         }
218
219         // Look up a string value for a resource given its name.
220         // 
221         public virtual String GetString(String name)
222         {
223             Object obj = GetObjectInternal(name);
224             try {
225                 return (String)obj;
226             }
227             catch (InvalidCastException) {
228                 throw new InvalidOperationException(Environment.GetResourceString("InvalidOperation_ResourceNotString_Name", name));
229             }
230         }
231
232         public virtual String GetString(String name, bool ignoreCase)
233         {
234             Object obj;
235             String s;
236
237             // Case-sensitive lookup
238             obj = GetObjectInternal(name);
239             try {
240                 s = (String)obj;
241             }
242             catch (InvalidCastException) {
243                 throw new InvalidOperationException(Environment.GetResourceString("InvalidOperation_ResourceNotString_Name", name));
244             }
245
246             // case-sensitive lookup succeeded
247             if (s != null || !ignoreCase) {
248                 return s;
249                 }
250
251             // Try doing a case-insensitive lookup
252             obj = GetCaseInsensitiveObjectInternal(name);
253             try {
254                 return (String)obj;
255             }
256             catch (InvalidCastException) {
257                 throw new InvalidOperationException(Environment.GetResourceString("InvalidOperation_ResourceNotString_Name", name));
258             }
259         }
260         
261         // Look up an object value for a resource given its name.
262         // 
263         public virtual Object GetObject(String name)
264         {
265             return GetObjectInternal(name);
266         }
267
268         public virtual Object GetObject(String name, bool ignoreCase)
269         {
270             Object obj = GetObjectInternal(name);
271             
272             if (obj != null || !ignoreCase)
273                 return obj;
274
275             return GetCaseInsensitiveObjectInternal(name);
276         }
277     
278         protected virtual void ReadResources()
279         {
280             IDictionaryEnumerator en = Reader.GetEnumerator();
281             while (en.MoveNext()) {
282                 Object value = en.Value;
283 #if LOOSELY_LINKED_RESOURCE_REFERENCE
284                 if (Assembly != null && value is LooselyLinkedResourceReference) {
285                     LooselyLinkedResourceReference assRef = (LooselyLinkedResourceReference) value;
286                     value = assRef.Resolve(Assembly);
287                 }
288 #endif //LOOSELYLINKEDRESOURCEREFERENCE
289                 Table.Add(en.Key, value);
290             }
291             // While technically possible to close the Reader here, don't close it
292             // to help with some WinRes lifetime issues.
293         }
294
295         private Object GetObjectInternal(String name)
296         {
297             if (name == null)
298                 throw new ArgumentNullException("name");
299             Contract.EndContractBlock();
300
301             Hashtable copyOfTable = Table;  // Avoid a race with Dispose
302
303             if (copyOfTable == null)
304                 throw new ObjectDisposedException(null, Environment.GetResourceString("ObjectDisposed_ResourceSet"));
305
306             return copyOfTable[name];
307         }
308
309         private Object GetCaseInsensitiveObjectInternal(String name)
310         {
311             Hashtable copyOfTable = Table;  // Avoid a race with Dispose
312
313             if (copyOfTable == null)
314                 throw new ObjectDisposedException(null, Environment.GetResourceString("ObjectDisposed_ResourceSet"));
315
316             Hashtable caseTable = _caseInsensitiveTable;  // Avoid ---- with Close
317             if (caseTable == null)
318             {
319                 caseTable = new Hashtable(StringComparer.OrdinalIgnoreCase);
320 #if _DEBUG
321                 //Console.WriteLine("ResourceSet::GetObject loading up case-insensitive data");
322                 BCLDebug.Perf(false, "Using case-insensitive lookups is bad perf-wise.  Consider capitalizing "+name+" correctly in your source");
323 #endif
324
325                 IDictionaryEnumerator en = copyOfTable.GetEnumerator();
326                 while (en.MoveNext())
327                 {
328                     caseTable.Add(en.Key, en.Value);
329                 }
330                 _caseInsensitiveTable = caseTable;
331             }
332
333             return caseTable[name];
334         }
335     }
336 }