* roottypes.cs: Rename from tree.cs.
[mono.git] / mcs / class / I18N / Common / Manager.cs
1 /*
2  * Manager.cs - Implementation of the "I18N.Common.Manager" class.
3  *
4  * Copyright (c) 2002  Southern Storm Software, Pty Ltd
5  *
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:
12  *
13  * The above copyright notice and this permission notice shall be included
14  * in all copies or substantial portions of the Software.
15  *
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.
23  */
24
25 namespace I18N.Common
26 {
27
28 using System;
29 using System.IO;
30 using System.Text;
31 using System.Globalization;
32 using System.Collections;
33 using System.Reflection;
34 using System.Security;
35
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.
40
41 public class Manager
42 {
43         // The primary I18N manager.
44         private static Manager manager;
45
46         // Internal state.
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 ();
51
52         // Constructor.
53         private Manager()
54                         {
55                                 handlers = new Hashtable (CaseInsensitiveHashCodeProvider.Default,
56                                                           CaseInsensitiveComparer.Default);
57                                 active = new Hashtable(16);
58                                 assemblies = new Hashtable(8);
59                                 LoadClassList();
60                         }
61
62         // Get the primary I18N manager instance.
63         public static Manager PrimaryManager
64                         {
65                                 get
66                                 {
67                                         lock(lockobj)
68                                         {
69                                                 if(manager == null)
70                                                 {
71                                                         manager = new Manager();
72                                                 }
73                                                 return manager;
74                                         }
75                                 }
76                         }
77
78         // Normalize a name.
79         // FIXME: This means, we accept invalid names such as "euc_jp"
80         private static String Normalize(String name)
81                         {
82                         #if ECMA_COMPAT
83                                 return (name.ToLower()).Replace('-', '_');
84                         #else
85                                 return (name.ToLower(CultureInfo.InvariantCulture))
86                                                         .Replace('-', '_');
87                         #endif
88                         }
89
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)
93                         {
94                                 return (Instantiate("CP" + codePage.ToString()) as Encoding);
95                         }
96
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)
100                         {
101                                 // Validate the parameter.
102                                 if(name == null)
103                                 {
104                                         return null;
105                                 }
106
107                                 string orgName = name;
108
109                                 // Normalize the encoding name.
110                                 name = Normalize(name);
111
112                                 // Try to find a class called "ENCname".
113                                 Encoding e = Instantiate ("ENC" + name) as Encoding;
114                                 if (e == null)
115                                         e = Instantiate (name) as Encoding;
116
117                                 if (e == null) {
118                                         // Try windows aliases
119                                         string alias = Handlers.GetAlias (name);
120                                         if (alias != null) {
121                                                 e = Instantiate ("ENC" + alias) as Encoding;
122                                                 if (e == null)
123                                                         e = Instantiate (alias) as Encoding;
124                                         }
125                                 }
126                                 if (e == null)
127                                         return null;
128
129                                 // e.g. Neither euc_jp nor shift-jis not allowed (Normalize() badness)
130                                 if (orgName.IndexOf ('_') > 0 && e.WebName.IndexOf ('-') > 0)
131                                         return null;
132                                 if (orgName.IndexOf ('-') > 0 && e.WebName.IndexOf ('_') > 0)
133                                         return null;
134                                 return e;
135                         }
136         
137         // List of hex digits for use by "GetCulture".
138         private const String hex = "0123456789abcdef";
139
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)
143                         {
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();
151
152                                 // Try looking for an override culture handler.
153                                 if(useUserOverride)
154                                 {
155                                         Object obj = Instantiate("CIDO" + name);
156                                         if(obj != null)
157                                         {
158                                                 return (obj as CultureInfo);
159                                         }
160                                 }
161
162                                 // Look for the generic non-override culture.
163                                 return (Instantiate("CID" + name) as CultureInfo);
164                         }
165
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)
169                         {
170                                 // Validate the parameter.
171                                 if(name == null)
172                                 {
173                                         return null;
174                                 }
175
176                                 // Normalize the culture name.
177                                 name = Normalize(name);
178
179                                 // Try looking for an override culture handler.
180                                 if(useUserOverride)
181                                 {
182                                         Object obj = Instantiate("CNO" + name.ToString());
183                                         if(obj != null)
184                                         {
185                                                 return (obj as CultureInfo);
186                                         }
187                                 }
188
189                                 // Look for the generic non-override culture.
190                                 return (Instantiate("CN" + name.ToString()) as CultureInfo);
191                         }
192
193         // Instantiate a handler class.  Returns null if it is not
194         // possible to instantiate the class.
195         internal Object Instantiate(String name)
196                         {
197                                 Object handler;
198                                 String region;
199                                 Assembly assembly;
200                                 Type type;
201
202                                 lock(this)
203                                 {
204                                         // See if we already have an active handler by this name.
205                                         handler = active[name];
206                                         if(handler != null)
207                                         {
208                                                 return handler;
209                                         }
210
211                                         // Determine which region assembly handles the class.
212                                         region = (String)(handlers[name]);
213                                         if(region == null)
214                                         {
215                                                 // The class does not exist in any region assembly.
216                                                 return null;
217                                         }
218
219                                         // Find the region-specific assembly and load it.
220                                         assembly = (Assembly)(assemblies[region]);
221                                         if(assembly == null)
222                                         {
223                                                 try
224                                                 {
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);
229                                                 }
230                                                 catch(SystemException)
231                                                 {
232                                                         assembly = null;
233                                                 }
234                                                 if(assembly == null)
235                                                 {
236                                                         return null;
237                                                 }
238                                                 assemblies[region] = assembly;
239                                         }
240
241                                         // Look for the class within the region-specific assembly.
242                                         type = assembly.GetType(region + "." + name, false, true);
243                                         if(type == null)
244                                         {
245                                                 return null;
246                                         }
247
248                                         // Invoke the constructor, which we assume is public
249                                         // and has zero arguments.
250                                         try
251                                         {
252                                                 handler = type.InvokeMember
253                                                                 (String.Empty,
254                                                                  BindingFlags.CreateInstance |
255                                                                         BindingFlags.Public |
256                                                                         BindingFlags.NonPublic |
257                                                                         BindingFlags.Instance,
258                                                                  null, null, null, null, null, null);
259                                         }
260                                         catch(MissingMethodException)
261                                         {
262                                                 // The constructor was not present.
263                                                 return null;
264                                         }
265                                         catch(SecurityException)
266                                         {
267                                                 // The constructor was inaccessible.
268                                                 return null;
269                                         }
270
271                                         // Add the handler to the active handlers cache.
272                                         active.Add(name, handler);
273
274                                         // Return the handler to the caller.
275                                         return handler;
276                                 }
277                         }
278
279         // Load the list of classes that are present in all region assemblies.
280         private void LoadClassList()
281                         {
282                                 FileStream stream;
283
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.
288                                 //
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.
293                                 try
294                                 {
295                                         stream = Assembly.GetExecutingAssembly()
296                                                                 .GetFile("I18N-handlers.def");
297                                         if(stream == null)
298                                         {
299                                                 LoadInternalClasses();
300                                                 return;
301                                         }
302                                 }
303                                 catch(FileLoadException)
304                                 {
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();
310                                         return;
311                                 }
312
313                                 // Load the class list from the stream.
314                                 StreamReader reader = new StreamReader(stream);
315                                 String line;
316                                 int posn;
317                                 while((line = reader.ReadLine()) != null)
318                                 {
319                                         // Skip comment lines in the input.
320                                         if(line.Length == 0 || line[0] == '#')
321                                         {
322                                                 continue;
323                                         }
324
325                                         // Split the line into namespace and name.  We assume
326                                         // that the line has the form "I18N.<Region>.<Name>".
327                                         posn = line.LastIndexOf('.');
328                                         if(posn != -1)
329                                         {
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))
334                                                 {
335                                                         handlers.Add(name, line.Substring(0, posn));
336                                                 }
337                                         }
338                                 }
339                                 reader.Close();
340                         }
341
342         // Load the list of classes from the internal list.
343         private void LoadInternalClasses()
344                         {
345                                 int posn;
346                                 foreach(String line in Handlers.List)
347                                 {
348                                         // Split the line into namespace and name.  We assume
349                                         // that the line has the form "I18N.<Region>.<Name>".
350                                         posn = line.LastIndexOf('.');
351                                         if(posn != -1)
352                                         {
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))
357                                                 {
358                                                         handlers.Add(name, line.Substring(0, posn));
359                                                 }
360                                         }
361                                 }
362                         }
363
364 }; // class Manager
365
366 }; // namespace I18N.Common