2 // System.Resources.ResourceManager.cs
5 // Duncan Mak (duncan@ximian.com)
6 // Dick Porter (dick@ximian.com)
8 // (C) 2001, 2002 Ximian, Inc. http://www.ximian.com
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:
22 // The above copyright notice and this permission notice shall be
23 // included in all copies or substantial portions of the Software.
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;
39 namespace System.Resources
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;
60 protected ResourceManager () {
61 ResourceSets=new Hashtable();
63 resourceSetType=typeof(ResourceSet);
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()
84 throw new ArgumentNullException ("baseName is 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));
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.");
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()
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;
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)
167 throw new ArgumentNullException("name is null");
171 culture=CultureInfo.CurrentUICulture;
175 ResourceSet set=InternalGetResourceSet(culture, true, true);
179 obj=set.GetObject(name, ignoreCase);
185 /* Try parent cultures */
188 culture=culture.Parent;
190 set=InternalGetResourceSet(culture, true, true);
192 obj=set.GetObject(name, ignoreCase);
197 } while(!culture.Equals(neutral_culture) &&
198 !culture.Equals(CultureInfo.InvariantCulture));
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.");
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)
226 throw new ArgumentNullException ("Name is null.");
230 culture=CultureInfo.CurrentUICulture;
234 ResourceSet set=InternalGetResourceSet(culture, true, true);
238 str=set.GetString(name, ignoreCase);
244 /* Try parent cultures */
247 culture=culture.Parent;
249 set=InternalGetResourceSet(culture, true, true);
251 str=set.GetString(name, ignoreCase);
256 } while(!culture.Equals(neutral_culture) &&
257 !culture.Equals(CultureInfo.InvariantCulture));
263 protected virtual string GetResourceFileName (CultureInfo culture)
265 if(culture.Equals(CultureInfo.InvariantCulture)) {
266 return(BaseNameField + ".resources");
268 return(BaseNameField + "." + culture.Name + ".resources");
272 static Stream GetManifestResourceStreamNoCase (Assembly ass, string fn)
274 foreach (string s in ass.GetManifestResourceNames ())
275 if (String.Compare (fn, s, true, CultureInfo.InvariantCulture) == 0)
276 return ass.GetManifestResourceStream (s);
280 protected virtual ResourceSet InternalGetResourceSet (CultureInfo culture, bool Createifnotexists, bool tryParents)
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 : "");
289 throw new MissingManifestResourceException (msg);
291 /* if we already have this resource set, return it */
292 set=(ResourceSet)ResourceSets[culture];
297 if(MainAssembly != null) {
298 /* Assembly resources */
300 string filename=GetResourceFileName(culture);
302 stream=MainAssembly.GetManifestResourceStream(filename);
304 stream = GetManifestResourceStreamNoCase (MainAssembly, filename);
307 /* Try a satellite assembly */
308 Version sat_version=GetSatelliteContractVersion(MainAssembly);
311 a = MainAssembly.GetSatelliteAssembly (culture, sat_version);
312 stream=a.GetManifestResourceStream(filename);
314 stream = GetManifestResourceStreamNoCase (a, filename);
316 } catch (Exception) {} // Ignored
319 if(stream!=null && Createifnotexists==true) {
320 object[] args=new Object[1];
325 * MissingMethodException, or
326 * just let someone else deal
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}\".";
336 msg = String.Format (msg, MainAssembly != null ? MainAssembly.GetName ().Name : "", filename);
338 throw new MissingManifestResourceException (msg);
340 } else if(resourceDir != null) {
342 string filename=Path.Combine(resourceDir, this.GetResourceFileName(culture));
343 if(File.Exists(filename) &&
344 Createifnotexists==true) {
345 object[] args=new Object[1];
350 * MissingMethodException, or
351 * just let someone else deal
354 set=(ResourceSet)Activator.CreateInstance(resourceSetType, args);
358 if(set==null && tryParents==true) {
359 // avoid endless recursion
360 if (!culture.Equals(CultureInfo.InvariantCulture))
361 set = InternalGetResourceSet (culture.Parent, Createifnotexists, tryParents);
365 ResourceSets.Add(culture, set);
371 public virtual void ReleaseAllResources ()
375 foreach (ResourceSet r in ResourceSets)
377 ResourceSets.Clear();
381 protected static CultureInfo GetNeutralResourcesLanguage (Assembly a)
385 attrs=a.GetCustomAttributes(typeof(NeutralResourcesLanguageAttribute), false);
387 if(attrs.Length==0) {
388 return(CultureInfo.InvariantCulture);
390 NeutralResourcesLanguageAttribute res_attr=(NeutralResourcesLanguageAttribute)attrs[0];
392 return(new CultureInfo(res_attr.CultureName));
396 protected static Version GetSatelliteContractVersion (Assembly a)
400 attrs=a.GetCustomAttributes(typeof(SatelliteContractVersionAttribute), false);
402 if(attrs.Length==0) {
405 SatelliteContractVersionAttribute sat_attr=(SatelliteContractVersionAttribute)attrs[0];
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
415 return(new Version(sat_attr.Version));