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