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;
53 string locationConfigPath;
55 internal Configuration (Configuration parent)
58 this.system = parent.system;
59 this.rootGroup = parent.rootGroup;
62 internal Configuration (InternalConfigurationSystem system, string locationSubPath)
67 system.InitForConfiguration (ref locationSubPath, out configPath, out locationConfigPath);
69 Configuration parent = null;
71 if (locationSubPath != null) {
72 parent = new Configuration (system, locationSubPath);
73 if (locationConfigPath != null)
74 parent = parent.FindLocationConfiguration (locationConfigPath, parent);
77 Init (system, configPath, parent);
80 internal Configuration FindLocationConfiguration (string relativePath, Configuration defaultConfiguration)
82 ConfigurationLocation loc = Locations.Find (relativePath);
84 Configuration parentConfig = defaultConfiguration;
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);
97 loc.SetParentConfiguration (parentConfig);
98 return loc.OpenConfiguration ();
101 internal void Init (IConfigSystem system, string configPath, Configuration parent)
103 this.system = system;
104 this.configPath = configPath;
105 streamName = system.Host.GetStreamName (configPath);
106 this.parent = parent;
108 rootGroup = parent.rootGroup;
110 rootGroup = new SectionGroupInfo ();
111 rootGroup.StreamName = streamName;
114 if (streamName != null)
118 internal Configuration Parent {
119 get { return parent; }
120 set { parent = value; }
123 internal Configuration GetParentWithFile ()
125 Configuration parentFile = Parent;
126 while (parentFile != null && !parentFile.HasFile)
127 parentFile = parentFile.Parent;
131 internal string FileName {
132 get { return streamName; }
135 internal IInternalConfigHost ConfigHost {
136 get { return system.Host; }
139 internal string LocationConfigPath {
140 get { return locationConfigPath; }
143 internal string ConfigPath {
144 get { return configPath; }
147 public AppSettingsSection AppSettings {
148 get { return (AppSettingsSection) GetSection ("appSettings"); }
151 public ConnectionStringsSection ConnectionStrings {
152 get { return (ConnectionStringsSection) GetSection ("connectionStrings"); }
155 public string FilePath {
156 get { return streamName; }
159 public bool HasFile {
165 [MonoTODO ("HostingContext")]
166 ContextInformation evaluationContext;
167 public ContextInformation EvaluationContext {
169 if (evaluationContext == null)
170 evaluationContext = new ContextInformation (this, null /* XXX */);
171 return evaluationContext;
175 public ConfigurationLocationCollection Locations {
177 if (locations == null) locations = new ConfigurationLocationCollection ();
182 public bool NamespaceDeclared {
183 get { return rootNamespace != null; }
184 set { rootNamespace = value ? "http://schemas.microsoft.com/.NetConfiguration/v2.0" : null; }
187 public ConfigurationSectionGroup RootSectionGroup {
189 if (rootSectionGroup == null) {
190 rootSectionGroup = new ConfigurationSectionGroup ();
191 rootSectionGroup.Initialize (this, rootGroup);
193 return rootSectionGroup;
197 public ConfigurationSectionGroupCollection SectionGroups {
198 get { return RootSectionGroup.SectionGroups; }
201 public ConfigurationSectionCollection Sections {
202 get { return RootSectionGroup.Sections; }
205 public ConfigurationSection GetSection (string path)
207 string[] parts = path.Split ('/');
208 if (parts.Length == 1)
209 return Sections [parts[0]];
211 ConfigurationSectionGroup group = SectionGroups [parts[0]];
212 for (int n=1; group != null && n<parts.Length-1; n++)
213 group = group.SectionGroups [parts [n]];
216 return group.Sections [parts [parts.Length - 1]];
221 public ConfigurationSectionGroup GetSectionGroup (string path)
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]];
230 internal ConfigurationSection GetSectionInstance (SectionInfo config, bool createDefaultInstance)
232 object data = elementData [config];
233 ConfigurationSection sec = data as ConfigurationSection;
234 if (sec != null || !createDefaultInstance) return sec;
236 object secObj = config.CreateInstance ();
237 sec = secObj as ConfigurationSection;
239 DefaultSection ds = new DefaultSection ();
240 ds.SectionHandler = secObj as IConfigurationSectionHandler;
244 ConfigurationSection parentSection = parent != null ? parent.GetSectionInstance (config, true) : null;
246 string xml = data as string;
247 if (xml == null && parentSection != null)
248 xml = parentSection.RawXml;
250 sec.Reset (parentSection);
252 if (xml != null && xml == data) {
253 XmlTextReader r = new XmlTextReader (new StringReader (xml));
254 sec.DeserializeSection (r);
258 elementData [config] = sec;
262 internal ConfigurationSectionGroup GetSectionGroupInstance (SectionGroupInfo group)
264 ConfigurationSectionGroup gr = group.CreateInstance () as ConfigurationSectionGroup;
265 if (gr != null) gr.Initialize (this, group);
269 internal void SetConfigurationSection (SectionInfo config, ConfigurationSection sec)
271 elementData [config] = sec;
274 internal void SetSectionXml (SectionInfo config, string data)
276 elementData [config] = data;
279 internal string GetSectionXml (SectionInfo config)
281 return elementData [config] as string;
284 internal void CreateSection (SectionGroupInfo group, string name, ConfigurationSection sec)
286 if (group.HasChild (name))
287 throw new ConfigurationException ("Cannot add a ConfigurationSection. A section or section group already exists with the name '" + name + "'");
289 if (!HasFile && !sec.SectionInformation.AllowLocation)
290 throw new ConfigurationErrorsException ("The configuration section <" + name + "> cannot be defined inside a <location> element.");
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 + "').");
297 if (sec.SectionInformation.Type == null)
298 sec.SectionInformation.Type = system.Host.GetConfigTypeName (sec.GetType ());
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;
307 internal void CreateSectionGroup (SectionGroupInfo parentGroup, string name, ConfigurationSectionGroup sec)
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 ());
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;
320 internal void RemoveConfigInfo (ConfigInfo config)
322 elementData.Remove (config);
327 Save (ConfigurationSaveMode.Modified, false);
330 public void Save (ConfigurationSaveMode mode)
335 public void Save (ConfigurationSaveMode mode, bool forceUpdateAll)
338 Stream stream = system.Host.OpenStreamForWrite (streamName, null, ref ctx);
340 Save (stream, mode, forceUpdateAll);
341 system.Host.WriteCompleted (streamName, true, ctx);
342 } catch (Exception ex) {
343 system.Host.WriteCompleted (streamName, false, ctx);
350 public void SaveAs (string filename)
352 SaveAs (filename, ConfigurationSaveMode.Modified, false);
355 public void SaveAs (string filename, ConfigurationSaveMode mode)
357 SaveAs (filename, mode, false);
360 [MonoTODO ("Detect if file has changed")]
361 public void SaveAs (string filename, ConfigurationSaveMode mode, bool forceUpdateAll)
363 Save (new FileStream (filename, FileMode.OpenOrCreate, FileAccess.Write), mode, forceUpdateAll);
366 void Save (Stream stream, ConfigurationSaveMode mode, bool forceUpdateAll)
368 XmlTextWriter tw = new XmlTextWriter (new StreamWriter (stream));
369 tw.Formatting = Formatting.Indented;
371 tw.WriteStartDocument ();
372 if (rootNamespace != null)
373 tw.WriteStartElement ("configuration", rootNamespace);
375 tw.WriteStartElement ("configuration");
376 if (rootGroup.HasConfigContent (this)) {
377 rootGroup.WriteConfig (this, tw, mode);
380 foreach (ConfigurationLocation loc in Locations) {
381 if (loc.OpenedConfiguration == null) {
383 tw.WriteRaw (loc.XmlContent);
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 ();
395 SaveData (tw, mode, forceUpdateAll);
396 tw.WriteEndElement ();
403 void SaveData (XmlTextWriter tw, ConfigurationSaveMode mode, bool forceUpdateAll)
405 rootGroup.WriteRootData (tw, this, mode);
410 if (streamName == null || streamName == "")
413 XmlTextReader reader = null;
414 Stream stream = null;
417 stream = system.Host.OpenStreamForRead (streamName);
418 } catch (Exception e) {
423 reader = new XmlTextReader (stream);
424 ReadConfigFile (reader, streamName);
433 internal void ReadConfigFile (XmlTextReader reader, string fileName)
435 reader.MoveToContent ();
437 if (reader.NodeType != XmlNodeType.Element || reader.Name != "configuration")
438 ThrowException ("Configuration file does not have a valid root element", reader);
440 if (reader.HasAttributes) {
441 while (reader.MoveToNextAttribute ()) {
442 if (reader.LocalName == "xmlns") {
443 rootNamespace = reader.Value;
446 ThrowException (String.Format ("Unrecognized attribute '{0}' in root element", reader.LocalName), reader);
450 reader.MoveToElement ();
452 if (reader.IsEmptyElement) {
457 reader.ReadStartElement ();
458 reader.MoveToContent ();
460 if (reader.LocalName == "configSections") {
461 if (reader.HasAttributes)
462 ThrowException ("Unrecognized attribute in <configSections>.", reader);
464 rootGroup.ReadConfig (this, fileName, reader);
467 rootGroup.ReadRootData (reader, this, true);
470 internal void ReadData (XmlTextReader reader, bool allowOverride)
472 rootGroup.ReadData (this, reader, allowOverride);
476 private void ThrowException (string text, XmlTextReader reader)
478 throw new ConfigurationException (text, streamName, reader.LineNumber);