2002-10-14 Martin Baulig <martin@gnome.org>
[mono.git] / mcs / class / System / System.Configuration / ConfigurationSettings.cs
1 //
2 // System.Configuration.ConfigurationSettings.cs
3 //
4 // Author:
5 //   Christopher Podurgiel (cpodurgiel@msn.com)
6 //   Gonzalo Paniagua Javier (gonzalo@ximian.com)
7 //
8 // C) Christopher Podurgiel
9 // (c) 2002 Ximian, Inc. (http://www.ximian.com)
10 //
11
12 using System;
13 using System.Collections;
14 using System.Collections.Specialized;
15 using System.IO;
16 using System.Reflection;
17 using System.Runtime.CompilerServices;
18 using System.Xml;
19 using System.Xml.XPath;
20
21 namespace System.Configuration
22 {
23         public sealed class ConfigurationSettings
24         {
25                 static IConfigurationSystem config;
26                         
27                 private ConfigurationSettings ()
28                 {
29                 }
30
31                 public static object GetConfig (string sectionName)
32                 {
33                         if (config == null)
34                                 config = DefaultConfig.GetInstance ();
35
36                         return config.GetConfig (sectionName);
37                 }
38
39                 public static NameValueCollection AppSettings
40                 {
41                         get {
42                                 object appSettings = GetConfig ("appSettings");
43                                 if (appSettings == null)
44                                         appSettings = new NameValueCollection ();
45
46                                 return (NameValueCollection) appSettings;
47                         }
48                 }
49
50         }
51
52         //
53         // class DefaultConfig: read configuration from machine.config file and application
54         // config file if available.
55         //
56         class DefaultConfig : IConfigurationSystem
57         {
58                 static string creatingInstance = "137213797382-asad";
59                 static string buildingData = "1797382-ladgasjkdg";
60                 static DefaultConfig instance;
61                 ConfigurationData config;
62
63                 private DefaultConfig ()
64                 {
65                 }
66
67                 public static DefaultConfig GetInstance ()
68                 {
69                         if (instance == null) {
70                                 lock (creatingInstance) {
71                                         if (instance == null) {
72                                                 instance = new DefaultConfig ();
73                                                 instance.Init ();
74                                         }
75                                         
76                                 }
77                         }
78
79                         return instance;
80                 }
81
82                 public object GetConfig (string sectionName)
83                 {
84                         if (config == null) 
85                                 return null;
86
87                         return config.GetConfig (sectionName);
88                 }
89
90                 public void Init ()
91                 {
92                         if (config == null){
93                                 lock (buildingData) {
94                                         if (config != null)
95                                                 return;
96
97                                         ConfigurationData data = new ConfigurationData ();
98                                         if (data.Load (GetMachineConfigPath ())) {
99                                                 ConfigurationData appData = new ConfigurationData (data);
100                                                 appData.Load (GetAppConfigPath ());
101                                                 config = appData;
102                                         } else {
103                                                 Console.WriteLine ("** Warning **: cannot find " + GetMachineConfigPath ());
104                                                 Console.WriteLine ("Trying to load app config file...");
105                                                 data.Load (GetAppConfigPath ());
106                                                 config = data;
107                                         }
108                                 }
109                         }
110                 }
111
112                 [MethodImplAttribute(MethodImplOptions.InternalCall)]
113                 extern private static string get_machine_config_path ();
114
115                 private static string GetMachineConfigPath ()
116                 {
117                         return get_machine_config_path ();
118                 }
119
120                 private static string GetAppConfigPath ()
121                 {
122                 /* FIXME: Uncomment and use this stuff when the current domain is initialized with
123                           the appropiate information.
124                         AppDomainSetup currentInfo = AppDomain.CurrentDomain.SetupInformation;
125
126                         string appBase = currentInfo.ApplicationBase;
127                         string configFile = currentInfo.ConfigurationFile;
128                         // FIXME: need to check out default domain configuration file name
129                         if (configFile == null || configFile.Length == 0)
130                                 return null;
131                 */
132
133                 // Remove when uncomment the previous comment
134                         string assemblyName = Assembly.GetEntryAssembly ().Location;
135                         string appBase = Path.GetDirectoryName (assemblyName);
136                         string configFile = Path.GetFileName (assemblyName) + ".exe.config";
137                 // End remove
138
139                         return Path.Combine (appBase, configFile);
140
141                 }
142         }
143
144         class ConfigurationData
145         {
146                 ConfigurationData parent;
147                 Hashtable factories;
148                 string fileName;
149                 object removedMark = new object ();
150                 object groupMark = new object ();
151
152                 public ConfigurationData () : this (null)
153                 {
154                 }
155
156                 public ConfigurationData (ConfigurationData parent)
157                 {
158                         this.parent = (parent == this) ? null : parent;
159                         factories = new Hashtable ();
160                 }
161
162                 public bool Load (string fileName)
163                 {
164                         if (fileName == null)
165                                 return false;
166
167                         this.fileName = fileName;
168                         XmlTextReader reader = null;
169
170                         try {
171                                 try {
172                                         reader = new XmlTextReader (fileName);
173                                 } catch {
174                                         return false;
175                                 }
176                                 
177                                 InitRead (reader);
178                                 ReadConfigFile (reader);
179                         } finally {
180                                 if (reader != null)
181                                         reader.Close();
182                         }
183
184                         return true;
185                 }
186
187                 object GetHandler (string sectionName)
188                 {
189                         object o = factories [sectionName];
190                         if (o == null || o == removedMark) {
191                                 if (parent != null)
192                                         return parent.GetHandler (sectionName);
193
194                                 return null;
195                         }
196
197                         if (o is IConfigurationSectionHandler)
198                                 return (IConfigurationSectionHandler) o;
199
200                         string [] typeInfo = ((string) o).Split (',');
201                         Type t;
202                         
203                         // Hack. Type.GetType should be enough
204                         if (typeInfo.Length > 1) {
205                                 Assembly ass = Assembly.Load (typeInfo [1].Trim ());
206                                 t = ass.GetType (typeInfo [0].Trim ());
207                         } else {
208                                 t = Type.GetType (typeInfo [0]);
209                         }
210                         
211                         if (t == null)
212                                 throw new ConfigurationException ("Cannot get Type for " + o);
213
214                         Type iconfig = typeof (IConfigurationSectionHandler);
215                         if (!iconfig.IsAssignableFrom (t))
216                                 throw new ConfigurationException (sectionName + " does not implement " + iconfig);
217                         
218                         o = Activator.CreateInstance (t, true);
219                         if (o == null)
220                                 throw new ConfigurationException ("Cannot get instance for " + t);
221
222                         factories [sectionName] = o;
223                         
224                         return o;
225                 }
226
227                 //TODO: Should use XPath when it works properly for this.
228                 XmlDocument GetDocumentForSection (string sectionName)
229                 {
230                         ConfigXmlDocument doc = new ConfigXmlDocument ();
231                         XmlTextReader reader = null;
232                         try {
233                                 reader = new XmlTextReader (fileName);
234                         } catch {
235                                 return doc;
236                         }
237
238                         InitRead (reader);
239                         string [] sectionPath = sectionName.Split ('/');
240                         int i = 0;
241                         if (!reader.EOF) {
242                                 if (reader.Name == "configSections")
243                                         reader.Skip ();
244                                 while (!reader.EOF) {
245                                         if (reader.NodeType == XmlNodeType.Element &&
246                                             reader.Name == sectionPath [i]) {
247                                                 if (++i == sectionPath.Length) {
248                                                         doc.LoadSingleElement (fileName, reader);
249                                                         break;
250                                                 }
251                                                 MoveToNextElement (reader);
252                                                 continue;
253                                         }
254                                         reader.Skip ();
255                                         if (reader.NodeType != XmlNodeType.Element)
256                                                 MoveToNextElement (reader);
257                                 }
258                         }
259
260                         reader.Close ();
261                         return doc;
262                 }
263                 
264                 public object GetConfig (string sectionName)
265                 {
266                         object handler = GetHandler (sectionName);
267                         if (handler == null)
268                                 return null;
269
270                         if (!(handler is IConfigurationSectionHandler))
271                                 return handler;
272
273                         object parentConfig = null;
274                         if (parent != null)
275                                 parentConfig = parent.GetConfig (sectionName);
276
277                         XmlDocument doc = GetDocumentForSection (sectionName);
278                         if (doc == null) {
279                                 if (parentConfig == null)
280                                         return null;
281
282                                 return parentConfig;
283                         }
284
285                         return ((IConfigurationSectionHandler) handler).Create (parentConfig, null, doc);
286                 }
287
288                 private object LookForFactory (string key)
289                 {
290                         object o = factories [key];
291                         if (o != null)
292                                 return o;
293
294                         if (parent != null)
295                                 return parent.LookForFactory (key);
296
297                         return null;
298                 }
299                 
300                 private void InitRead (XmlTextReader reader)
301                 {
302                         reader.MoveToContent ();
303                         if (reader.NodeType != XmlNodeType.Element || reader.Name != "configuration")
304                                 ThrowException ("Configuration file does not have a valid root element", reader);
305
306                         if (reader.HasAttributes)
307                                 ThrowException ("Unrecognized attribute in root element", reader);
308
309                         MoveToNextElement (reader);
310                 }
311
312                 private void MoveToNextElement (XmlTextReader reader)
313                 {
314                         while (reader.Read ()) {
315                                 XmlNodeType ntype = reader.NodeType;
316                                 if (ntype == XmlNodeType.Element)
317                                         return;
318
319                                 if (ntype != XmlNodeType.Whitespace &&
320                                     ntype != XmlNodeType.Comment &&
321                                     ntype != XmlNodeType.SignificantWhitespace &&
322                                     ntype != XmlNodeType.EndElement)
323                                         ThrowException ("Unrecognized element", reader);
324                         }
325                 }
326
327                 private void ReadSection (XmlTextReader reader, string sectionName)
328                 {
329                         string attName;
330                         string nameValue = null;
331                         string typeValue = null;
332
333                         while (reader.MoveToNextAttribute ()) {
334                                 attName = reader.Name;
335                                 if (attName == null)
336                                         continue;
337
338                                 if (attName == "allowLocation" || attName == "allowDefinition")
339                                         continue;
340
341                                 if (attName == "type")  {
342                                         if (typeValue != null)
343                                                 ThrowException ("Duplicated type attribute.", reader);
344                                         typeValue = reader.Value;
345                                         continue;
346                                 }
347                                 
348                                 if (attName == "name")  {
349                                         if (nameValue != null)
350                                                 ThrowException ("Duplicated name attribute.", reader);
351                                         nameValue = reader.Value;
352                                         continue;
353                                 }
354
355                                 ThrowException ("Unrecognized attribute.", reader);
356                         }
357
358                         if (nameValue == null || typeValue == null)
359                                 ThrowException ("Required attribute missing", reader);
360
361                         if (sectionName != null)
362                                 nameValue = sectionName + '/' + nameValue;
363
364                         reader.MoveToElement();
365                         object o = LookForFactory (nameValue);
366                         if (o != null && o != removedMark)
367                                 ThrowException ("Already have a factory for " + nameValue, reader);
368
369                         factories [nameValue] = typeValue;
370                         MoveToNextElement (reader);
371                 }
372
373                 private void ReadRemoveSection (XmlTextReader reader, string sectionName)
374                 {
375                         if (!reader.MoveToNextAttribute () || reader.Name != "name")
376                                 ThrowException ("Unrecognized attribute.", reader);
377
378                         string removeValue = reader.Value;
379                         if (removeValue == null || removeValue.Length == 0)
380                                 ThrowException ("Empty name to remove", reader);
381
382                         reader.MoveToElement ();
383
384                         if (sectionName != null)
385                                 removeValue = sectionName + '/' + removeValue;
386
387                         object o = LookForFactory (removeValue);
388                         if (o != null && o != removedMark)
389                                 ThrowException ("No factory for " + removeValue, reader);
390
391                         factories [removeValue] = removedMark;
392                         MoveToNextElement (reader);
393                 }
394
395                 private void ReadSectionGroup (XmlTextReader reader, string configSection)
396                 {
397                         if (!reader.MoveToNextAttribute ())
398                                 ThrowException ("sectionGroup must have a 'name' attribute.", reader);
399
400                         if (reader.Name != "name")
401                                 ThrowException ("Unrecognized attribute.", reader);
402
403                         if (reader.MoveToNextAttribute ())
404                                 ThrowException ("Unrecognized attribute.", reader);
405
406                         string value = reader.Value;
407                         if (configSection != null)
408                                 value = configSection + '/' + value;
409
410                         object o = LookForFactory (value);
411                         if (o != null && o != removedMark)
412                                 ThrowException ("Already have a factory for " + value, reader);
413
414                         factories [value] = groupMark;
415                         MoveToNextElement (reader);
416                         ReadSections (reader, value);
417                 }
418
419                 private void ReadSections (XmlTextReader reader, string configSection)
420                 {
421                         int depth = reader.Depth;
422                         while (reader.Depth == depth) {
423                                 string name = reader.Name;
424                                 if (name == "section") {
425                                         ReadSection (reader, configSection);
426                                         continue;
427                                 } 
428                                 
429                                 if (name == "remove") {
430                                         ReadRemoveSection (reader, configSection);
431                                         continue;
432                                 }
433
434                                 if (name == "clear") {
435                                         if (reader.HasAttributes)
436                                                 ThrowException ("Unrecognized attribute.", reader);
437
438                                         factories.Clear ();
439                                         MoveToNextElement (reader);
440                                         continue;
441                                 }
442
443                                 if (name == "sectionGroup") {
444                                         ReadSectionGroup (reader, configSection);
445                                         continue;
446                                 }
447                                 
448
449                                 ThrowException ("Unrecognized element: " + reader.Name, reader);
450                         }
451                 }
452
453                 private void ReadConfigFile (XmlTextReader reader)
454                 {
455                         int depth = reader.Depth;
456                         while (reader.Depth == depth) {
457                                 string name = reader.Name;
458                                 if (name == "configSections") {
459                                         if (reader.HasAttributes)
460                                                 ThrowException ("Unrecognized attribute in configSections element.", reader);
461                                         MoveToNextElement (reader);
462                                         ReadSections (reader, null);
463                                         return;
464                                 }
465
466                                 MoveToNextElement (reader);
467                         }
468                 }
469                                 
470                 private void ThrowException (string text, XmlTextReader reader)
471                 {
472                         throw new ConfigurationException (text, fileName, reader.LineNumber);
473                 }
474         }
475 }
476
477