// // System.Resources.ResourceManager.cs // // Authors: // Duncan Mak (duncan@ximian.com) // Dick Porter (dick@ximian.com) // Alexander Olk (alex.olk@googlemail.com) // // (C) 2001, 2002 Ximian, Inc. http://www.ximian.com // // // Copyright (C) 2004 Novell, Inc (http://www.novell.com) // // Permission is hereby granted, free of charge, to any person obtaining // a copy of this software and associated documentation files (the // "Software"), to deal in the Software without restriction, including // without limitation the rights to use, copy, modify, merge, publish, // distribute, sublicense, and/or sell copies of the Software, and to // permit persons to whom the Software is furnished to do so, subject to // the following conditions: // // The above copyright notice and this permission notice shall be // included in all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. // using System.Collections; using System.Reflection; using System.Globalization; using System.Runtime.InteropServices; using System.IO; namespace System.Resources { [Serializable] [ComVisible (true)] public class ResourceManager { static readonly object thisLock = new object (); static readonly Hashtable ResourceCache = new Hashtable (); static readonly Hashtable NonExistent = Hashtable.Synchronized (new Hashtable ()); public static readonly int HeaderVersionNumber = 1; public static readonly int MagicNumber = unchecked ((int) 0xBEEFCACE); protected string BaseNameField; protected Assembly MainAssembly; // Maps cultures to ResourceSet objects #if NET_4_0 [Obsolete ("Use InternalGetResourceSet instead.")] #endif protected Hashtable ResourceSets; private bool ignoreCase; private Type resourceSource; private Type resourceSetType = typeof (RuntimeResourceSet); private String resourceDir; // Contains cultures which have no resource sets /* Recursing through culture parents stops here */ private CultureInfo neutral_culture; private UltimateResourceFallbackLocation fallbackLocation; static Hashtable GetResourceSets (Assembly assembly, string basename) { lock (ResourceCache) { string key = String.Empty; if (assembly != null) { key = assembly.FullName; } else { key = basename.GetHashCode ().ToString () + "@@"; } if (basename != null && basename != String.Empty) { key += "!" + basename; } else { key += "!" + key.GetHashCode (); } Hashtable tbl = ResourceCache [key] as Hashtable; if (tbl == null) { tbl = Hashtable.Synchronized (new Hashtable ()); ResourceCache [key] = tbl; } return tbl; } } // constructors protected ResourceManager () { } public ResourceManager (Type resourceSource) { if (resourceSource == null) throw new ArgumentNullException ("resourceSource"); this.resourceSource = resourceSource; BaseNameField = resourceSource.Name; MainAssembly = resourceSource.Assembly; ResourceSets = GetResourceSets (MainAssembly, BaseNameField); neutral_culture = GetNeutralResourcesLanguage (MainAssembly); } public ResourceManager (string baseName, Assembly assembly) { if (baseName == null) throw new ArgumentNullException ("baseName"); if (assembly == null) throw new ArgumentNullException ("assembly"); BaseNameField = baseName; MainAssembly = assembly; ResourceSets = GetResourceSets (MainAssembly, BaseNameField); neutral_culture = GetNeutralResourcesLanguage (MainAssembly); } private Type CheckResourceSetType (Type usingResourceSet, bool verifyType) { if (usingResourceSet == null) return resourceSetType; if (verifyType && !typeof (ResourceSet).IsAssignableFrom (usingResourceSet)) throw new ArgumentException ("Type parameter" + " must refer to a subclass of" + " ResourceSet.", "usingResourceSet"); return usingResourceSet; } public ResourceManager (string baseName, Assembly assembly, Type usingResourceSet) { if (baseName == null) throw new ArgumentNullException ("baseName"); if (assembly == null) throw new ArgumentNullException ("assembly"); BaseNameField = baseName; MainAssembly = assembly; ResourceSets = GetResourceSets (MainAssembly, BaseNameField); resourceSetType = CheckResourceSetType (usingResourceSet, true); neutral_culture = GetNeutralResourcesLanguage (MainAssembly); } /* Private constructor for CreateFileBasedResourceManager */ private ResourceManager(String baseName, String resourceDir, Type usingResourceSet) { if (baseName == null) throw new ArgumentNullException ("baseName"); if (resourceDir == null) throw new ArgumentNullException("resourceDir"); BaseNameField = baseName; this.resourceDir = resourceDir; resourceSetType = CheckResourceSetType (usingResourceSet, false); ResourceSets = GetResourceSets (MainAssembly, BaseNameField); } public static ResourceManager CreateFileBasedResourceManager (string baseName, string resourceDir, Type usingResourceSet) { return new ResourceManager (baseName, resourceDir, usingResourceSet); } public virtual string BaseName { get { return BaseNameField; } } public virtual bool IgnoreCase { get { return ignoreCase; } set { ignoreCase = value; } } public virtual Type ResourceSetType { get { return resourceSetType; } } public virtual object GetObject (string name) { return GetObject (name, null); } public virtual object GetObject (string name, CultureInfo culture) { if (name == null) throw new ArgumentNullException("name"); if (culture == null) culture = CultureInfo.CurrentUICulture; lock (thisLock) { ResourceSet set = InternalGetResourceSet(culture, true, true); object obj = null; if (set != null) { obj = set.GetObject(name, ignoreCase); if (obj != null) return obj; } /* Try parent cultures */ do { culture = culture.Parent; set = InternalGetResourceSet (culture, true, true); if (set != null) { obj = set.GetObject (name, ignoreCase); if (obj != null) return obj; } } while (!culture.Equals (neutral_culture) && !culture.Equals (CultureInfo.InvariantCulture)); } return null; } public virtual ResourceSet GetResourceSet (CultureInfo culture, bool createIfNotExists, bool tryParents) { if (culture == null) throw new ArgumentNullException ("culture"); lock (thisLock) { return InternalGetResourceSet (culture, createIfNotExists, tryParents); } } public virtual string GetString (string name) { return GetString (name, null); } public virtual string GetString (string name, CultureInfo culture) { if (name == null) throw new ArgumentNullException ("name"); if (culture == null) culture = CultureInfo.CurrentUICulture; lock (thisLock) { ResourceSet set = InternalGetResourceSet (culture, true, true); string str = null; if (set != null) { str = set.GetString (name, ignoreCase); if (str != null) return str; } /* Try parent cultures */ do { culture = culture.Parent; set = InternalGetResourceSet (culture, true, true); if (set != null) { str = set.GetString(name, ignoreCase); if (str != null) return str; } } while (!culture.Equals (neutral_culture) && !culture.Equals (CultureInfo.InvariantCulture)); } return null; } protected virtual string GetResourceFileName (CultureInfo culture) { if (culture.Equals (CultureInfo.InvariantCulture)) return BaseNameField + ".resources"; else return BaseNameField + "." + culture.Name + ".resources"; } private string GetResourceFilePath (CultureInfo culture) { #if !MOONLIGHT if (resourceDir != null) return Path.Combine (resourceDir, GetResourceFileName (culture)); else #endif return GetResourceFileName (culture); } Stream GetManifestResourceStreamNoCase (Assembly ass, string fn) { string resourceName = GetManifestResourceName (fn); foreach (string s in ass.GetManifestResourceNames ()) if (String.Compare (resourceName, s, true, CultureInfo.InvariantCulture) == 0) return ass.GetManifestResourceStream (s); return null; } [ComVisible (false)] public UnmanagedMemoryStream GetStream (string name) { return GetStream (name, (CultureInfo) null); } [ComVisible (false)] public UnmanagedMemoryStream GetStream (string name, CultureInfo culture) { if (name == null) throw new ArgumentNullException ("name"); if (culture == null) culture = CultureInfo.CurrentUICulture; ResourceSet set; lock (thisLock) { set = InternalGetResourceSet (culture, true, true); } return set.GetStream (name, ignoreCase); } protected virtual ResourceSet InternalGetResourceSet (CultureInfo culture, bool createIfNotExists, bool tryParents) { if (culture == null) throw new ArgumentNullException ("key"); // 'key' instead of 'culture' to make a test pass ResourceSet set = null; /* if we already have this resource set, return it */ set = (ResourceSet) ResourceSets [culture]; if (set != null) { return set; /* try { if (!set.IsDisposed) return set; } catch { // ignore } ResourceSets.Remove (culture); if (NonExistent.Contains (culture)) NonExistent.Remove (culture); set = null; */ } if (NonExistent.Contains (culture)) return null; if (MainAssembly != null) { /* Assembly resources */ CultureInfo resourceCulture = culture; // when the specified culture matches the neutral culture, // then use the invariant resources if (culture.Equals (neutral_culture)) resourceCulture = CultureInfo.InvariantCulture; Stream stream = null; string filename = GetResourceFileName (resourceCulture); if (!resourceCulture.Equals (CultureInfo.InvariantCulture)) { /* Try a satellite assembly */ Version sat_version = GetSatelliteContractVersion (MainAssembly); try { Assembly a = MainAssembly.GetSatelliteAssemblyNoThrow ( resourceCulture, sat_version); if (a != null){ stream = a.GetManifestResourceStream (filename); if (stream == null) stream = GetManifestResourceStreamNoCase (a, filename); } } catch (Exception) { // Ignored } } else { stream = MainAssembly.GetManifestResourceStream ( resourceSource, filename); if (stream == null) stream = GetManifestResourceStreamNoCase ( MainAssembly, filename); } if (stream != null && createIfNotExists) { object [] args = new Object [1] { stream }; /* should we catch * MissingMethodException, or * just let someone else deal * with it? */ set = (ResourceSet) Activator.CreateInstance (resourceSetType, args); } else if (resourceCulture.Equals (CultureInfo.InvariantCulture)) { throw AssemblyResourceMissing (filename); } } else if (resourceDir != null || BaseNameField != null) { /* File resources */ string filename = GetResourceFilePath (culture); if (createIfNotExists && File.Exists (filename)) { object [] args = new Object [1] { filename }; /* should we catch * MissingMethodException, or * just let someone else deal * with it? */ set = (ResourceSet) Activator.CreateInstance( resourceSetType, args); } else if (culture.Equals (CultureInfo.InvariantCulture)) { string msg = string.Format ("Could not find any " + "resources appropriate for the specified culture " + "(or the neutral culture) on disk.{0}" + "baseName: {1} locationInfo: {2} fileName: {3}", Environment.NewLine, BaseNameField, "", GetResourceFileName (culture)); throw new MissingManifestResourceException (msg); } } if (set == null && tryParents) { // avoid endless recursion if (!culture.Equals (CultureInfo.InvariantCulture)) set = InternalGetResourceSet (culture.Parent, createIfNotExists, tryParents); } if (set != null) ResourceSets [culture] = set; else NonExistent [culture] = culture; return set; } public virtual void ReleaseAllResources () { lock(this) { foreach (ResourceSet r in ResourceSets.Values) r.Close(); ResourceSets.Clear(); } } protected static CultureInfo GetNeutralResourcesLanguage (Assembly a) { object [] attrs = a.GetCustomAttributes ( typeof (NeutralResourcesLanguageAttribute), false); if (attrs.Length == 0) { return CultureInfo.InvariantCulture; } else { NeutralResourcesLanguageAttribute res_attr = (NeutralResourcesLanguageAttribute) attrs [0]; return new CultureInfo (res_attr.CultureName); } } protected static Version GetSatelliteContractVersion (Assembly a) { object [] attrs = a.GetCustomAttributes ( typeof (SatelliteContractVersionAttribute), false); if (attrs.Length == 0) { return null; } else { SatelliteContractVersionAttribute sat_attr = (SatelliteContractVersionAttribute) attrs[0]; /* Version(string) can throw * ArgumentException if the version is * invalid, but the spec for * GetSatelliteContractVersion says we * can throw the same exception for * the same reason, so dont bother to * catch it. */ return new Version (sat_attr.Version); } } [MonoTODO ("the property exists but is not respected")] protected UltimateResourceFallbackLocation FallbackLocation { get { return fallbackLocation; } set { fallbackLocation = value; } } MissingManifestResourceException AssemblyResourceMissing (string fileName) { AssemblyName aname = MainAssembly != null ? MainAssembly.GetName () : null; string manifestName = GetManifestResourceName (fileName); string msg = string.Format ("Could not find any resources " + "appropriate for the specified culture or the " + "neutral culture. Make sure \"{0}\" was correctly " + "embedded or linked into assembly \"{1}\" at " + "compile time, or that all the satellite assemblies " + "required are loadable and fully signed.", manifestName, aname != null ? aname.Name : string.Empty); throw new MissingManifestResourceException (msg); } string GetManifestResourceName (string fn) { string resourceName = null; if (resourceSource != null) { if (resourceSource.Namespace != null && resourceSource.Namespace.Length > 0) resourceName = string.Concat (resourceSource.Namespace, ".", fn); else resourceName = fn; } else { resourceName = fn; } return resourceName; } } }