**** Merged from MCS ****
[mono-project.git] / mcs / class / corlib / System.Resources / ResourceManager.cs
bloba8367b7c184f7f5bfff832bc4f06fa020e58338f
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 //
12 // Copyright (C) 2004 Novell, Inc (http://www.novell.com)
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.
34 using System.Collections;
35 using System.Reflection;
36 using System.Globalization;
37 using System.IO;
39 namespace System.Resources
41 [Serializable]
42 public class ResourceManager
44 public static readonly int HeaderVersionNumber = 1;
45 public static readonly int MagicNumber = unchecked((int)0xBEEFCACE);
47 protected string BaseNameField;
48 protected Assembly MainAssembly;
49 // Maps cultures to ResourceSet objects
50 protected Hashtable ResourceSets;
52 private bool ignoreCase;
53 private Type resourceSetType;
54 private String resourceDir;
56 /* Recursing through culture parents stops here */
57 private CultureInfo neutral_culture;
59 // constructors
60 protected ResourceManager () {
61 ResourceSets=new Hashtable();
62 ignoreCase=false;
63 resourceSetType=typeof(ResourceSet);
64 resourceDir=null;
65 neutral_culture=null;
68 public ResourceManager (Type resourceSource) : this()
70 if (resourceSource == null)
71 throw new ArgumentNullException ("resourceSource is null.");
73 BaseNameField = resourceSource.FullName;
74 MainAssembly = resourceSource.Assembly;
76 /* Temporary workaround for bug 43567 */
77 resourceSetType = typeof(ResourceSet);
78 neutral_culture = GetNeutralResourcesLanguage(MainAssembly);
81 public ResourceManager (string baseName, Assembly assembly) : this()
83 if (baseName == null)
84 throw new ArgumentNullException ("baseName is null.");
85 if(assembly == null)
86 throw new ArgumentNullException ("assembly is null.");
88 BaseNameField = baseName;
89 MainAssembly = assembly;
90 neutral_culture = GetNeutralResourcesLanguage(MainAssembly);
93 private Type CheckResourceSetType(Type usingResourceSet)
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.");
101 return(usingResourceSet);
105 public ResourceManager (string baseName, Assembly assembly, Type usingResourceSet) : this()
107 if (baseName == null)
108 throw new ArgumentNullException ("baseName is null.");
109 if(assembly == null)
110 throw new ArgumentNullException ("assembly is null.");
112 BaseNameField = baseName;
113 MainAssembly = assembly;
114 resourceSetType = CheckResourceSetType(usingResourceSet);
115 neutral_culture = GetNeutralResourcesLanguage(MainAssembly);
118 /* Private constructor for CreateFileBasedResourceManager */
119 private ResourceManager(String baseName, String resourceDir, Type usingResourceSet) : this()
121 if(baseName==null) {
122 throw new ArgumentNullException("The base name is null");
124 if(baseName.EndsWith(".resources")) {
125 throw new ArgumentException("The base name ends in '.resources'");
127 if(resourceDir==null) {
128 throw new ArgumentNullException("The resourceDir is null");
131 BaseNameField = baseName;
132 MainAssembly = null;
133 resourceSetType = CheckResourceSetType(usingResourceSet);
134 this.resourceDir = resourceDir;
137 public static ResourceManager CreateFileBasedResourceManager (string baseName,
138 string resourceDir, Type usingResourceSet)
140 return new ResourceManager(baseName, resourceDir, usingResourceSet);
143 public virtual string BaseName
145 get { return BaseNameField; }
148 public virtual bool IgnoreCase
150 get { return ignoreCase; }
151 set { ignoreCase = value; }
154 public virtual Type ResourceSetType
156 get { return resourceSetType; }
159 public virtual object GetObject(string name)
161 return(GetObject(name, null));
164 public virtual object GetObject(string name, CultureInfo culture)
166 if(name==null) {
167 throw new ArgumentNullException("name is null");
170 if(culture==null) {
171 culture=CultureInfo.CurrentUICulture;
174 lock(this) {
175 ResourceSet set=InternalGetResourceSet(culture, true, true);
176 object obj=null;
178 if(set != null) {
179 obj=set.GetObject(name, ignoreCase);
180 if(obj != null) {
181 return(obj);
185 /* Try parent cultures */
187 do {
188 culture=culture.Parent;
190 set=InternalGetResourceSet(culture, true, true);
191 if(set!=null) {
192 obj=set.GetObject(name, ignoreCase);
193 if(obj != null) {
194 return(obj);
197 } while(!culture.Equals(neutral_culture) &&
198 !culture.Equals(CultureInfo.InvariantCulture));
201 return(null);
205 public virtual ResourceSet GetResourceSet (CultureInfo culture,
206 bool createIfNotExists, bool tryParents)
209 if (culture == null) {
210 throw new ArgumentNullException ("CultureInfo is a null reference.");
213 lock(this) {
214 return(InternalGetResourceSet(culture, createIfNotExists, tryParents));
218 public virtual string GetString (string name)
220 return(GetString(name, null));
223 public virtual string GetString (string name, CultureInfo culture)
225 if (name == null) {
226 throw new ArgumentNullException ("Name is null.");
229 if(culture==null) {
230 culture=CultureInfo.CurrentUICulture;
233 lock(this) {
234 ResourceSet set=InternalGetResourceSet(culture, true, true);
235 string str=null;
237 if(set!=null) {
238 str=set.GetString(name, ignoreCase);
239 if(str!=null) {
240 return(str);
244 /* Try parent cultures */
246 do {
247 culture=culture.Parent;
249 set=InternalGetResourceSet(culture, true, true);
250 if(set!=null) {
251 str=set.GetString(name, ignoreCase);
252 if(str!=null) {
253 return(str);
256 } while(!culture.Equals(neutral_culture) &&
257 !culture.Equals(CultureInfo.InvariantCulture));
260 return(null);
263 protected virtual string GetResourceFileName (CultureInfo culture)
265 if(culture.Equals(CultureInfo.InvariantCulture)) {
266 return(BaseNameField + ".resources");
267 } else {
268 return(BaseNameField + "." + culture.Name + ".resources");
272 protected virtual ResourceSet InternalGetResourceSet (CultureInfo culture, bool Createifnotexists, bool tryParents)
274 ResourceSet set;
276 if (culture == null) {
277 string msg = String.Format ("Could not find any resource appropiate for the " +
278 "specified culture or its parents (assembly:{0})",
279 MainAssembly != null ? MainAssembly.GetName ().Name : "");
281 throw new MissingManifestResourceException (msg);
283 /* if we already have this resource set, return it */
284 set=(ResourceSet)ResourceSets[culture];
285 if(set!=null) {
286 return(set);
289 if(MainAssembly != null) {
290 /* Assembly resources */
291 Stream stream;
292 string filename=GetResourceFileName(culture);
294 stream=MainAssembly.GetManifestResourceStream(filename);
295 if(stream==null) {
296 /* Try a satellite assembly */
297 Version sat_version=GetSatelliteContractVersion(MainAssembly);
298 Assembly a = null;
299 try {
300 a = MainAssembly.GetSatelliteAssembly (culture, sat_version);
301 stream=a.GetManifestResourceStream(filename);
302 } catch (Exception) {} // Ignored
305 if(stream!=null && Createifnotexists==true) {
306 object[] args=new Object[1];
308 args[0]=stream;
310 /* should we catch
311 * MissingMethodException, or
312 * just let someone else deal
313 * with it?
315 set=(ResourceSet)Activator.CreateInstance(resourceSetType, args);
316 } else if (culture == CultureInfo.InvariantCulture) {
317 string msg = "Could not find any resource appropiate for the " +
318 "specified culture or its parents (assembly:{0})";
320 msg = String.Format (msg, MainAssembly != null ? MainAssembly.GetName ().Name : "");
322 throw new MissingManifestResourceException (msg);
324 } else if(resourceDir != null) {
325 /* File resources */
326 string filename=Path.Combine(resourceDir, this.GetResourceFileName(culture));
327 if(File.Exists(filename) &&
328 Createifnotexists==true) {
329 object[] args=new Object[1];
331 args[0]=filename;
333 /* should we catch
334 * MissingMethodException, or
335 * just let someone else deal
336 * with it?
338 set=(ResourceSet)Activator.CreateInstance(resourceSetType, args);
342 if(set==null && tryParents==true) {
343 // avoid endless recursion
344 if (!culture.Equals (neutral_culture) && !culture.Equals(CultureInfo.InvariantCulture))
345 set = InternalGetResourceSet (culture.Parent, Createifnotexists, tryParents);
348 if(set!=null) {
349 ResourceSets.Add(culture, set);
352 return(set);
355 public virtual void ReleaseAllResources ()
357 lock(this)
359 foreach (ResourceSet r in ResourceSets)
360 r.Close();
361 ResourceSets.Clear();
365 protected static CultureInfo GetNeutralResourcesLanguage (Assembly a)
367 object[] attrs;
369 attrs=a.GetCustomAttributes(typeof(NeutralResourcesLanguageAttribute), false);
371 if(attrs.Length==0) {
372 return(CultureInfo.InvariantCulture);
373 } else {
374 NeutralResourcesLanguageAttribute res_attr=(NeutralResourcesLanguageAttribute)attrs[0];
376 return(new CultureInfo(res_attr.CultureName));
380 protected static Version GetSatelliteContractVersion (Assembly a)
382 object[] attrs;
384 attrs=a.GetCustomAttributes(typeof(SatelliteContractVersionAttribute), false);
386 if(attrs.Length==0) {
387 return(null);
388 } else {
389 SatelliteContractVersionAttribute sat_attr=(SatelliteContractVersionAttribute)attrs[0];
391 /* Version(string) can throw
392 * ArgumentException if the version is
393 * invalid, but the spec for
394 * GetSatelliteContractVersion says we
395 * can throw the same exception for
396 * the same reason, so dont bother to
397 * catch it.
399 return(new Version(sat_attr.Version));