2002-12-15 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                         ConfigXmlDocument doc = new ConfigXmlDocument ();
209                         XmlTextReader reader = null;
210                         try {
211                                 reader = new XmlTextReader (fileName);
212                         } catch {
213                                 return doc;
214                         }
215
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                                 while (!reader.EOF) {
223                                         if (reader.NodeType == XmlNodeType.Element &&
224                                             reader.Name == sectionPath [i]) {
225                                                 if (++i == sectionPath.Length) {
226                                                         doc.LoadSingleElement (fileName, reader);
227                                                         break;
228                                                 }
229                                                 MoveToNextElement (reader);
230                                                 continue;
231                                         }
232                                         reader.Skip ();
233                                         if (reader.NodeType != XmlNodeType.Element)
234                                                 MoveToNextElement (reader);
235                                 }
236                         }
237
238                         reader.Close ();
239                         return doc;
240                 }
241                 
242                 public object GetConfig (string sectionName)
243                 {
244                         object handler = GetHandler (sectionName);
245                         if (handler == null)
246                                 return null;
247
248                         if (!(handler is IConfigurationSectionHandler))
249                                 return handler;
250
251                         object parentConfig = null;
252                         if (parent != null)
253                                 parentConfig = parent.GetConfig (sectionName);
254
255                         XmlDocument doc = GetDocumentForSection (sectionName);
256                         if (doc == null) {
257                                 if (parentConfig == null)
258                                         return null;
259
260                                 return parentConfig;
261                         }
262
263                         return ((IConfigurationSectionHandler) handler).Create (parentConfig, null, doc);
264                 }
265
266                 private object LookForFactory (string key)
267                 {
268                         object o = factories [key];
269                         if (o != null)
270                                 return o;
271
272                         if (parent != null)
273                                 return parent.LookForFactory (key);
274
275                         return null;
276                 }
277                 
278                 private void InitRead (XmlTextReader reader)
279                 {
280                         reader.MoveToContent ();
281                         if (reader.NodeType != XmlNodeType.Element || reader.Name != "configuration")
282                                 ThrowException ("Configuration file does not have a valid root element", reader);
283
284                         if (reader.HasAttributes)
285                                 ThrowException ("Unrecognized attribute in root element", reader);
286
287                         MoveToNextElement (reader);
288                 }
289
290                 private void MoveToNextElement (XmlTextReader reader)
291                 {
292                         while (reader.Read ()) {
293                                 XmlNodeType ntype = reader.NodeType;
294                                 if (ntype == XmlNodeType.Element)
295                                         return;
296
297                                 if (ntype != XmlNodeType.Whitespace &&
298                                     ntype != XmlNodeType.Comment &&
299                                     ntype != XmlNodeType.SignificantWhitespace &&
300                                     ntype != XmlNodeType.EndElement)
301                                         ThrowException ("Unrecognized element", reader);
302                         }
303                 }
304
305                 private void ReadSection (XmlTextReader reader, string sectionName)
306                 {
307                         string attName;
308                         string nameValue = null;
309                         string typeValue = null;
310
311                         while (reader.MoveToNextAttribute ()) {
312                                 attName = reader.Name;
313                                 if (attName == null)
314                                         continue;
315
316                                 if (attName == "allowLocation" || attName == "allowDefinition")
317                                         continue;
318
319                                 if (attName == "type")  {
320                                         if (typeValue != null)
321                                                 ThrowException ("Duplicated type attribute.", reader);
322                                         typeValue = reader.Value;
323                                         continue;
324                                 }
325                                 
326                                 if (attName == "name")  {
327                                         if (nameValue != null)
328                                                 ThrowException ("Duplicated name attribute.", reader);
329                                         nameValue = reader.Value;
330                                         continue;
331                                 }
332
333                                 ThrowException ("Unrecognized attribute.", reader);
334                         }
335
336                         if (nameValue == null || typeValue == null)
337                                 ThrowException ("Required attribute missing", reader);
338
339                         if (sectionName != null)
340                                 nameValue = sectionName + '/' + nameValue;
341
342                         reader.MoveToElement();
343                         object o = LookForFactory (nameValue);
344                         if (o != null && o != removedMark)
345                                 ThrowException ("Already have a factory for " + nameValue, reader);
346
347                         factories [nameValue] = typeValue;
348                         MoveToNextElement (reader);
349                 }
350
351                 private void ReadRemoveSection (XmlTextReader reader, string sectionName)
352                 {
353                         if (!reader.MoveToNextAttribute () || reader.Name != "name")
354                                 ThrowException ("Unrecognized attribute.", reader);
355
356                         string removeValue = reader.Value;
357                         if (removeValue == null || removeValue.Length == 0)
358                                 ThrowException ("Empty name to remove", reader);
359
360                         reader.MoveToElement ();
361
362                         if (sectionName != null)
363                                 removeValue = sectionName + '/' + removeValue;
364
365                         object o = LookForFactory (removeValue);
366                         if (o != null && o == removedMark)
367                                 ThrowException ("No factory for " + removeValue, reader);
368
369                         factories [removeValue] = removedMark;
370                         MoveToNextElement (reader);
371                 }
372
373                 private void ReadSectionGroup (XmlTextReader reader, string configSection)
374                 {
375                         if (!reader.MoveToNextAttribute ())
376                                 ThrowException ("sectionGroup must have a 'name' attribute.", reader);
377
378                         if (reader.Name != "name")
379                                 ThrowException ("Unrecognized attribute.", reader);
380
381                         if (reader.MoveToNextAttribute ())
382                                 ThrowException ("Unrecognized attribute.", reader);
383
384                         string value = reader.Value;
385                         if (configSection != null)
386                                 value = configSection + '/' + value;
387
388                         object o = LookForFactory (value);
389                         if (o != null && o != removedMark)
390                                 ThrowException ("Already have a factory for " + value, reader);
391
392                         factories [value] = groupMark;
393                         MoveToNextElement (reader);
394                         ReadSections (reader, value);
395                 }
396
397                 private void ReadSections (XmlTextReader reader, string configSection)
398                 {
399                         int depth = reader.Depth;
400                         while (reader.Depth == depth) {
401                                 string name = reader.Name;
402                                 if (name == "section") {
403                                         ReadSection (reader, configSection);
404                                         continue;
405                                 } 
406                                 
407                                 if (name == "remove") {
408                                         ReadRemoveSection (reader, configSection);
409                                         continue;
410                                 }
411
412                                 if (name == "clear") {
413                                         if (reader.HasAttributes)
414                                                 ThrowException ("Unrecognized attribute.", reader);
415
416                                         factories.Clear ();
417                                         MoveToNextElement (reader);
418                                         continue;
419                                 }
420
421                                 if (name == "sectionGroup") {
422                                         ReadSectionGroup (reader, configSection);
423                                         continue;
424                                 }
425                                 
426
427                                 ThrowException ("Unrecognized element: " + reader.Name, reader);
428                         }
429                 }
430
431                 private void ReadConfigFile (XmlTextReader reader)
432                 {
433                         int depth = reader.Depth;
434                         while (reader.Depth == depth) {
435                                 string name = reader.Name;
436                                 if (name == "configSections") {
437                                         if (reader.HasAttributes)
438                                                 ThrowException ("Unrecognized attribute in configSections element.", reader);
439                                         MoveToNextElement (reader);
440                                         ReadSections (reader, null);
441                                         return;
442                                 }
443
444                                 MoveToNextElement (reader);
445                         }
446                 }
447                                 
448                 private void ThrowException (string text, XmlTextReader reader)
449                 {
450                         throw new ConfigurationException (text, fileName, reader.LineNumber);
451                 }
452         }
453 }
454
455