merge -r 60439:60440
[mono.git] / mcs / class / corlib / System.Resources / ResourceManager.cs
1 //      
2 // System.Resources.ResourceManager.cs
3 //
4 // Authors:
5 //      Duncan Mak (duncan@ximian.com)
6 //      Dick Porter (dick@ximian.com)
7 //
8 // (C) 2001, 2002 Ximian, Inc. http://www.ximian.com
9 //
10
11 //
12 // Copyright (C) 2004 Novell, Inc (http://www.novell.com)
13 //
14 // Permission is hereby granted, free of charge, to any person obtaining
15 // a copy of this software and associated documentation files (the
16 // "Software"), to deal in the Software without restriction, including
17 // without limitation the rights to use, copy, modify, merge, publish,
18 // distribute, sublicense, and/or sell copies of the Software, and to
19 // permit persons to whom the Software is furnished to do so, subject to
20 // the following conditions:
21 // 
22 // The above copyright notice and this permission notice shall be
23 // included in all copies or substantial portions of the Software.
24 // 
25 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
26 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
27 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
28 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
29 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
30 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
31 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
32 //
33
34 using System.Collections;
35 using System.Reflection;
36 using System.Globalization;
37 using System.IO;
38
39 namespace System.Resources
40 {
41         [Serializable]
42         public class ResourceManager
43         {
44                 public static readonly int HeaderVersionNumber = 1;
45                 public static readonly int MagicNumber = unchecked((int)0xBEEFCACE);
46
47                 protected string BaseNameField;
48                 protected Assembly MainAssembly;
49                 // Maps cultures to ResourceSet objects
50                 protected Hashtable ResourceSets;
51                 
52                 private bool ignoreCase;
53                 private Type resourceSetType;
54                 private String resourceDir;
55                 
56                 /* Recursing through culture parents stops here */
57                 private CultureInfo neutral_culture;
58                 
59                 // constructors
60                 protected ResourceManager () {
61                         ResourceSets=new Hashtable();
62                         ignoreCase=false;
63                         resourceSetType=typeof(ResourceSet);
64                         resourceDir=null;
65                         neutral_culture=null;
66                 }
67                 
68                 public ResourceManager (Type resourceSource) : this()
69                 {
70                         if (resourceSource == null)
71                                 throw new ArgumentNullException ("resourceSource is null.");
72                         
73                         BaseNameField = resourceSource.FullName;
74                         MainAssembly = resourceSource.Assembly;
75
76                         /* Temporary workaround for bug 43567 */
77                         resourceSetType = typeof(ResourceSet);
78                         neutral_culture = GetNeutralResourcesLanguage(MainAssembly);
79                 }
80                 
81                 public ResourceManager (string baseName, Assembly assembly) : this()
82                 {
83                         if (baseName == null)
84                                 throw new ArgumentNullException ("baseName is null.");
85                         if(assembly == null)
86                                 throw new ArgumentNullException ("assembly is null.");
87                         
88                         BaseNameField = baseName;
89                         MainAssembly = assembly;
90                         neutral_culture = GetNeutralResourcesLanguage(MainAssembly);
91                 }
92                          
93                 private Type CheckResourceSetType(Type usingResourceSet)
94                 {
95                         if(usingResourceSet==null) {
96                                 return(typeof(ResourceSet));
97                         } else {
98                                 if (!usingResourceSet.IsSubclassOf (typeof (ResourceSet)))
99                                         throw new ArgumentException ("Type must be from ResourceSet.");
100                                 
101                                 return(usingResourceSet);
102                         }
103                 }
104                 
105                 public ResourceManager (string baseName, Assembly assembly, Type usingResourceSet) : this()
106                 {
107                         if (baseName == null)
108                                 throw new ArgumentNullException ("baseName is null.");
109                         if(assembly == null)
110                                 throw new ArgumentNullException ("assembly is null.");
111                         
112                         BaseNameField = baseName;
113                         MainAssembly = assembly;
114                         resourceSetType = CheckResourceSetType(usingResourceSet);
115                         neutral_culture = GetNeutralResourcesLanguage(MainAssembly);
116                 }
117                 
118                 /* Private constructor for CreateFileBasedResourceManager */
119                 private ResourceManager(String baseName, String resourceDir, Type usingResourceSet) : this()
120                 {
121                         if(baseName==null) {
122                                 throw new ArgumentNullException("The base name is null");
123                         }
124                         if(baseName.EndsWith(".resources")) {
125                                 throw new ArgumentException("The base name ends in '.resources'");
126                         }
127                         if(resourceDir==null) {
128                                 throw new ArgumentNullException("The resourceDir is null");
129                         }
130
131                         BaseNameField = baseName;
132                         MainAssembly = null;
133                         resourceSetType = CheckResourceSetType(usingResourceSet);
134                         this.resourceDir = resourceDir;
135                 }
136                 
137                 public static ResourceManager CreateFileBasedResourceManager (string baseName,
138                                                       string resourceDir, Type usingResourceSet)
139                 {
140                         return new ResourceManager(baseName, resourceDir, usingResourceSet);
141                 }
142
143                 public virtual string BaseName
144                 {
145                         get { return BaseNameField; }
146                 }
147
148                 public virtual bool IgnoreCase
149                 {
150                         get { return ignoreCase; }
151                         set { ignoreCase = value; }
152                 }
153
154                 public virtual Type ResourceSetType
155                 {
156                         get { return resourceSetType; }
157                 }
158
159                 public virtual object GetObject(string name)
160                 {
161                         return(GetObject(name, null));
162                 }
163
164                 public virtual object GetObject(string name, CultureInfo culture)
165                 {
166                         if(name==null) {
167                                 throw new ArgumentNullException("name is null");
168                         }
169
170                         if(culture==null) {
171                                 culture=CultureInfo.CurrentUICulture;
172                         }
173
174                         lock(this) {
175                                 ResourceSet set=InternalGetResourceSet(culture, true, true);
176                                 object obj=null;
177                                 
178                                 if(set != null) {
179                                         obj=set.GetObject(name, ignoreCase);
180                                         if(obj != null) {
181                                                 return(obj);
182                                         }
183                                 }
184                                 
185                                 /* Try parent cultures */
186
187                                 do {
188                                         culture=culture.Parent;
189
190                                         set=InternalGetResourceSet(culture, true, true);
191                                         if(set!=null) {
192                                                 obj=set.GetObject(name, ignoreCase);
193                                                 if(obj != null) {
194                                                         return(obj);
195                                                 }
196                                         }
197                                 } while(!culture.Equals(neutral_culture) &&
198                                         !culture.Equals(CultureInfo.InvariantCulture));
199                         }
200                         
201                         return(null);
202                 }
203                 
204                 
205                 public virtual ResourceSet GetResourceSet (CultureInfo culture,
206                                            bool createIfNotExists, bool tryParents)
207                         
208                 {
209                         if (culture == null) {
210                                 throw new ArgumentNullException ("CultureInfo is a null reference.");
211                         }
212
213                         lock(this) {
214                                 return(InternalGetResourceSet(culture, createIfNotExists, tryParents));
215                         }
216                 }
217                 
218                 public virtual string GetString (string name)
219                 {
220                         return(GetString(name, null));
221                 }
222
223                 public virtual string GetString (string name, CultureInfo culture)
224                 {
225                         if (name == null) {
226                                 throw new ArgumentNullException ("Name is null.");
227                         }
228
229                         if(culture==null) {
230                                 culture=CultureInfo.CurrentUICulture;
231                         }
232
233                         lock(this) {
234                                 ResourceSet set=InternalGetResourceSet(culture, true, true);
235                                 string str=null;
236
237                                 if(set!=null) {
238                                         str=set.GetString(name, ignoreCase);
239                                         if(str!=null) {
240                                                 return(str);
241                                         }
242                                 }
243
244                                 /* Try parent cultures */
245
246                                 do {
247                                         culture=culture.Parent;
248
249                                         set=InternalGetResourceSet(culture, true, true);
250                                         if(set!=null) {
251                                                 str=set.GetString(name, ignoreCase);
252                                                 if(str!=null) {
253                                                         return(str);
254                                                 }
255                                         }
256                                 } while(!culture.Equals(neutral_culture) &&
257                                         !culture.Equals(CultureInfo.InvariantCulture));
258                         }
259                         
260                         return(null);
261                 }
262
263                 protected virtual string GetResourceFileName (CultureInfo culture)
264                 {
265                         if(culture.Equals(CultureInfo.InvariantCulture)) {
266                                 return(BaseNameField + ".resources");
267                         } else {
268                                 return(BaseNameField + "." +  culture.Name + ".resources");
269                         }
270                 }
271                 
272                 static Stream GetManifestResourceStreamNoCase (Assembly ass, string fn)
273                 {
274                         foreach (string s in ass.GetManifestResourceNames ())
275                                 if (String.Compare (fn, s, true, CultureInfo.InvariantCulture) == 0)
276                                         return ass.GetManifestResourceStream (s);
277                         return null;
278                 }
279                 
280                 protected virtual ResourceSet InternalGetResourceSet (CultureInfo culture, bool Createifnotexists, bool tryParents)
281                 {
282                         ResourceSet set;
283                         
284                         if (culture == null) {
285                                 string msg = String.Format ("Could not find any resource appropiate for the " +
286                                                             "specified culture or its parents (assembly:{0})",
287                                                             MainAssembly != null ? MainAssembly.GetName ().Name : "");
288                                                             
289                                 throw new MissingManifestResourceException (msg);
290                         }
291                         /* if we already have this resource set, return it */
292                         set=(ResourceSet)ResourceSets[culture];
293                         if(set!=null) {
294                                 return(set);
295                         }
296
297                         if(MainAssembly != null) {
298                                 /* Assembly resources */
299                                 Stream stream;
300                                 string filename=GetResourceFileName(culture);
301                                 
302                                 stream=MainAssembly.GetManifestResourceStream(filename);
303                                 if (stream == null)
304                                         stream = GetManifestResourceStreamNoCase (MainAssembly, filename);
305                                 
306                                 if(stream==null) {
307                                         /* Try a satellite assembly */
308                                         Version sat_version=GetSatelliteContractVersion(MainAssembly);
309                                         Assembly a = null;
310                                         try {
311                                                 a = MainAssembly.GetSatelliteAssembly (culture, sat_version);
312                                                 stream=a.GetManifestResourceStream(filename);
313                                                 if (stream == null)
314                                                         stream = GetManifestResourceStreamNoCase (a, filename);
315                                         
316                                         } catch (Exception) {} // Ignored
317                                 }
318
319                                 if(stream!=null && Createifnotexists==true) {
320                                         object[] args=new Object[1];
321
322                                         args[0]=stream;
323                                         
324                                         /* should we catch
325                                          * MissingMethodException, or
326                                          * just let someone else deal
327                                          * with it?
328                                          */
329                                         set=(ResourceSet)Activator.CreateInstance(resourceSetType, args);
330                                 } else if (culture.Equals (CultureInfo.InvariantCulture)) {
331                                         string msg = "Could not find any resource appropiate for the " +
332                                                      "specified culture or its parents. " +
333                                                      "Make sure \"{1}\" was correctly embedded or " +
334                                                      "linked into assembly \"{0}\".";
335                                                      
336                                         msg = String.Format (msg, MainAssembly != null ? MainAssembly.GetName ().Name : "", filename);
337                                                             
338                                         throw new MissingManifestResourceException (msg);
339                                 }
340                         } else if(resourceDir != null) {
341                                 /* File resources */
342                                 string filename=Path.Combine(resourceDir, this.GetResourceFileName(culture));
343                                 if(File.Exists(filename) &&
344                                    Createifnotexists==true) {
345                                         object[] args=new Object[1];
346
347                                         args[0]=filename;
348                                         
349                                         /* should we catch
350                                          * MissingMethodException, or
351                                          * just let someone else deal
352                                          * with it?
353                                          */
354                                         set=(ResourceSet)Activator.CreateInstance(resourceSetType, args);
355                                 }
356                         }
357
358                         if(set==null && tryParents==true) {
359                                 // avoid endless recursion
360                                 if (!culture.Equals(CultureInfo.InvariantCulture))
361                                         set = InternalGetResourceSet (culture.Parent, Createifnotexists, tryParents);
362                         }
363
364                         if(set!=null) {
365                                 ResourceSets.Add(culture, set);
366                         }
367                         
368                         return(set);
369                 }
370                    
371                 public virtual void ReleaseAllResources ()
372                 {
373                         lock(this) 
374                         {
375                                 foreach (ResourceSet r in ResourceSets)
376                                         r.Close();
377                                 ResourceSets.Clear();
378                         }
379                 }
380
381                 protected static CultureInfo GetNeutralResourcesLanguage (Assembly a)
382                 {
383                         object[] attrs;
384
385                         attrs=a.GetCustomAttributes(typeof(NeutralResourcesLanguageAttribute), false);
386
387                         if(attrs.Length==0) {
388                                 return(CultureInfo.InvariantCulture);
389                         } else {
390                                 NeutralResourcesLanguageAttribute res_attr=(NeutralResourcesLanguageAttribute)attrs[0];
391                                 
392                                 return(new CultureInfo(res_attr.CultureName));
393                         }
394                 }
395
396                 protected static Version GetSatelliteContractVersion (Assembly a)
397                 {
398                         object[] attrs;
399                         
400                         attrs=a.GetCustomAttributes(typeof(SatelliteContractVersionAttribute), false);
401
402                         if(attrs.Length==0) {
403                                 return(null);
404                         } else {
405                                 SatelliteContractVersionAttribute sat_attr=(SatelliteContractVersionAttribute)attrs[0];
406
407                                 /* Version(string) can throw
408                                  * ArgumentException if the version is
409                                  * invalid, but the spec for
410                                  * GetSatelliteContractVersion says we
411                                  * can throw the same exception for
412                                  * the same reason, so dont bother to
413                                  * catch it.
414                                  */
415                                 return(new Version(sat_attr.Version));
416                         }
417                 }
418         }
419 }