2 * Manager.cs - Implementation of the "I18N.Common.Manager" class.
4 * Copyright (c) 2002 Southern Storm Software, Pty Ltd
6 * Permission is hereby granted, free of charge, to any person obtaining
7 * a copy of this software and associated documentation files (the "Software"),
8 * to deal in the Software without restriction, including without limitation
9 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
10 * and/or sell copies of the Software, and to permit persons to whom the
11 * Software is furnished to do so, subject to the following conditions:
13 * The above copyright notice and this permission notice shall be included
14 * in all copies or substantial portions of the Software.
16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
17 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
19 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
20 * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
21 * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
22 * OTHER DEALINGS IN THE SOFTWARE.
31 using System.Globalization;
32 using System.Collections;
33 using System.Reflection;
34 using System.Security;
36 // This class provides the primary entry point into the I18N
37 // library. Users of the library start by getting the value
38 // of the "PrimaryManager" property. They then invoke methods
39 // on the manager to obtain further I18N information.
43 // The primary I18N manager.
44 private static Manager manager;
47 private Hashtable handlers; // List of all handler classes.
48 private Hashtable active; // Currently active handlers.
49 private Hashtable assemblies; // Currently loaded region assemblies.
50 static readonly object lockobj = new object ();
55 handlers = new Hashtable (CaseInsensitiveHashCodeProvider.Default,
56 CaseInsensitiveComparer.Default);
57 active = new Hashtable(16);
58 assemblies = new Hashtable(8);
62 // Get the primary I18N manager instance.
63 public static Manager PrimaryManager
71 manager = new Manager();
79 // FIXME: This means, we accept invalid names such as "euc_jp"
80 private static String Normalize(String name)
83 return (name.ToLower()).Replace('-', '_');
85 return (name.ToLower(CultureInfo.InvariantCulture))
90 // Get an encoding object for a specific code page.
91 // Returns NULL if the code page is not available.
92 public Encoding GetEncoding(int codePage)
94 return (Instantiate("CP" + codePage.ToString()) as Encoding);
97 // Get an encoding object for a specific Web encoding.
98 // Returns NULL if the encoding is not available.
99 public Encoding GetEncoding(String name)
101 // Validate the parameter.
107 string orgName = name;
109 // Normalize the encoding name.
110 name = Normalize(name);
112 // Try to find a class called "ENCname".
113 Encoding e = Instantiate ("ENC" + name) as Encoding;
115 e = Instantiate (name) as Encoding;
118 // Try windows aliases
119 string alias = Handlers.GetAlias (name);
121 e = Instantiate ("ENC" + alias) as Encoding;
123 e = Instantiate (alias) as Encoding;
129 // e.g. Neither euc_jp nor shift-jis not allowed (Normalize() badness)
130 if (orgName.IndexOf ('_') > 0 && e.WebName.IndexOf ('-') > 0)
132 if (orgName.IndexOf ('-') > 0 && e.WebName.IndexOf ('_') > 0)
137 // List of hex digits for use by "GetCulture".
138 private const String hex = "0123456789abcdef";
140 // Get a specific culture by identifier. Returns NULL
141 // if the culture information is not available.
142 public CultureInfo GetCulture(int culture, bool useUserOverride)
144 // Create the hex version of the culture identifier.
145 StringBuilder builder = new StringBuilder();
146 builder.Append(hex[(culture >> 12) & 0x0F]);
147 builder.Append(hex[(culture >> 8) & 0x0F]);
148 builder.Append(hex[(culture >> 4) & 0x0F]);
149 builder.Append(hex[culture & 0x0F]);
150 String name = builder.ToString();
152 // Try looking for an override culture handler.
155 Object obj = Instantiate("CIDO" + name);
158 return (obj as CultureInfo);
162 // Look for the generic non-override culture.
163 return (Instantiate("CID" + name) as CultureInfo);
166 // Get a specific culture by name. Returns NULL if the
167 // culture informaion is not available.
168 public CultureInfo GetCulture(String name, bool useUserOverride)
170 // Validate the parameter.
176 // Normalize the culture name.
177 name = Normalize(name);
179 // Try looking for an override culture handler.
182 Object obj = Instantiate("CNO" + name.ToString());
185 return (obj as CultureInfo);
189 // Look for the generic non-override culture.
190 return (Instantiate("CN" + name.ToString()) as CultureInfo);
193 // Instantiate a handler class. Returns null if it is not
194 // possible to instantiate the class.
195 internal Object Instantiate(String name)
204 // See if we already have an active handler by this name.
205 handler = active[name];
211 // Determine which region assembly handles the class.
212 region = (String)(handlers[name]);
215 // The class does not exist in any region assembly.
219 // Find the region-specific assembly and load it.
220 assembly = (Assembly)(assemblies[region]);
225 // we use the same strong name as I18N.dll except the assembly name
226 AssemblyName myName = typeof(Manager).Assembly.GetName();
227 myName.Name = region;
228 assembly = Assembly.Load(myName);
230 catch(SystemException)
238 assemblies[region] = assembly;
241 // Look for the class within the region-specific assembly.
242 type = assembly.GetType(region + "." + name, false, true);
248 // Invoke the constructor, which we assume is public
249 // and has zero arguments.
252 handler = type.InvokeMember
254 BindingFlags.CreateInstance |
255 BindingFlags.Public |
256 BindingFlags.NonPublic |
257 BindingFlags.Instance,
258 null, null, null, null, null, null);
260 catch(MissingMethodException)
262 // The constructor was not present.
265 catch(SecurityException)
267 // The constructor was inaccessible.
271 // Add the handler to the active handlers cache.
272 active.Add(name, handler);
274 // Return the handler to the caller.
279 // Load the list of classes that are present in all region assemblies.
280 private void LoadClassList()
284 // Look for "I18N-handlers.def" in the same directory
285 // as this assembly. Note: this assumes that the
286 // "Assembly.GetFile" method can access files that
287 // aren't explicitly part of the assembly manifest.
289 // This is necessary because the "I18N-handlers.def"
290 // file is generated after the "i18n" assembly is
291 // compiled and linked. So it cannot be embedded
292 // directly into the assembly manifest.
295 stream = Assembly.GetExecutingAssembly()
296 .GetFile("I18N-handlers.def");
299 LoadInternalClasses();
303 catch(FileLoadException)
305 // The file does not exist, or the runtime engine
306 // refuses to implement the necessary semantics.
307 // Fall back to an internal list, which must be
308 // kept up to date manually.
309 LoadInternalClasses();
313 // Load the class list from the stream.
314 StreamReader reader = new StreamReader(stream);
317 while((line = reader.ReadLine()) != null)
319 // Skip comment lines in the input.
320 if(line.Length == 0 || line[0] == '#')
325 // Split the line into namespace and name. We assume
326 // that the line has the form "I18N.<Region>.<Name>".
327 posn = line.LastIndexOf('.');
330 // Add the namespace to the "handlers" hash,
331 // attached to the name of the handler class.
332 String name = line.Substring(posn + 1);
333 if(!handlers.Contains(name))
335 handlers.Add(name, line.Substring(0, posn));
342 // Load the list of classes from the internal list.
343 private void LoadInternalClasses()
346 foreach(String line in Handlers.List)
348 // Split the line into namespace and name. We assume
349 // that the line has the form "I18N.<Region>.<Name>".
350 posn = line.LastIndexOf('.');
353 // Add the namespace to the "handlers" hash,
354 // attached to the name of the handler class.
355 String name = line.Substring(posn + 1);
356 if(!handlers.Contains(name))
358 handlers.Add(name, line.Substring(0, posn));
366 }; // namespace I18N.Common