New test.
[mono.git] / mcs / class / System.Configuration / System.Configuration / Configuration.cs
1 //
2 // System.Configuration.Configuration.cs
3 //
4 // Authors:
5 //      Duncan Mak (duncan@ximian.com)
6 //      Lluis Sanchez Gual (lluis@novell.com)
7 //
8 // Permission is hereby granted, free of charge, to any person obtaining
9 // a copy of this software and associated documentation files (the
10 // "Software"), to deal in the Software without restriction, including
11 // without limitation the rights to use, copy, modify, merge, publish,
12 // distribute, sublicense, and/or sell copies of the Software, and to
13 // permit persons to whom the Software is furnished to do so, subject to
14 // the following conditions:
15 // 
16 // The above copyright notice and this permission notice shall be
17 // included in all copies or substantial portions of the Software.
18 // 
19 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
20 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
21 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
22 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
23 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
24 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
25 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
26 //
27 // Copyright (C) 2004 Novell, Inc (http://www.novell.com)
28 //
29 #if NET_2_0
30 using System;
31 using System.Collections;
32 using System.Collections.Specialized;
33 using System.Reflection;
34 using System.Xml;
35 using System.IO;
36 using System.Configuration.Internal;
37
38 namespace System.Configuration {
39
40         public sealed class Configuration
41         {
42                 Configuration parent;
43                 Hashtable elementData = new Hashtable ();
44                 string streamName;
45                 ConfigurationSectionGroup rootSectionGroup;
46                 ConfigurationLocationCollection locations;
47                 SectionGroupInfo rootGroup;
48                 IConfigSystem system;
49                 bool hasFile;
50                 string rootNamespace;
51                 
52                 string configPath;
53                 string locationConfigPath;
54                         
55                 internal Configuration (Configuration parent)
56                 {
57                         this.parent = parent;
58                         this.system = parent.system;
59                         this.rootGroup = parent.rootGroup;
60                 }
61                 
62                 internal Configuration (InternalConfigurationSystem system, string locationSubPath)
63                 {
64                         hasFile = true;
65                         this.system = system;
66                         
67                         system.InitForConfiguration (ref locationSubPath, out configPath, out locationConfigPath);
68                         
69                         Configuration parent = null;
70                         
71                         if (locationSubPath != null) {
72                                 parent = new Configuration (system, locationSubPath);
73                                 if (locationConfigPath != null)
74                                         parent = parent.FindLocationConfiguration (locationConfigPath, parent);
75                         }
76                         
77                         Init (system, configPath, parent);
78                 }
79                 
80                 internal Configuration FindLocationConfiguration (string relativePath, Configuration defaultConfiguration)
81                 {
82                         ConfigurationLocation loc = Locations.Find (relativePath);
83                         
84                         Configuration parentConfig = defaultConfiguration;
85                         
86                         if (LocationConfigPath != null) {
87                                 Configuration parentFile = GetParentWithFile ();
88                                 if (parentFile != null) {
89                                         string parentRelativePath = system.Host.GetConfigPathFromLocationSubPath (LocationConfigPath, relativePath);
90                                         parentConfig = parentFile.FindLocationConfiguration (parentRelativePath, defaultConfiguration);
91                                 }
92                         }
93
94                         if (loc == null)
95                                 return parentConfig;
96                         
97                         loc.SetParentConfiguration (parentConfig);
98                         return loc.OpenConfiguration ();
99                 }
100                 
101                 internal void Init (IConfigSystem system, string configPath, Configuration parent)
102                 {
103                         this.system = system;
104                         this.configPath = configPath;
105                         streamName = system.Host.GetStreamName (configPath);
106                         this.parent = parent;
107                         if (parent != null)
108                                 rootGroup = parent.rootGroup;
109                         else {
110                                 rootGroup = new SectionGroupInfo ();
111                                 rootGroup.StreamName = streamName;
112                         }
113                         
114                         if (streamName != null)
115                                 Load ();
116                 }
117                 
118                 internal Configuration Parent {
119                         get { return parent; }
120                         set { parent = value; }
121                 }
122                 
123                 internal Configuration GetParentWithFile ()
124                 {
125                         Configuration parentFile = Parent;
126                         while (parentFile != null && !parentFile.HasFile)
127                                 parentFile = parentFile.Parent;
128                         return parentFile;
129                 }
130                 
131                 internal string FileName {
132                         get { return streamName; }
133                 }
134
135                 internal IInternalConfigHost ConfigHost {
136                         get { return system.Host; }
137                 }
138                 
139                 internal string LocationConfigPath {
140                         get { return locationConfigPath; }
141                 }
142
143                 internal string ConfigPath {
144                         get { return configPath; }
145                 }
146
147                 public AppSettingsSection AppSettings {
148                         get { return (AppSettingsSection) GetSection ("appSettings"); }
149                 }
150
151                 public ConnectionStringsSection ConnectionStrings {
152                         get { return (ConnectionStringsSection) GetSection ("connectionStrings"); }
153                 }
154
155                 public string FilePath {
156                         get { return streamName; }
157                 }
158
159                 public bool HasFile {
160                         get {
161                                 return hasFile;
162                         }
163                 }
164
165                 [MonoTODO ("HostingContext")]
166                 ContextInformation evaluationContext;
167                 public ContextInformation EvaluationContext {
168                         get {
169                                 if (evaluationContext == null)
170                                         evaluationContext = new ContextInformation (this, null /* XXX */);
171                                 return evaluationContext;
172                         }
173                 }
174                 
175                 public ConfigurationLocationCollection Locations {
176                         get {
177                                 if (locations == null) locations = new ConfigurationLocationCollection ();
178                                 return locations;
179                         }
180                 }
181
182                 public bool NamespaceDeclared {
183                         get { return rootNamespace != null; }
184                         set { rootNamespace = value ? "http://schemas.microsoft.com/.NetConfiguration/v2.0" : null; }
185                 }
186
187                 public ConfigurationSectionGroup RootSectionGroup {
188                         get {
189                                 if (rootSectionGroup == null) {
190                                         rootSectionGroup = new ConfigurationSectionGroup ();
191                                         rootSectionGroup.Initialize (this, rootGroup);
192                                 }
193                                 return rootSectionGroup;
194                         }                        
195                 }
196
197                 public ConfigurationSectionGroupCollection SectionGroups {
198                         get { return RootSectionGroup.SectionGroups; }                        
199                 }
200
201                 public ConfigurationSectionCollection Sections {
202                         get { return RootSectionGroup.Sections; }
203                 }
204                 
205                 public ConfigurationSection GetSection (string path)
206                 {
207                         string[] parts = path.Split ('/');
208                         if (parts.Length == 1)
209                                 return Sections [parts[0]];
210
211                         ConfigurationSectionGroup group = SectionGroups [parts[0]];
212                         for (int n=1; group != null && n<parts.Length-1; n++)
213                                 group = group.SectionGroups [parts [n]];
214
215                         if (group != null)
216                                 return group.Sections [parts [parts.Length - 1]];
217                         else
218                                 return null;
219                 }
220                 
221                 public ConfigurationSectionGroup GetSectionGroup (string path)
222                 {
223                         string[] parts = path.Split ('/');
224                         ConfigurationSectionGroup group = SectionGroups [parts[0]];
225                         for (int n=1; group != null && n<parts.Length; n++)
226                                 group = group.SectionGroups [parts [n]];
227                         return group;
228                 }
229                 
230                 internal ConfigurationSection GetSectionInstance (SectionInfo config, bool createDefaultInstance)
231                 {
232                         object data = elementData [config];
233                         ConfigurationSection sec = data as ConfigurationSection;
234                         if (sec != null || !createDefaultInstance) return sec;
235                         
236                         object secObj = config.CreateInstance ();
237                         sec = secObj as ConfigurationSection;
238                         if (sec == null) {
239                                 DefaultSection ds = new DefaultSection ();
240                                 ds.SectionHandler = secObj as IConfigurationSectionHandler;
241                                 sec = ds;
242                         }
243
244                         ConfigurationSection parentSection = parent != null ? parent.GetSectionInstance (config, true) : null;
245
246                         string xml = data as string;
247                         if (xml == null && parentSection != null)
248                                 xml = parentSection.RawXml;
249                         sec.RawXml = xml;
250                         sec.Reset (parentSection);
251
252                         if (xml != null && xml == data) {
253                                 XmlTextReader r = new XmlTextReader (new StringReader (xml));
254                                 sec.DeserializeSection (r);
255                                 r.Close ();
256                         }
257                         
258                         elementData [config] = sec;
259                         return sec;
260                 }
261                 
262                 internal ConfigurationSectionGroup GetSectionGroupInstance (SectionGroupInfo group)
263                 {
264                         ConfigurationSectionGroup gr = group.CreateInstance () as ConfigurationSectionGroup;
265                         if (gr != null) gr.Initialize (this, group);
266                         return gr;
267                 }
268                 
269                 internal void SetConfigurationSection (SectionInfo config, ConfigurationSection sec)
270                 {
271                         elementData [config] = sec;
272                 }
273                 
274                 internal void SetSectionXml (SectionInfo config, string data)
275                 {
276                         elementData [config] = data;
277                 }
278                 
279                 internal string GetSectionXml (SectionInfo config)
280                 {
281                         return elementData [config] as string;
282                 }
283                 
284                 internal void CreateSection (SectionGroupInfo group, string name, ConfigurationSection sec)
285                 {
286                         if (group.HasChild (name))
287                                 throw new ConfigurationException ("Cannot add a ConfigurationSection. A section or section group already exists with the name '" + name + "'");
288                                 
289                         if (!HasFile && !sec.SectionInformation.AllowLocation)
290                                 throw new ConfigurationErrorsException ("The configuration section <" + name + "> cannot be defined inside a <location> element."); 
291
292                         if (!system.Host.IsDefinitionAllowed (configPath, sec.SectionInformation.AllowDefinition, sec.SectionInformation.AllowExeDefinition)) {
293                                 object ctx = sec.SectionInformation.AllowExeDefinition != ConfigurationAllowExeDefinition.MachineToApplication ? (object) sec.SectionInformation.AllowExeDefinition : (object) sec.SectionInformation.AllowDefinition;
294                                 throw new ConfigurationErrorsException ("The section <" + name + "> can't be defined in this configuration file (the allowed definition context is '" + ctx + "').");
295                         }
296                                                 
297                         if (sec.SectionInformation.Type == null)
298                                 sec.SectionInformation.Type = system.Host.GetConfigTypeName (sec.GetType ());
299                         
300                         SectionInfo section = new SectionInfo (name, sec.SectionInformation);
301                         section.StreamName = streamName;
302                         section.ConfigHost = system.Host;
303                         group.AddChild (section);
304                         elementData [section] = sec;
305                 }
306                 
307                 internal void CreateSectionGroup (SectionGroupInfo parentGroup, string name, ConfigurationSectionGroup sec)
308                 {
309                         if (parentGroup.HasChild (name)) throw new ConfigurationException ("Cannot add a ConfigurationSectionGroup. A section or section group already exists with the name '" + name + "'");
310                         if (sec.Type == null) sec.Type = system.Host.GetConfigTypeName (sec.GetType ());
311                         sec.SetName (name);
312
313                         SectionGroupInfo section = new SectionGroupInfo (name, sec.Type);
314                         section.StreamName = streamName;
315                         section.ConfigHost = system.Host;
316                         parentGroup.AddChild (section);
317                         elementData [section] = sec;
318                 }
319                 
320                 internal void RemoveConfigInfo (ConfigInfo config)
321                 {
322                         elementData.Remove (config);
323                 }
324                 
325                 public void Save ()
326                 {
327                         Save (ConfigurationSaveMode.Modified, false);
328                 }
329                 
330                 public void Save (ConfigurationSaveMode mode)
331                 {
332                         Save (mode, false);
333                 }
334                 
335                 public void Save (ConfigurationSaveMode mode, bool forceUpdateAll)
336                 {
337                         object ctx = null;
338                         Stream stream = system.Host.OpenStreamForWrite (streamName, null, ref ctx);
339                         try {
340                                 Save (stream, mode, forceUpdateAll);
341                                 system.Host.WriteCompleted (streamName, true, ctx);
342                         } catch (Exception ex) {
343                                 system.Host.WriteCompleted (streamName, false, ctx);
344                                 throw;
345                         } finally {
346                                 stream.Close ();
347                         }
348                 }
349                 
350                 public void SaveAs (string filename)
351                 {
352                         SaveAs (filename, ConfigurationSaveMode.Modified, false);
353                 }
354                 
355                 public void SaveAs (string filename, ConfigurationSaveMode mode)
356                 {
357                         SaveAs (filename, mode, false);
358                 }
359                 
360                 [MonoTODO ("Detect if file has changed")]
361                 public void SaveAs (string filename, ConfigurationSaveMode mode, bool forceUpdateAll)
362                 {
363                         Save (new FileStream (filename, FileMode.OpenOrCreate, FileAccess.Write), mode, forceUpdateAll);
364                 }
365
366                 void Save (Stream stream, ConfigurationSaveMode mode, bool forceUpdateAll)
367                 {
368                         XmlTextWriter tw = new XmlTextWriter (new StreamWriter (stream));
369                         tw.Formatting = Formatting.Indented;
370                         try {
371                                 tw.WriteStartDocument ();
372                                 if (rootNamespace != null)
373                                         tw.WriteStartElement ("configuration", rootNamespace);
374                                 else
375                                         tw.WriteStartElement ("configuration");
376                                 if (rootGroup.HasConfigContent (this)) {
377                                         rootGroup.WriteConfig (this, tw, mode);
378                                 }
379                                 
380                                 foreach (ConfigurationLocation loc in Locations) {
381                                         if (loc.OpenedConfiguration == null) {
382                                                 tw.WriteRaw ("\n");
383                                                 tw.WriteRaw (loc.XmlContent);
384                                         }
385                                         else {
386                                                 tw.WriteStartElement ("location");
387                                                 tw.WriteAttributeString ("path", loc.Path); 
388                                                 if (!loc.AllowOverride)
389                                                         tw.WriteAttributeString ("allowOverride", "false");
390                                                 loc.OpenedConfiguration.SaveData (tw, mode, forceUpdateAll);
391                                                 tw.WriteEndElement ();
392                                         }
393                                 }
394                                 
395                                 SaveData (tw, mode, forceUpdateAll);
396                                 tw.WriteEndElement ();
397                         }
398                         finally {
399                                 tw.Close ();
400                         }
401                 }
402                 
403                 void SaveData (XmlTextWriter tw, ConfigurationSaveMode mode, bool forceUpdateAll)
404                 {
405                         rootGroup.WriteRootData (tw, this, mode);
406                 }
407                 
408                 bool Load ()
409                 {
410                         if (streamName == null || streamName == "")
411                                 return true;
412
413                         XmlTextReader reader = null;
414                         Stream stream = null;
415                         
416                         try {
417                                 stream = system.Host.OpenStreamForRead (streamName);
418                         } catch (Exception e) {
419                                 return false;
420                         }
421
422                         try {
423                                 reader = new XmlTextReader (stream);
424                                 ReadConfigFile (reader, streamName);
425                         } finally {
426                                 if (reader != null)
427                                         reader.Close();
428                         }
429                         return true;
430                 }
431
432
433                 internal void ReadConfigFile (XmlTextReader reader, string fileName)
434                 {
435                         reader.MoveToContent ();
436
437                         if (reader.NodeType != XmlNodeType.Element || reader.Name != "configuration")
438                                 ThrowException ("Configuration file does not have a valid root element", reader);
439
440                         if (reader.HasAttributes) {
441                                 while (reader.MoveToNextAttribute ()) {
442                                         if (reader.LocalName == "xmlns") {
443                                                 rootNamespace = reader.Value;
444                                                 continue;
445                                         }
446                                         ThrowException (String.Format ("Unrecognized attribute '{0}' in root element", reader.LocalName), reader);
447                                 }
448                         }
449
450                         reader.MoveToElement ();
451
452                         if (reader.IsEmptyElement) {
453                                 reader.Skip ();
454                                 return;
455                         }
456                         
457                         reader.ReadStartElement ();
458                         reader.MoveToContent ();
459
460                         if (reader.LocalName == "configSections") {
461                                 if (reader.HasAttributes)
462                                         ThrowException ("Unrecognized attribute in <configSections>.", reader);
463                                 
464                                 rootGroup.ReadConfig (this, fileName, reader);
465                         }
466                         
467                         rootGroup.ReadRootData (reader, this, true);
468                 }
469                 
470                 internal void ReadData (XmlTextReader reader, bool allowOverride)
471                 {
472                         rootGroup.ReadData (this, reader, allowOverride);
473                 }
474                 
475
476                 private void ThrowException (string text, XmlTextReader reader)
477                 {
478                         throw new ConfigurationException (text, streamName, reader.LineNumber);
479                 }
480         }
481 }
482
483 #endif