2 // System.Resources.ResourceManager.cs
5 // Duncan Mak (duncan@ximian.com)
6 // Dick Porter (dick@ximian.com)
7 // Alexander Olk (alex.olk@googlemail.com)
9 // (C) 2001, 2002 Ximian, Inc. http://www.ximian.com
13 // Copyright (C) 2004 Novell, Inc (http://www.novell.com)
15 // Permission is hereby granted, free of charge, to any person obtaining
16 // a copy of this software and associated documentation files (the
17 // "Software"), to deal in the Software without restriction, including
18 // without limitation the rights to use, copy, modify, merge, publish,
19 // distribute, sublicense, and/or sell copies of the Software, and to
20 // permit persons to whom the Software is furnished to do so, subject to
21 // the following conditions:
23 // The above copyright notice and this permission notice shall be
24 // included in all copies or substantial portions of the Software.
26 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
27 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
28 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
29 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
30 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
31 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
32 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
35 using System
.Collections
;
36 using System
.Reflection
;
37 using System
.Globalization
;
38 using System
.Runtime
.InteropServices
;
41 namespace System
.Resources
45 public class ResourceManager
47 static Hashtable ResourceCache
= new Hashtable ();
48 static Hashtable NonExistent
= Hashtable
.Synchronized (new Hashtable ());
49 public static readonly int HeaderVersionNumber
= 1;
50 public static readonly int MagicNumber
= unchecked ((int) 0xBEEFCACE);
52 protected string BaseNameField
;
53 protected Assembly MainAssembly
;
54 // Maps cultures to ResourceSet objects
55 protected Hashtable ResourceSets
;
57 private bool ignoreCase
;
58 private Type resourceSource
;
59 private Type resourceSetType
= typeof (RuntimeResourceSet
);
60 private String resourceDir
;
62 // Contains cultures which have no resource sets
64 /* Recursing through culture parents stops here */
65 private CultureInfo neutral_culture
;
67 private UltimateResourceFallbackLocation fallbackLocation
;
69 static Hashtable
GetResourceSets (Assembly assembly
, string basename
)
71 lock (ResourceCache
) {
72 string key
= String
.Empty
;
73 if (assembly
!= null) {
74 key
= assembly
.FullName
;
76 key
= basename
.GetHashCode ().ToString () + "@@";
78 if (basename
!= null && basename
!= String
.Empty
) {
79 key
+= "!" + basename
;
81 key
+= "!" + key
.GetHashCode ();
83 Hashtable tbl
= ResourceCache
[key
] as Hashtable
;
85 tbl
= Hashtable
.Synchronized (new Hashtable ());
86 ResourceCache
[key
] = tbl
;
93 protected ResourceManager ()
97 public ResourceManager (Type resourceSource
)
99 if (resourceSource
== null)
100 throw new ArgumentNullException ("resourceSource");
102 this.resourceSource
= resourceSource
;
103 BaseNameField
= resourceSource
.Name
;
104 MainAssembly
= resourceSource
.Assembly
;
105 ResourceSets
= GetResourceSets (MainAssembly
, BaseNameField
);
106 neutral_culture
= GetNeutralResourcesLanguage (MainAssembly
);
109 public ResourceManager (string baseName
, Assembly assembly
)
111 if (baseName
== null)
112 throw new ArgumentNullException ("baseName");
113 if (assembly
== null)
114 throw new ArgumentNullException ("assembly");
116 BaseNameField
= baseName
;
117 MainAssembly
= assembly
;
118 ResourceSets
= GetResourceSets (MainAssembly
, BaseNameField
);
119 neutral_culture
= GetNeutralResourcesLanguage (MainAssembly
);
122 private Type
CheckResourceSetType (Type usingResourceSet
, bool verifyType
)
124 if (usingResourceSet
== null)
125 return resourceSetType
;
127 if (verifyType
&& !typeof (ResourceSet
).IsAssignableFrom (usingResourceSet
))
128 throw new ArgumentException ("Type parameter"
129 + " must refer to a subclass of"
130 + " ResourceSet.", "usingResourceSet");
131 return usingResourceSet
;
134 public ResourceManager (string baseName
, Assembly assembly
, Type usingResourceSet
)
136 if (baseName
== null)
137 throw new ArgumentNullException ("baseName");
138 if (assembly
== null)
139 throw new ArgumentNullException ("assembly");
141 BaseNameField
= baseName
;
142 MainAssembly
= assembly
;
143 ResourceSets
= GetResourceSets (MainAssembly
, BaseNameField
);
144 resourceSetType
= CheckResourceSetType (usingResourceSet
, true);
145 neutral_culture
= GetNeutralResourcesLanguage (MainAssembly
);
148 /* Private constructor for CreateFileBasedResourceManager */
149 private ResourceManager(String baseName
, String resourceDir
, Type usingResourceSet
)
151 if (baseName
== null)
152 throw new ArgumentNullException ("baseName");
153 if (resourceDir
== null)
154 throw new ArgumentNullException("resourceDir");
156 BaseNameField
= baseName
;
157 this.resourceDir
= resourceDir
;
158 resourceSetType
= CheckResourceSetType (usingResourceSet
, false);
159 ResourceSets
= GetResourceSets (MainAssembly
, BaseNameField
);
162 public static ResourceManager
CreateFileBasedResourceManager (string baseName
,
163 string resourceDir
, Type usingResourceSet
)
165 return new ResourceManager (baseName
, resourceDir
, usingResourceSet
);
168 public virtual string BaseName
{
169 get { return BaseNameField; }
172 public virtual bool IgnoreCase
{
173 get { return ignoreCase; }
174 set { ignoreCase = value; }
177 public virtual Type ResourceSetType
{
178 get { return resourceSetType; }
181 public virtual object GetObject (string name
)
183 return GetObject (name
, null);
186 public virtual object GetObject (string name
, CultureInfo culture
)
189 throw new ArgumentNullException("name");
192 culture
= CultureInfo
.CurrentUICulture
;
195 ResourceSet
set = InternalGetResourceSet(culture
, true, true);
199 obj
= set.GetObject(name
, ignoreCase
);
204 /* Try parent cultures */
207 culture
= culture
.Parent
;
209 set = InternalGetResourceSet (culture
, true, true);
211 obj
= set.GetObject (name
, ignoreCase
);
215 } while (!culture
.Equals (neutral_culture
) &&
216 !culture
.Equals (CultureInfo
.InvariantCulture
));
222 public virtual ResourceSet
GetResourceSet (CultureInfo culture
,
223 bool createIfNotExists
, bool tryParents
)
227 throw new ArgumentNullException ("culture");
230 return InternalGetResourceSet (culture
, createIfNotExists
, tryParents
);
234 public virtual string GetString (string name
)
236 return GetString (name
, null);
239 public virtual string GetString (string name
, CultureInfo culture
)
242 throw new ArgumentNullException ("name");
245 culture
= CultureInfo
.CurrentUICulture
;
248 ResourceSet
set = InternalGetResourceSet (culture
, true, true);
252 str
= set.GetString (name
, ignoreCase
);
257 /* Try parent cultures */
260 culture
= culture
.Parent
;
261 set = InternalGetResourceSet (culture
, true, true);
263 str
= set.GetString(name
, ignoreCase
);
267 } while (!culture
.Equals (neutral_culture
) &&
268 !culture
.Equals (CultureInfo
.InvariantCulture
));
274 protected virtual string GetResourceFileName (CultureInfo culture
)
276 if (culture
.Equals (CultureInfo
.InvariantCulture
))
277 return BaseNameField
+ ".resources";
279 return BaseNameField
+ "." + culture
.Name
+ ".resources";
282 private string GetResourceFilePath (CultureInfo culture
)
285 if (resourceDir
!= null)
286 return Path
.Combine (resourceDir
, GetResourceFileName (culture
));
289 return GetResourceFileName (culture
);
292 Stream
GetManifestResourceStreamNoCase (Assembly ass
, string fn
)
294 string resourceName
= GetManifestResourceName (fn
);
296 foreach (string s
in ass
.GetManifestResourceNames ())
297 if (String
.Compare (resourceName
, s
, true, CultureInfo
.InvariantCulture
) == 0)
298 return ass
.GetManifestResourceStream (s
);
302 [CLSCompliant (false)]
304 public UnmanagedMemoryStream
GetStream (string name
)
306 return GetStream (name
, (CultureInfo
) null);
309 [CLSCompliant (false)]
311 public UnmanagedMemoryStream
GetStream (string name
, CultureInfo culture
)
314 throw new ArgumentNullException ("name");
316 culture
= CultureInfo
.CurrentUICulture
;
317 ResourceSet
set = InternalGetResourceSet (culture
, true, true);
318 return set.GetStream (name
, ignoreCase
);
321 protected virtual ResourceSet
InternalGetResourceSet (CultureInfo culture
, bool createIfNotExists
, bool tryParents
)
324 throw new ArgumentNullException ("key"); // 'key' instead of 'culture' to make a test pass
328 /* if we already have this resource set, return it */
329 set = (ResourceSet
) ResourceSets
[culture
];
333 if (NonExistent
.Contains (culture
))
336 if (MainAssembly
!= null) {
337 /* Assembly resources */
338 CultureInfo resourceCulture
= culture
;
340 // when the specified culture matches the neutral culture,
341 // then use the invariant resources
342 if (culture
.Equals (neutral_culture
))
343 resourceCulture
= CultureInfo
.InvariantCulture
;
345 Stream stream
= null;
347 string filename
= GetResourceFileName (resourceCulture
);
348 if (!resourceCulture
.Equals (CultureInfo
.InvariantCulture
)) {
349 /* Try a satellite assembly */
350 Version sat_version
= GetSatelliteContractVersion (MainAssembly
);
352 Assembly a
= MainAssembly
.GetSatelliteAssemblyNoThrow (
353 resourceCulture
, sat_version
);
355 stream
= a
.GetManifestResourceStream (filename
);
357 stream
= GetManifestResourceStreamNoCase (a
, filename
);
359 } catch (Exception
) {
363 stream
= MainAssembly
.GetManifestResourceStream (
364 resourceSource
, filename
);
366 stream
= GetManifestResourceStreamNoCase (
367 MainAssembly
, filename
);
370 if (stream
!= null && createIfNotExists
) {
371 object [] args
= new Object
[1] { stream }
;
374 * MissingMethodException, or
375 * just let someone else deal
378 set = (ResourceSet
) Activator
.CreateInstance (resourceSetType
, args
);
379 } else if (resourceCulture
.Equals (CultureInfo
.InvariantCulture
)) {
380 throw AssemblyResourceMissing (filename
);
382 } else if (resourceDir
!= null || BaseNameField
!= null) {
384 string filename
= GetResourceFilePath (culture
);
385 if (createIfNotExists
&& File
.Exists (filename
)) {
386 object [] args
= new Object
[1] { filename }
;
389 * MissingMethodException, or
390 * just let someone else deal
393 set = (ResourceSet
) Activator
.CreateInstance(
394 resourceSetType
, args
);
395 } else if (culture
.Equals (CultureInfo
.InvariantCulture
)) {
396 string msg
= string.Format ("Could not find any " +
397 "resources appropriate for the specified culture " +
398 "(or the neutral culture) on disk.{0}" +
399 "baseName: {1} locationInfo: {2} fileName: {3}",
400 Environment
.NewLine
, BaseNameField
, "<null>",
401 GetResourceFileName (culture
));
402 throw new MissingManifestResourceException (msg
);
406 if (set == null && tryParents
) {
407 // avoid endless recursion
408 if (!culture
.Equals (CultureInfo
.InvariantCulture
))
409 set = InternalGetResourceSet (culture
.Parent
,
410 createIfNotExists
, tryParents
);
414 ResourceSets
[culture
] = set;
416 NonExistent
[culture
] = culture
;
421 public virtual void ReleaseAllResources ()
424 foreach (ResourceSet r
in ResourceSets
.Values
)
426 ResourceSets
.Clear();
430 protected static CultureInfo
GetNeutralResourcesLanguage (Assembly a
)
432 object [] attrs
= a
.GetCustomAttributes (
433 typeof (NeutralResourcesLanguageAttribute
),
436 if (attrs
.Length
== 0) {
437 return CultureInfo
.InvariantCulture
;
439 NeutralResourcesLanguageAttribute res_attr
= (NeutralResourcesLanguageAttribute
) attrs
[0];
440 return new CultureInfo (res_attr
.CultureName
);
444 protected static Version
GetSatelliteContractVersion (Assembly a
)
446 object [] attrs
= a
.GetCustomAttributes (
447 typeof (SatelliteContractVersionAttribute
),
449 if (attrs
.Length
== 0) {
452 SatelliteContractVersionAttribute sat_attr
=
453 (SatelliteContractVersionAttribute
) attrs
[0];
455 /* Version(string) can throw
456 * ArgumentException if the version is
457 * invalid, but the spec for
458 * GetSatelliteContractVersion says we
459 * can throw the same exception for
460 * the same reason, so dont bother to
463 return new Version (sat_attr
.Version
);
467 [MonoTODO ("the property exists but is not respected")]
468 protected UltimateResourceFallbackLocation FallbackLocation
{
469 get { return fallbackLocation; }
470 set { fallbackLocation = value; }
474 MissingManifestResourceException
AssemblyResourceMissing (string fileName
)
476 AssemblyName aname
= MainAssembly
!= null ? MainAssembly
.GetName ()
479 string manifestName
= GetManifestResourceName (fileName
);
480 string msg
= string.Format ("Could not find any resources " +
481 "appropriate for the specified culture or the " +
482 "neutral culture. Make sure \"{0}\" was correctly " +
483 "embedded or linked into assembly \"{1}\" at " +
484 "compile time, or that all the satellite assemblies " +
485 "required are loadable and fully signed.",
486 manifestName
, aname
!= null ? aname
.Name
: string.Empty
);
487 throw new MissingManifestResourceException (msg
);
490 string GetManifestResourceName (string fn
)
492 string resourceName
= null;
493 if (resourceSource
!= null) {
494 if (resourceSource
.Namespace
!= null && resourceSource
.Namespace
.Length
> 0)
495 resourceName
= string.Concat (resourceSource
.Namespace
,