* roottypes.cs: Rename from tree.cs.
[mono.git] / mcs / class / corlib / System.Resources / ResourceManager.cs
index 762b36b3a90b36f9193031a76aff5dbee89ebc74..8d6c540334646d47346b37089ff398ced9318d86 100644 (file)
 //     
 // System.Resources.ResourceManager.cs
 //
-// Author:
+// Authors:
 //     Duncan Mak (duncan@ximian.com)
+//     Dick Porter (dick@ximian.com)
 //
-// 2001 (C) Ximian, Inc. http://www.ximian.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.IO;
 
-namespace System.Resources {
-          public class ResourceManager {
-                        public static readonly int HeaderVersionNumber;
-                        public static readonly int MagicNumber;
+namespace System.Resources
+{
+       [Serializable]
+       public class ResourceManager
+       {
+               public static readonly int HeaderVersionNumber = 1;
+               public static readonly int MagicNumber = unchecked((int)0xBEEFCACE);
 
-                        protected string BaseNameField;
-                        protected Assembly MainAssembly;
-                        protected Hashtable Resourcesets;
-                        
-                        private bool ignoreCase;
-                        private Type resourceSetType;
-
-                        // constructors
-                        public ResourceManager () {}
-                        public ResourceManager (Type resourceSource) {
-                                   if (resourceSource == null)
-                                                 throw new ArgumentNullException ("resourceSource is null.");
-                                   resourceSetType = resourceSource; // TODO Incomplete
-                        }
-                        public ResourceManager (string baseName, Assembly assembly) {
-                                   if (baseName == null || assembly == null)
-                                                 throw new ArgumentNullException ("The arguments are null.");
-                                   // TODO
-                        }
-                        public ResourceManager (string baseName, Assembly assembly, Type usingResourceSet) {
-                                   if (baseName == null || assembly == null)
-                                                 throw new ArgumentNullException ("The arguments are null.");
-
-                                   if (usingResourceSet != null)
-                                                 if (!usingResourceSet.isSubclassOf (Typeof (ResourceSet)))
-                                                        throw new ArgumentException ("Type must be derived from ResourceSet.");
-                        }
-
-                        public static ResourceManager CreateFileBasedResourceManager (string baseName,
-                                                                                                                          string resourceDir,
-                                                                                                                          Type 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 ResourceSet GetResourceSet (CultureInfo culture,
-                                                                                           bool createIfNotExists,
-                                                                                           bool tryParents) {
-                                   if (culture == null)
-                                                 throw new ArgumentNullException ("CultureInfo is a null reference.");
-                        }
-
-                        public virtual string GetString (string name) {
-                                   if (name == null)
-                                                 throw new ArgumentNullException ("Name is null.");
-                                   if (ResourceSets.Contains (name)) {
-                                                 if (!ResourceSets[name] is string)
-                                                               throw new InvalidOperationException ("The resource is not a string.");
-                                                 return ResourceSets[name].ToString();
-                                   }
-                                   // TODO check for correctness.                                  
-                        }
+               protected string BaseNameField;
+               protected Assembly MainAssembly;
+               // Maps cultures to ResourceSet objects
+               protected Hashtable ResourceSets;
+               
+               private bool ignoreCase;
+               private Type resourceSetType;
+               private String resourceDir;
+               
+               /* Recursing through culture parents stops here */
+               private CultureInfo neutral_culture;
+               
+               // constructors
+               protected ResourceManager () {
+                       ResourceSets=new Hashtable();
+                       ignoreCase=false;
+                       resourceSetType=typeof(ResourceSet);
+                       resourceDir=null;
+                       neutral_culture=null;
+               }
+               
+               public ResourceManager (Type resourceSource) : this()
+               {
+                       if (resourceSource == null)
+                               throw new ArgumentNullException ("resourceSource is null.");
+                       
+                       BaseNameField = resourceSource.FullName;
+                       MainAssembly = resourceSource.Assembly;
+
+                       /* Temporary workaround for bug 43567 */
+                       resourceSetType = typeof(ResourceSet);
+                       neutral_culture = GetNeutralResourcesLanguage(MainAssembly);
+               }
+               
+               public ResourceManager (string baseName, Assembly assembly) : this()
+               {
+                       if (baseName == null)
+                               throw new ArgumentNullException ("baseName is null.");
+                       if(assembly == null)
+                               throw new ArgumentNullException ("assembly is null.");
+                       
+                       BaseNameField = baseName;
+                       MainAssembly = assembly;
+                       neutral_culture = GetNeutralResourcesLanguage(MainAssembly);
+               }
                         
-                        public virtual string GetString (string name, CultureInfo culture) {
-                                   if (name == null)
-                                                 throw new ArgumentNullException ("Name is null.");
-                               return name;
-                        }
-
-                        protected virtual string GetResourceFileName (CultureInfo culture) {
-                                   return new culture.Name + ".resources"; // TODO check for correctness.
-                        }
-
-                        protected virtual ResourceSet InternalGetResourceSet (CultureInfo culture,
-                                                                                                               bool Createifnotexists,
-                                                                                                               bool tryParents) {}
-                                                 
-                        public virtual void ReleaseAllResources () {
-                                   foreach (ResourceSet r in ResourceSets)
-                                                 r.Close();
-                        }
-
-                        protected static CultureInfo GetNeutralResourcesLanguage (Assembly a) {
-                                   foreach (Attribute attribute in a.GetCustomAttributes ()) {
-                                                 if (attribute is NeutralResourcesLanguageAttribute)
-                                                               return new Cultureinfo (attribute.CultureName);
-                                   }
-                                   return null;
-                        }
-
-                        public static Version GetSatelliteContractVersion (Assembly a) {
-
-                                   foreach (Attribute attribute in a.GetCustomAttributes ()) {
-                                                 if (attribute is SatelliteContractVersionAttribute)
-                                                               return new Version (attribute.Version);
-                                   }
-                                   return null;
-                        }
-          }
+               private Type CheckResourceSetType(Type usingResourceSet)
+               {
+                       if(usingResourceSet==null) {
+                               return(typeof(ResourceSet));
+                       } else {
+                               if (!usingResourceSet.IsSubclassOf (typeof (ResourceSet)))
+                                       throw new ArgumentException ("Type must be from ResourceSet.");
+                               
+                               return(usingResourceSet);
+                       }
+               }
+               
+               public ResourceManager (string baseName, Assembly assembly, Type usingResourceSet) : this()
+               {
+                       if (baseName == null)
+                               throw new ArgumentNullException ("baseName is null.");
+                       if(assembly == null)
+                               throw new ArgumentNullException ("assembly is null.");
+                       
+                       BaseNameField = baseName;
+                       MainAssembly = assembly;
+                       resourceSetType = CheckResourceSetType(usingResourceSet);
+                       neutral_culture = GetNeutralResourcesLanguage(MainAssembly);
+               }
+               
+               /* Private constructor for CreateFileBasedResourceManager */
+               private ResourceManager(String baseName, String resourceDir, Type usingResourceSet) : this()
+               {
+                       if(baseName==null) {
+                               throw new ArgumentNullException("The base name is null");
+                       }
+                       if(baseName.EndsWith(".resources")) {
+                               throw new ArgumentException("The base name ends in '.resources'");
+                       }
+                       if(resourceDir==null) {
+                               throw new ArgumentNullException("The resourceDir is null");
+                       }
+
+                       BaseNameField = baseName;
+                       MainAssembly = null;
+                       resourceSetType = CheckResourceSetType(usingResourceSet);
+                       this.resourceDir = resourceDir;
+               }
+               
+               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 is null");
+                       }
+
+                       if(culture==null) {
+                               culture=CultureInfo.CurrentUICulture;
+                       }
+
+                       lock(this) {
+                               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 ("CultureInfo is a null reference.");
+                       }
+
+                       lock(this) {
+                               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 is null.");
+                       }
+
+                       if(culture==null) {
+                               culture=CultureInfo.CurrentUICulture;
+                       }
+
+                       lock(this) {
+                               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");
+                       }
+               }
+               
+               static Stream GetManifestResourceStreamNoCase (Assembly ass, string fn)
+               {
+                       foreach (string s in ass.GetManifestResourceNames ())
+                               if (String.Compare (fn, s, true, CultureInfo.InvariantCulture) == 0)
+                                       return ass.GetManifestResourceStream (s);
+                       return null;
+               }
+               
+               protected virtual ResourceSet InternalGetResourceSet (CultureInfo culture, bool Createifnotexists, bool tryParents)
+               {
+                       ResourceSet set;
+                       
+                       if (culture == null) {
+                               string msg = String.Format ("Could not find any resource appropiate for the " +
+                                                           "specified culture or its parents (assembly:{0})",
+                                                           MainAssembly != null ? MainAssembly.GetName ().Name : "");
+                                                           
+                               throw new MissingManifestResourceException (msg);
+                       }
+                       /* if we already have this resource set, return it */
+                       set=(ResourceSet)ResourceSets[culture];
+                       if(set!=null) {
+                               return(set);
+                       }
+
+                       if(MainAssembly != null) {
+                               /* Assembly resources */
+                               Stream stream;
+                               string filename=GetResourceFileName(culture);
+                               
+                               stream=MainAssembly.GetManifestResourceStream(filename);
+                               if (stream == null)
+                                       stream = GetManifestResourceStreamNoCase (MainAssembly, filename);
+                               
+                               if(stream==null) {
+                                       /* Try a satellite assembly */
+                                       Version sat_version=GetSatelliteContractVersion(MainAssembly);
+                                       Assembly a = null;
+                                       try {
+                                               a = MainAssembly.GetSatelliteAssembly (culture, sat_version);
+                                               stream=a.GetManifestResourceStream(filename);
+                                               if (stream == null)
+                                                       stream = GetManifestResourceStreamNoCase (a, filename);
+                                       
+                                       } catch (Exception) {} // Ignored
+                               }
+
+                               if(stream!=null && Createifnotexists==true) {
+                                       object[] args=new Object[1];
+
+                                       args[0]=stream;
+                                       
+                                       /* 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 = "Could not find any resource appropiate for the " +
+                                                    "specified culture or its parents. " +
+                                                    "Make sure \"{1}\" was correctly embedded or " +
+                                                    "linked into assembly \"{0}\".";
+                                                    
+                                       msg = String.Format (msg, MainAssembly != null ? MainAssembly.GetName ().Name : "", filename);
+                                                           
+                                       throw new MissingManifestResourceException (msg);
+                               }
+                       } else if(resourceDir != null) {
+                               /* File resources */
+                               string filename=Path.Combine(resourceDir, this.GetResourceFileName(culture));
+                               if(File.Exists(filename) &&
+                                  Createifnotexists==true) {
+                                       object[] args=new Object[1];
+
+                                       args[0]=filename;
+                                       
+                                       /* should we catch
+                                        * MissingMethodException, or
+                                        * just let someone else deal
+                                        * with it?
+                                        */
+                                       set=(ResourceSet)Activator.CreateInstance(resourceSetType, args);
+                               }
+                       }
+
+                       if(set==null && tryParents==true) {
+                               // avoid endless recursion
+                               if (!culture.Equals(CultureInfo.InvariantCulture))
+                                       set = InternalGetResourceSet (culture.Parent, Createifnotexists, tryParents);
+                       }
+
+                       if(set!=null) {
+                               ResourceSets.Add(culture, set);
+                       }
+                       
+                       return(set);
+               }
+                  
+               public virtual void ReleaseAllResources ()
+               {
+                       lock(this) 
+                       {
+                               foreach (ResourceSet r in ResourceSets)
+                                       r.Close();
+                               ResourceSets.Clear();
+                       }
+               }
+
+               protected static CultureInfo GetNeutralResourcesLanguage (Assembly a)
+               {
+                       object[] attrs;
+
+                       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;
+                       
+                       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));
+                       }
+               }
+       }
 }