2 // System.Configuration.Configuration.cs
5 // Duncan Mak (duncan@ximian.com)
6 // Lluis Sanchez Gual (lluis@novell.com)
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:
16 // The above copyright notice and this permission notice shall be
17 // included in all copies or substantial portions of the Software.
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.
27 // Copyright (C) 2004 Novell, Inc (http://www.novell.com)
31 using System.Collections;
32 using System.Collections.Specialized;
33 using System.Reflection;
36 using System.Configuration.Internal;
38 namespace System.Configuration {
40 public sealed class Configuration
43 Hashtable elementData = new Hashtable ();
45 ConfigurationSectionGroup rootSectionGroup;
46 ConfigurationLocationCollection locations;
47 SectionGroupInfo rootGroup;
52 string locationConfigPath;
54 internal Configuration (Configuration parent)
56 Init (parent.system, null, parent);
59 internal Configuration (InternalConfigurationSystem system, string locationSubPath)
64 system.InitForConfiguration (ref locationSubPath, out configPath, out locationConfigPath);
66 Configuration parent = null;
68 if (locationSubPath != null) {
69 parent = new Configuration (system, locationSubPath);
70 if (locationConfigPath != null)
71 parent = parent.FindLocationConfiguration (locationConfigPath, parent);
74 Init (system, configPath, parent);
77 internal Configuration FindLocationConfiguration (string relativePath, Configuration defaultConfiguration)
79 ConfigurationLocation loc = Locations.Find (relativePath);
81 Configuration parentConfig = defaultConfiguration;
83 if (LocationConfigPath != null) {
84 Configuration parentFile = GetParentWithFile ();
85 if (parentFile != null) {
86 string parentRelativePath = system.Host.GetConfigPathFromLocationSubPath (LocationConfigPath, relativePath);
87 parentConfig = parentFile.FindLocationConfiguration (parentRelativePath, defaultConfiguration);
94 loc.SetParentConfiguration (parentConfig);
95 return loc.OpenConfiguration ();
98 internal void Init (IConfigSystem system, string configPath, Configuration parent)
100 this.system = system;
101 streamName = system.Host.GetStreamName (configPath);
102 this.parent = parent;
104 rootGroup = parent.rootGroup;
106 rootGroup = new SectionGroupInfo ();
107 rootGroup.StreamName = streamName;
110 if (configPath != null)
114 internal Configuration Parent {
115 get { return parent; }
116 set { parent = value; }
119 internal Configuration GetParentWithFile ()
121 Configuration parentFile = Parent;
122 while (parentFile != null && !parentFile.HasFile)
123 parentFile = parentFile.Parent;
127 internal string FileName {
128 get { return streamName; }
131 internal IInternalConfigHost ConfigHost {
132 get { return system.Host; }
135 internal string LocationConfigPath {
136 get { return locationConfigPath; }
139 internal string ConfigPath {
140 get { return configPath; }
143 public AppSettingsSection AppSettings {
144 get { return Sections ["appSettings"] as AppSettingsSection; }
147 public ConnectionStringsSection ConnectionStrings {
148 get { return (ConnectionStringsSection) GetSection ("connectionStrings"); }
151 public string FilePath {
152 get { return streamName; }
155 public bool HasFile {
161 public ConfigurationLocationCollection Locations {
163 if (locations == null) locations = new ConfigurationLocationCollection ();
168 public ConfigurationSectionGroup RootSectionGroup {
170 if (rootSectionGroup == null) {
171 rootSectionGroup = new ConfigurationSectionGroup ();
172 rootSectionGroup.Initialize (this, rootGroup);
174 return rootSectionGroup;
178 public ConfigurationSectionGroupCollection SectionGroups {
179 get { return RootSectionGroup.SectionGroups; }
182 public ConfigurationSectionCollection Sections {
183 get { return RootSectionGroup.Sections; }
186 public ConfigurationSection GetSection (string path)
188 string[] parts = path.Split ('/');
189 if (parts.Length == 1)
190 return Sections [parts[0]];
192 ConfigurationSectionGroup group = SectionGroups [parts[0]];
193 for (int n=1; group != null && n<parts.Length-1; n++)
194 group = group.SectionGroups [parts [n]];
197 return group.Sections [parts [parts.Length - 1]];
202 public ConfigurationSectionGroup GetSectionGroup (string path)
204 string[] parts = path.Split ('/');
205 ConfigurationSectionGroup group = SectionGroups [parts[0]];
206 for (int n=1; group != null && n<parts.Length; n++)
207 group = group.SectionGroups [parts [n]];
211 internal ConfigurationSection GetSectionInstance (SectionInfo config, bool createDefaultInstance)
213 object data = elementData [config];
214 ConfigurationSection sec = data as ConfigurationSection;
215 if (sec != null || !createDefaultInstance) return sec;
217 object secObj = config.CreateInstance () as ConfigurationSection;
218 if (!(secObj is ConfigurationSection))
219 sec = new RuntimeOnlySection ();
221 sec = (ConfigurationSection) secObj;
224 ConfigurationSection parentSection = parent != null ? parent.GetSectionInstance (config, true) : null;
225 sec.RawXml = data as string;
226 sec.Reset (parentSection);
229 XmlTextReader r = new XmlTextReader (new StringReader (data as string));
230 sec.DeserializeSection (r);
234 elementData [config] = sec;
238 internal ConfigurationSectionGroup GetSectionGroupInstance (SectionGroupInfo group)
240 ConfigurationSectionGroup gr = group.CreateInstance () as ConfigurationSectionGroup;
241 if (gr != null) gr.Initialize (this, group);
245 internal void SetConfigurationSection (SectionInfo config, ConfigurationSection sec)
247 elementData [config] = sec;
250 internal void SetSectionXml (SectionInfo config, string data)
252 elementData [config] = data;
255 internal string GetSectionXml (SectionInfo config)
257 return elementData [config] as string;
260 internal void CreateSection (SectionGroupInfo group, string name, ConfigurationSection sec)
262 if (group.HasChild (name)) throw new ConfigurationException ("Cannot add a ConfigurationSection. A section or section group already exists with the name '" + name + "'");
263 if (sec.SectionInformation.Type == null) sec.SectionInformation.Type = system.Host.GetConfigTypeName (sec.GetType ());
264 sec.SectionInformation.SetName (name);
266 SectionInfo section = new SectionInfo (name, sec.SectionInformation.Type, sec.SectionInformation.AllowLocation, sec.SectionInformation.AllowDefinition);
267 section.StreamName = streamName;
268 section.ConfigHost = system.Host;
269 group.AddChild (section);
270 elementData [section] = sec;
273 internal void CreateSectionGroup (SectionGroupInfo parentGroup, string name, ConfigurationSectionGroup sec)
275 if (parentGroup.HasChild (name)) throw new ConfigurationException ("Cannot add a ConfigurationSectionGroup. A section or section group already exists with the name '" + name + "'");
276 if (sec.Type == null) sec.Type = system.Host.GetConfigTypeName (sec.GetType ());
279 SectionGroupInfo section = new SectionGroupInfo (name, sec.Type);
280 section.StreamName = streamName;
281 section.ConfigHost = system.Host;
282 parentGroup.AddChild (section);
283 elementData [section] = sec;
286 internal void RemoveConfigInfo (ConfigInfo config)
288 elementData.Remove (config);
293 Save (ConfigurationSaveMode.Modified, false);
296 public void Save (ConfigurationSaveMode mode)
301 public void Save (ConfigurationSaveMode mode, bool forceUpdateAll)
304 Stream stream = system.Host.OpenStreamForWrite (streamName, null, ref ctx);
306 Save (stream, mode, forceUpdateAll);
307 system.Host.WriteCompleted (streamName, true, ctx);
308 } catch (Exception ex) {
309 system.Host.WriteCompleted (streamName, false, ctx);
316 public void SaveAs (string filename)
318 SaveAs (filename, ConfigurationSaveMode.Modified, false);
321 public void SaveAs (string filename, ConfigurationSaveMode mode)
323 SaveAs (filename, mode, false);
326 [MonoTODO ("Detect if file has changed")]
327 public void SaveAs (string filename, ConfigurationSaveMode mode, bool forceUpdateAll)
329 Save (new FileStream (filename, FileMode.Open, FileAccess.Write), mode, forceUpdateAll);
332 void Save (Stream stream, ConfigurationSaveMode mode, bool forceUpdateAll)
334 XmlTextWriter tw = new XmlTextWriter (new StreamWriter (stream));
335 tw.Formatting = Formatting.Indented;
337 tw.WriteStartElement ("configuration");
338 if (rootGroup.HasConfigContent (this)) {
339 rootGroup.WriteConfig (this, tw, mode);
341 rootGroup.WriteRootData (tw, this, mode);
342 tw.WriteEndElement ();
351 if (streamName == null)
354 Stream stream = system.Host.OpenStreamForRead (streamName);
355 XmlTextReader reader = null;
358 reader = new XmlTextReader (stream);
359 ReadConfigFile (reader, streamName);
360 /* } catch (ConfigurationException) {
362 } catch (Exception e) {
363 throw new ConfigurationException ("Error reading " + fileName, e);
372 internal void ReadConfigFile (XmlTextReader reader, string fileName)
374 reader.MoveToContent ();
375 if (reader.NodeType != XmlNodeType.Element || reader.Name != "configuration")
376 ThrowException ("Configuration file does not have a valid root element", reader);
378 if (reader.HasAttributes)
379 ThrowException ("Unrecognized attribute in root element", reader);
381 if (reader.IsEmptyElement) {
386 reader.ReadStartElement ();
387 reader.MoveToContent ();
389 if (reader.LocalName == "configSections") {
390 if (reader.HasAttributes)
391 ThrowException ("Unrecognized attribute in <configSections>.", reader);
393 rootGroup.ReadConfig (this, fileName, reader);
396 rootGroup.ReadRootData (reader, this);
399 internal void ReadData (XmlTextReader reader)
401 rootGroup.ReadRootData (reader, this);
405 private void ThrowException (string text, XmlTextReader reader)
407 throw new ConfigurationException (text, streamName, reader.LineNumber);