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;
54 string locationSubPath;
56 internal Configuration (Configuration parent, string locationSubPath)
59 this.system = parent.system;
60 this.rootGroup = parent.rootGroup;
61 this.locationSubPath = locationSubPath;
64 internal Configuration (InternalConfigurationSystem system, string locationSubPath)
69 system.InitForConfiguration (ref locationSubPath, out configPath, out locationConfigPath);
71 Configuration parent = null;
73 if (locationSubPath != null) {
74 parent = new Configuration (system, locationSubPath);
75 if (locationConfigPath != null)
76 parent = parent.FindLocationConfiguration (locationConfigPath, parent);
79 Init (system, configPath, parent);
82 internal Configuration FindLocationConfiguration (string relativePath, Configuration defaultConfiguration)
84 ConfigurationLocation loc = Locations.Find (relativePath);
86 Configuration parentConfig = defaultConfiguration;
88 if (LocationConfigPath != null) {
89 Configuration parentFile = GetParentWithFile ();
90 if (parentFile != null) {
91 string parentRelativePath = system.Host.GetConfigPathFromLocationSubPath (LocationConfigPath, relativePath);
92 parentConfig = parentFile.FindLocationConfiguration (parentRelativePath, defaultConfiguration);
99 loc.SetParentConfiguration (parentConfig);
100 return loc.OpenConfiguration ();
103 internal void Init (IConfigSystem system, string configPath, Configuration parent)
105 this.system = system;
106 this.configPath = configPath;
107 streamName = system.Host.GetStreamName (configPath);
108 this.parent = parent;
110 rootGroup = parent.rootGroup;
112 rootGroup = new SectionGroupInfo ();
113 rootGroup.StreamName = streamName;
116 if (streamName != null)
120 internal Configuration Parent {
121 get { return parent; }
122 set { parent = value; }
125 internal Configuration GetParentWithFile ()
127 Configuration parentFile = Parent;
128 while (parentFile != null && !parentFile.HasFile)
129 parentFile = parentFile.Parent;
133 internal string FileName {
134 get { return streamName; }
137 internal IInternalConfigHost ConfigHost {
138 get { return system.Host; }
141 internal string LocationConfigPath {
142 get { return locationConfigPath; }
145 internal string GetLocationSubPath ()
147 Configuration confg = parent;
149 while (confg != null) {
150 path = confg.locationSubPath;
151 if (!String.IsNullOrEmpty (path))
153 confg = confg.parent;
158 internal string ConfigPath {
159 get { return configPath; }
162 public AppSettingsSection AppSettings {
163 get { return (AppSettingsSection) GetSection ("appSettings"); }
166 public ConnectionStringsSection ConnectionStrings {
167 get { return (ConnectionStringsSection) GetSection ("connectionStrings"); }
170 // MSDN: If the value for this FilePath property represents a merged view and
171 // no actual file exists for the application, the path to the parent configuration
173 public string FilePath {
175 if (streamName == null && parent != null)
176 return parent.FilePath;
181 public bool HasFile {
187 ContextInformation evaluationContext;
188 public ContextInformation EvaluationContext {
190 if (evaluationContext == null) {
191 object ctx = system.Host.CreateConfigurationContext (configPath, GetLocationSubPath() );
192 evaluationContext = new ContextInformation (this, ctx);
196 return evaluationContext;
200 public ConfigurationLocationCollection Locations {
202 if (locations == null) locations = new ConfigurationLocationCollection ();
207 public bool NamespaceDeclared {
208 get { return rootNamespace != null; }
209 set { rootNamespace = value ? "http://schemas.microsoft.com/.NetConfiguration/v2.0" : null; }
212 public ConfigurationSectionGroup RootSectionGroup {
214 if (rootSectionGroup == null) {
215 rootSectionGroup = new ConfigurationSectionGroup ();
216 rootSectionGroup.Initialize (this, rootGroup);
218 return rootSectionGroup;
222 public ConfigurationSectionGroupCollection SectionGroups {
223 get { return RootSectionGroup.SectionGroups; }
226 public ConfigurationSectionCollection Sections {
227 get { return RootSectionGroup.Sections; }
230 public ConfigurationSection GetSection (string path)
232 string[] parts = path.Split ('/');
233 if (parts.Length == 1)
234 return Sections [parts[0]];
236 ConfigurationSectionGroup group = SectionGroups [parts[0]];
237 for (int n=1; group != null && n<parts.Length-1; n++)
238 group = group.SectionGroups [parts [n]];
241 return group.Sections [parts [parts.Length - 1]];
246 public ConfigurationSectionGroup GetSectionGroup (string path)
248 string[] parts = path.Split ('/');
249 ConfigurationSectionGroup group = SectionGroups [parts[0]];
250 for (int n=1; group != null && n<parts.Length; n++)
251 group = group.SectionGroups [parts [n]];
255 internal ConfigurationSection GetSectionInstance (SectionInfo config, bool createDefaultInstance)
257 object data = elementData [config];
258 ConfigurationSection sec = data as ConfigurationSection;
259 if (sec != null || !createDefaultInstance) return sec;
261 object secObj = config.CreateInstance ();
262 sec = secObj as ConfigurationSection;
264 DefaultSection ds = new DefaultSection ();
265 ds.SectionHandler = secObj as IConfigurationSectionHandler;
268 sec.Configuration = this;
270 ConfigurationSection parentSection = null;
271 if (parent != null) {
272 parentSection = parent.GetSectionInstance (config, true);
273 sec.SectionInformation.SetParentSection (parentSection);
275 sec.SectionInformation.ConfigFilePath = FilePath;
277 string xml = data as string;
279 sec.Reset (parentSection);
281 if (xml != null && xml == data) {
282 XmlTextReader r = new XmlTextReader (new StringReader (xml));
283 sec.DeserializeSection (r);
286 if (!String.IsNullOrEmpty (sec.SectionInformation.ConfigSource) && !String.IsNullOrEmpty (FilePath))
287 sec.DeserializeConfigSource (Path.GetDirectoryName (FilePath));
290 elementData [config] = sec;
294 internal ConfigurationSectionGroup GetSectionGroupInstance (SectionGroupInfo group)
296 ConfigurationSectionGroup gr = group.CreateInstance () as ConfigurationSectionGroup;
297 if (gr != null) gr.Initialize (this, group);
301 internal void SetConfigurationSection (SectionInfo config, ConfigurationSection sec)
303 elementData [config] = sec;
306 internal void SetSectionXml (SectionInfo config, string data)
308 elementData [config] = data;
311 internal string GetSectionXml (SectionInfo config)
313 return elementData [config] as string;
316 internal void CreateSection (SectionGroupInfo group, string name, ConfigurationSection sec)
318 if (group.HasChild (name))
319 throw new ConfigurationException ("Cannot add a ConfigurationSection. A section or section group already exists with the name '" + name + "'");
321 if (!HasFile && !sec.SectionInformation.AllowLocation)
322 throw new ConfigurationErrorsException ("The configuration section <" + name + "> cannot be defined inside a <location> element.");
324 if (!system.Host.IsDefinitionAllowed (configPath, sec.SectionInformation.AllowDefinition, sec.SectionInformation.AllowExeDefinition)) {
325 object ctx = sec.SectionInformation.AllowExeDefinition != ConfigurationAllowExeDefinition.MachineToApplication ? (object) sec.SectionInformation.AllowExeDefinition : (object) sec.SectionInformation.AllowDefinition;
326 throw new ConfigurationErrorsException ("The section <" + name + "> can't be defined in this configuration file (the allowed definition context is '" + ctx + "').");
329 if (sec.SectionInformation.Type == null)
330 sec.SectionInformation.Type = system.Host.GetConfigTypeName (sec.GetType ());
332 SectionInfo section = new SectionInfo (name, sec.SectionInformation);
333 section.StreamName = streamName;
334 section.ConfigHost = system.Host;
335 group.AddChild (section);
336 elementData [section] = sec;
339 internal void CreateSectionGroup (SectionGroupInfo parentGroup, string name, ConfigurationSectionGroup sec)
341 if (parentGroup.HasChild (name)) throw new ConfigurationException ("Cannot add a ConfigurationSectionGroup. A section or section group already exists with the name '" + name + "'");
342 if (sec.Type == null) sec.Type = system.Host.GetConfigTypeName (sec.GetType ());
345 SectionGroupInfo section = new SectionGroupInfo (name, sec.Type);
346 section.StreamName = streamName;
347 section.ConfigHost = system.Host;
348 parentGroup.AddChild (section);
349 elementData [section] = sec;
351 sec.Initialize (this, section);
354 internal void RemoveConfigInfo (ConfigInfo config)
356 elementData.Remove (config);
361 Save (ConfigurationSaveMode.Modified, false);
364 public void Save (ConfigurationSaveMode mode)
369 public void Save (ConfigurationSaveMode mode, bool forceUpdateAll)
372 Stream stream = system.Host.OpenStreamForWrite (streamName, null, ref ctx);
374 Save (stream, mode, forceUpdateAll);
375 system.Host.WriteCompleted (streamName, true, ctx);
376 } catch (Exception) {
377 system.Host.WriteCompleted (streamName, false, ctx);
384 public void SaveAs (string filename)
386 SaveAs (filename, ConfigurationSaveMode.Modified, false);
389 public void SaveAs (string filename, ConfigurationSaveMode mode)
391 SaveAs (filename, mode, false);
394 [MonoInternalNote ("Detect if file has changed")]
395 public void SaveAs (string filename, ConfigurationSaveMode mode, bool forceUpdateAll)
397 string dir = Path.GetDirectoryName (Path.GetFullPath (filename));
398 if (!Directory.Exists (dir))
399 Directory.CreateDirectory (dir);
400 Save (new FileStream (filename, FileMode.OpenOrCreate, FileAccess.Write), mode, forceUpdateAll);
403 void Save (Stream stream, ConfigurationSaveMode mode, bool forceUpdateAll)
405 XmlTextWriter tw = new XmlTextWriter (new StreamWriter (stream));
406 tw.Formatting = Formatting.Indented;
408 tw.WriteStartDocument ();
409 if (rootNamespace != null)
410 tw.WriteStartElement ("configuration", rootNamespace);
412 tw.WriteStartElement ("configuration");
413 if (rootGroup.HasConfigContent (this)) {
414 rootGroup.WriteConfig (this, tw, mode);
417 foreach (ConfigurationLocation loc in Locations) {
418 if (loc.OpenedConfiguration == null) {
420 tw.WriteRaw (loc.XmlContent);
423 tw.WriteStartElement ("location");
424 tw.WriteAttributeString ("path", loc.Path);
425 if (!loc.AllowOverride)
426 tw.WriteAttributeString ("allowOverride", "false");
427 loc.OpenedConfiguration.SaveData (tw, mode, forceUpdateAll);
428 tw.WriteEndElement ();
432 SaveData (tw, mode, forceUpdateAll);
433 tw.WriteEndElement ();
440 void SaveData (XmlTextWriter tw, ConfigurationSaveMode mode, bool forceUpdateAll)
442 rootGroup.WriteRootData (tw, this, mode);
447 if (String.IsNullOrEmpty (streamName))
450 XmlTextReader reader = null;
451 Stream stream = null;
453 // FIXME: we should remove this kind of hack that
454 // hides the actual error
456 stream = system.Host.OpenStreamForRead (streamName);
457 } catch (Exception) {
462 reader = new XmlTextReader (stream);
463 ReadConfigFile (reader, streamName);
472 void ReadConfigFile (XmlTextReader reader, string fileName)
474 reader.MoveToContent ();
476 if (reader.NodeType != XmlNodeType.Element || reader.Name != "configuration")
477 ThrowException ("Configuration file does not have a valid root element", reader);
479 if (reader.HasAttributes) {
480 while (reader.MoveToNextAttribute ()) {
481 if (reader.LocalName == "xmlns") {
482 rootNamespace = reader.Value;
485 ThrowException (String.Format ("Unrecognized attribute '{0}' in root element", reader.LocalName), reader);
489 reader.MoveToElement ();
491 if (reader.IsEmptyElement) {
496 reader.ReadStartElement ();
497 reader.MoveToContent ();
499 if (reader.LocalName == "configSections") {
500 if (reader.HasAttributes)
501 ThrowException ("Unrecognized attribute in <configSections>.", reader);
503 rootGroup.ReadConfig (this, fileName, reader);
506 rootGroup.ReadRootData (reader, this, true);
509 internal void ReadData (XmlTextReader reader, bool allowOverride)
511 rootGroup.ReadData (this, reader, allowOverride);
515 private void ThrowException (string text, XmlTextReader reader)
517 throw new ConfigurationException (text, streamName, reader.LineNumber);