2003-09-07 Dick Porter <dick@ximian.com>
[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 using System.Collections;
12 using System.Reflection;
13 using System.Globalization;
14 using System.IO;
15
16 namespace System.Resources
17 {
18         [Serializable]
19         public class ResourceManager
20         {
21                 public static readonly int HeaderVersionNumber = 1;
22                 public static readonly int MagicNumber = unchecked((int)0xBEEFCACE);
23
24                 protected string BaseNameField;
25                 protected Assembly MainAssembly;
26                 // Maps cultures to ResourceSet objects
27                 protected Hashtable ResourceSets;
28                 
29                 private bool ignoreCase;
30                 private Type resourceSetType;
31                 private String resourceDir;
32                 
33                 /* Recursing through culture parents stops here */
34                 private CultureInfo neutral_culture;
35                 
36                 // constructors
37                 protected ResourceManager () {
38                         ResourceSets=new Hashtable();
39                         ignoreCase=false;
40                         resourceSetType=typeof(ResourceSet);
41                         resourceDir=null;
42                         neutral_culture=null;
43                 }
44                 
45                 public ResourceManager (Type resourceSource) : this()
46                 {
47                         if (resourceSource == null)
48                                 throw new ArgumentNullException ("resourceSource is null.");
49                         
50                         BaseNameField = resourceSource.FullName;
51                         MainAssembly = resourceSource.Assembly;
52
53                         /* Temporary workaround for bug 43567 */
54                         resourceSetType = typeof(ResourceSet);
55                         neutral_culture = GetNeutralResourcesLanguage(MainAssembly);
56                 }
57                 
58                 public ResourceManager (string baseName, Assembly assembly) : this()
59                 {
60                         if (baseName == null)
61                                 throw new ArgumentNullException ("baseName is null.");
62                         if(assembly == null)
63                                 throw new ArgumentNullException ("assembly is null.");
64                         
65                         BaseNameField = baseName;
66                         MainAssembly = assembly;
67                         neutral_culture = GetNeutralResourcesLanguage(MainAssembly);
68                 }
69                          
70                 private Type CheckResourceSetType(Type usingResourceSet)
71                 {
72                         if(usingResourceSet==null) {
73                                 return(typeof(ResourceSet));
74                         } else {
75                                 if (!usingResourceSet.IsSubclassOf (typeof (ResourceSet)))
76                                         throw new ArgumentException ("Type must be from ResourceSet.");
77                                 
78                                 return(usingResourceSet);
79                         }
80                 }
81                 
82                 public ResourceManager (string baseName, Assembly assembly, Type usingResourceSet) : this()
83                 {
84                         if (baseName == null)
85                                 throw new ArgumentNullException ("baseName is null.");
86                         if(assembly == null)
87                                 throw new ArgumentNullException ("assembly is null.");
88                         
89                         BaseNameField = baseName;
90                         MainAssembly = assembly;
91                         resourceSetType = CheckResourceSetType(usingResourceSet);
92                         neutral_culture = GetNeutralResourcesLanguage(MainAssembly);
93                 }
94                 
95                 /* Private constructor for CreateFileBasedResourceManager */
96                 private ResourceManager(String baseName, String resourceDir, Type usingResourceSet) : this()
97                 {
98                         if(baseName==null) {
99                                 throw new ArgumentNullException("The base name is null");
100                         }
101                         if(baseName.EndsWith(".resources")) {
102                                 throw new ArgumentException("The base name ends in '.resources'");
103                         }
104                         if(resourceDir==null) {
105                                 throw new ArgumentNullException("The resourceDir is null");
106                         }
107
108                         BaseNameField = baseName;
109                         MainAssembly = null;
110                         resourceSetType = CheckResourceSetType(usingResourceSet);
111                         this.resourceDir = resourceDir;
112                 }
113                 
114                 public static ResourceManager CreateFileBasedResourceManager (string baseName,
115                                                       string resourceDir, Type usingResourceSet)
116                 {
117                         return new ResourceManager(baseName, resourceDir, usingResourceSet);
118                 }
119
120                 public virtual string BaseName
121                 {
122                         get { return BaseNameField; }
123                 }
124
125                 public virtual bool IgnoreCase
126                 {
127                         get { return ignoreCase; }
128                         set { ignoreCase = value; }
129                 }
130
131                 public virtual Type ResourceSetType
132                 {
133                         get { return resourceSetType; }
134                 }
135
136                 public virtual object GetObject(string name)
137                 {
138                         return(GetObject(name, null));
139                 }
140
141                 public virtual object GetObject(string name, CultureInfo culture)
142                 {
143                         if(name==null) {
144                                 throw new ArgumentNullException("name is null");
145                         }
146
147                         if(culture==null) {
148                                 culture=CultureInfo.CurrentUICulture;
149                         }
150
151                         lock(this) {
152                                 ResourceSet set=InternalGetResourceSet(culture, true, true);
153                                 object obj=null;
154                                 
155                                 if(set != null) {
156                                         obj=set.GetObject(name, ignoreCase);
157                                         if(obj != null) {
158                                                 return(obj);
159                                         }
160                                 }
161                                 
162                                 /* Try parent cultures */
163
164                                 do {
165                                         culture=culture.Parent;
166
167                                         set=InternalGetResourceSet(culture, true, true);
168                                         if(set!=null) {
169                                                 obj=set.GetObject(name, ignoreCase);
170                                                 if(obj != null) {
171                                                         return(obj);
172                                                 }
173                                         }
174                                 } while(!culture.Equals(neutral_culture) &&
175                                         !culture.Equals(CultureInfo.InvariantCulture));
176                         }
177                         
178                         return(null);
179                 }
180                 
181                 
182                 public virtual ResourceSet GetResourceSet (CultureInfo culture,
183                                            bool createIfNotExists, bool tryParents)
184                         
185                 {
186                         if (culture == null) {
187                                 throw new ArgumentNullException ("CultureInfo is a null reference.");
188                         }
189
190                         lock(this) {
191                                 return(InternalGetResourceSet(culture, createIfNotExists, tryParents));
192                         }
193                 }
194                 
195                 public virtual string GetString (string name)
196                 {
197                         return(GetString(name, null));
198                 }
199
200                 public virtual string GetString (string name, CultureInfo culture)
201                 {
202                         if (name == null) {
203                                 throw new ArgumentNullException ("Name is null.");
204                         }
205
206                         if(culture==null) {
207                                 culture=CultureInfo.CurrentUICulture;
208                         }
209
210                         lock(this) {
211                                 ResourceSet set=InternalGetResourceSet(culture, true, true);
212                                 string str=null;
213
214                                 if(set!=null) {
215                                         str=set.GetString(name, ignoreCase);
216                                         if(str!=null) {
217                                                 return(str);
218                                         }
219                                 }
220
221                                 /* Try parent cultures */
222
223                                 do {
224                                         culture=culture.Parent;
225
226                                         set=InternalGetResourceSet(culture, true, true);
227                                         if(set!=null) {
228                                                 str=set.GetString(name, ignoreCase);
229                                                 if(str!=null) {
230                                                         return(str);
231                                                 }
232                                         }
233                                 } while(!culture.Equals(neutral_culture) &&
234                                         !culture.Equals(CultureInfo.InvariantCulture));
235                         }
236                         
237                         return(null);
238                 }
239
240                 protected virtual string GetResourceFileName (CultureInfo culture)
241                 {
242                         if(culture.Equals(CultureInfo.InvariantCulture)) {
243                                 return(BaseNameField + ".resources");
244                         } else {
245                                 return(BaseNameField + "." +  culture.Name + ".resources");
246                         }
247                 }
248                 
249                 protected virtual ResourceSet InternalGetResourceSet (CultureInfo culture, bool Createifnotexists, bool tryParents)
250                 {
251                         ResourceSet set;
252                         
253                         if (culture == null) {
254                                 string msg = String.Format ("Could not find any resource appropiate for the " +
255                                                             "specified culture or its parents (assembly:{0})",
256                                                             MainAssembly != null ? MainAssembly.GetName ().Name : "");
257                                                             
258                                 throw new MissingManifestResourceException (msg);
259                         }
260                         /* if we already have this resource set, return it */
261                         set=(ResourceSet)ResourceSets[culture];
262                         if(set!=null) {
263                                 return(set);
264                         }
265
266                         if(MainAssembly != null) {
267                                 /* Assembly resources */
268                                 Stream stream;
269                                 string filename=GetResourceFileName(culture);
270                                 
271                                 stream=MainAssembly.GetManifestResourceStream(filename);
272                                 if(stream==null) {
273                                         /* Try a satellite assembly */
274                                         Version sat_version=GetSatelliteContractVersion(MainAssembly);
275                                         Assembly a = null;
276                                         try {
277                                                 a = MainAssembly.GetSatelliteAssembly (culture, sat_version);
278                                                 stream=a.GetManifestResourceStream(filename);
279                                         } catch (Exception) {} // Ignored
280                                 }
281
282                                 if(stream!=null && Createifnotexists==true) {
283                                         object[] args=new Object[1];
284
285                                         args[0]=stream;
286                                         
287                                         /* should we catch
288                                          * MissingMethodException, or
289                                          * just let someone else deal
290                                          * with it?
291                                          */
292                                         set=(ResourceSet)Activator.CreateInstance(resourceSetType, args);
293                                 } else if (culture == CultureInfo.InvariantCulture) {
294                                         string msg = "Could not find any resource appropiate for the " +
295                                                      "specified culture or its parents (assembly:{0})";
296                                                      
297                                         msg = String.Format (msg, MainAssembly != null ? MainAssembly.GetName ().Name : "");
298                                                             
299                                         throw new MissingManifestResourceException (msg);
300                                 }
301                         } else if(resourceDir != null) {
302                                 /* File resources */
303                                 string filename=Path.Combine(resourceDir, this.GetResourceFileName(culture));
304                                 if(File.Exists(filename) &&
305                                    Createifnotexists==true) {
306                                         object[] args=new Object[1];
307
308                                         args[0]=filename;
309                                         
310                                         /* should we catch
311                                          * MissingMethodException, or
312                                          * just let someone else deal
313                                          * with it?
314                                          */
315                                         set=(ResourceSet)Activator.CreateInstance(resourceSetType, args);
316                                 }
317                         }
318
319                         if(set==null && tryParents==true) {
320                                 set=this.InternalGetResourceSet(culture.Parent, Createifnotexists, tryParents);
321                         }
322
323                         if(set!=null) {
324                                 ResourceSets.Add(culture, set);
325                         }
326                         
327                         return(set);
328                 }
329                    
330                 public virtual void ReleaseAllResources ()
331                 {
332                         lock(this) 
333                         {
334                                 foreach (ResourceSet r in ResourceSets)
335                                         r.Close();
336                                 ResourceSets.Clear();
337                         }
338                 }
339
340                 protected static CultureInfo GetNeutralResourcesLanguage (Assembly a)
341                 {
342                         object[] attrs;
343
344                         attrs=a.GetCustomAttributes(typeof(NeutralResourcesLanguageAttribute), false);
345
346                         if(attrs.Length==0) {
347                                 return(CultureInfo.InvariantCulture);
348                         } else {
349                                 NeutralResourcesLanguageAttribute res_attr=(NeutralResourcesLanguageAttribute)attrs[0];
350                                 
351                                 return(new CultureInfo(res_attr.CultureName));
352                         }
353                 }
354
355                 protected static Version GetSatelliteContractVersion (Assembly a)
356                 {
357                         object[] attrs;
358                         
359                         attrs=a.GetCustomAttributes(typeof(SatelliteContractVersionAttribute), false);
360
361                         if(attrs.Length==0) {
362                                 return(null);
363                         } else {
364                                 SatelliteContractVersionAttribute sat_attr=(SatelliteContractVersionAttribute)attrs[0];
365
366                                 /* Version(string) can throw
367                                  * ArgumentException if the version is
368                                  * invalid, but the spec for
369                                  * GetSatelliteContractVersion says we
370                                  * can throw the same exception for
371                                  * the same reason, so dont bother to
372                                  * catch it.
373                                  */
374                                 return(new Version(sat_attr.Version));
375                         }
376                 }
377         }
378 }