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