Merge pull request #260 from pcc/topmost
[mono.git] / mcs / class / System.Configuration / System.Configuration / Configuration.cs
index 9cba5ceaeceefa8b737857bf589f0ba71c0a7937..760371766904e216889732ba05dc9a24cc7746b3 100644 (file)
 //
 // Copyright (C) 2004 Novell, Inc (http://www.novell.com)
 //
-#if NET_2_0
+
 using System;
 using System.Collections;
 using System.Collections.Specialized;
+using System.Configuration.Internal;
+using System.ComponentModel;
 using System.Reflection;
 using System.Xml;
 using System.IO;
-using System.Configuration.Internal;
 
 namespace System.Configuration {
 
-       public sealed class Configuration
+       // For configuration document, use this XmlDocument instead of the standard one. This ignores xmlns attribute for MS.
+       internal class ConfigurationXmlDocument : XmlDocument
        {
+               public override XmlElement CreateElement (string prefix, string localName, string namespaceURI)
+               {
+                       if (namespaceURI == "http://schemas.microsoft.com/.NetConfiguration/v2.0")
+                               return base.CreateElement (String.Empty, localName, String.Empty);
+                       else
+                               return base.CreateElement (prefix, localName, namespaceURI);
+               }
+       }
+
+       public sealed class Configuration
+       {               
                Configuration parent;
                Hashtable elementData = new Hashtable ();
                string streamName;
@@ -53,19 +66,23 @@ namespace System.Configuration {
                string locationConfigPath;
                string locationSubPath;
 
+               internal static event ConfigurationSaveEventHandler SaveStart;
+               internal static event ConfigurationSaveEventHandler SaveEnd;
+               
                internal Configuration (Configuration parent, string locationSubPath)
                {
                        this.parent = parent;
                        this.system = parent.system;
                        this.rootGroup = parent.rootGroup;
                        this.locationSubPath = locationSubPath;
+                       this.configPath = parent.ConfigPath;
                }
                
                internal Configuration (InternalConfigurationSystem system, string locationSubPath)
                {
                        hasFile = true;
                        this.system = system;
-                       
+
                        system.InitForConfiguration (ref locationSubPath, out configPath, out locationConfigPath);
                        
                        Configuration parent = null;
@@ -81,18 +98,21 @@ namespace System.Configuration {
                
                internal Configuration FindLocationConfiguration (string relativePath, Configuration defaultConfiguration)
                {
-                       ConfigurationLocation loc = Locations.Find (relativePath);
-                       
                        Configuration parentConfig = defaultConfiguration;
-                       
-                       if (LocationConfigPath != null) {
+
+                       if (!String.IsNullOrEmpty (LocationConfigPath)) {
                                Configuration parentFile = GetParentWithFile ();
                                if (parentFile != null) {
-                                       string parentRelativePath = system.Host.GetConfigPathFromLocationSubPath (LocationConfigPath, relativePath);
+                                       string parentRelativePath = system.Host.GetConfigPathFromLocationSubPath (configPath, relativePath);
                                        parentConfig = parentFile.FindLocationConfiguration (parentRelativePath, defaultConfiguration);
                                }
                        }
 
+                       string relConfigPath = configPath.Substring (1) + "/";
+                       if (relativePath.StartsWith (relConfigPath, StringComparison.Ordinal))
+                               relativePath = relativePath.Substring (relConfigPath.Length);
+
+                       ConfigurationLocation loc = Locations.FindBest (relativePath);
                        if (loc == null)
                                return parentConfig;
                        
@@ -216,11 +236,11 @@ namespace System.Configuration {
                                        rootSectionGroup.Initialize (this, rootGroup);
                                }
                                return rootSectionGroup;
-                       }                        
+                       }
                }
 
                public ConfigurationSectionGroupCollection SectionGroups {
-                       get { return RootSectionGroup.SectionGroups; }                        
+                       get { return RootSectionGroup.SectionGroups; }
                }
 
                public ConfigurationSectionCollection Sections {
@@ -265,6 +285,7 @@ namespace System.Configuration {
                                ds.SectionHandler = secObj as IConfigurationSectionHandler;
                                sec = ds;
                        }
+                       sec.Configuration = this;
 
                        ConfigurationSection parentSection = null;
                        if (parent != null) {
@@ -272,13 +293,15 @@ namespace System.Configuration {
                                sec.SectionInformation.SetParentSection (parentSection);
                        }
                        sec.SectionInformation.ConfigFilePath = FilePath;
+
+                       sec.ConfigContext = system.Host.CreateDeprecatedConfigContext(configPath);
                        
                        string xml = data as string;
                        sec.RawXml = xml;
                        sec.Reset (parentSection);
 
-                       if (xml != null && xml == data) {
-                               XmlTextReader r = new XmlTextReader (new StringReader (xml));
+                       if (xml != null) {
+                               XmlTextReader r = new ConfigXmlTextReader (new StringReader (xml), FilePath);
                                sec.DeserializeSection (r);
                                r.Close ();
 
@@ -315,7 +338,7 @@ namespace System.Configuration {
                internal void CreateSection (SectionGroupInfo group, string name, ConfigurationSection sec)
                {
                        if (group.HasChild (name))
-                               throw new ConfigurationException ("Cannot add a ConfigurationSection. A section or section group already exists with the name '" + name + "'");
+                               throw new ConfigurationErrorsException ("Cannot add a ConfigurationSection. A section or section group already exists with the name '" + name + "'");
                                
                        if (!HasFile && !sec.SectionInformation.AllowLocation)
                                throw new ConfigurationErrorsException ("The configuration section <" + name + "> cannot be defined inside a <location> element."); 
@@ -324,20 +347,21 @@ namespace System.Configuration {
                                object ctx = sec.SectionInformation.AllowExeDefinition != ConfigurationAllowExeDefinition.MachineToApplication ? (object) sec.SectionInformation.AllowExeDefinition : (object) sec.SectionInformation.AllowDefinition;
                                throw new ConfigurationErrorsException ("The section <" + name + "> can't be defined in this configuration file (the allowed definition context is '" + ctx + "').");
                        }
-                                               
+
                        if (sec.SectionInformation.Type == null)
                                sec.SectionInformation.Type = system.Host.GetConfigTypeName (sec.GetType ());
-                       
+
                        SectionInfo section = new SectionInfo (name, sec.SectionInformation);
                        section.StreamName = streamName;
                        section.ConfigHost = system.Host;
                        group.AddChild (section);
                        elementData [section] = sec;
+                       sec.Configuration = this;
                }
                
                internal void CreateSectionGroup (SectionGroupInfo parentGroup, string name, ConfigurationSectionGroup sec)
                {
-                       if (parentGroup.HasChild (name)) throw new ConfigurationException ("Cannot add a ConfigurationSectionGroup. A section or section group already exists with the name '" + name + "'");
+                       if (parentGroup.HasChild (name)) throw new ConfigurationErrorsException ("Cannot add a ConfigurationSectionGroup. A section or section group already exists with the name '" + name + "'");
                        if (sec.Type == null) sec.Type = system.Host.GetConfigTypeName (sec.GetType ());
                        sec.SetName (name);
 
@@ -367,16 +391,31 @@ namespace System.Configuration {
                
                public void Save (ConfigurationSaveMode mode, bool forceUpdateAll)
                {
+                       if (!forceUpdateAll && (mode != ConfigurationSaveMode.Full) && !HasValues (mode)) {
+                               ResetModified ();
+                               return;
+                       }
+
+                       ConfigurationSaveEventHandler saveStart = SaveStart;
+                       ConfigurationSaveEventHandler saveEnd = SaveEnd;
+                       
                        object ctx = null;
+                       Exception saveEx = null;
                        Stream stream = system.Host.OpenStreamForWrite (streamName, null, ref ctx);
                        try {
+                               if (saveStart != null)
+                                       saveStart (this, new ConfigurationSaveEventArgs (streamName, true, null, ctx));
+                               
                                Save (stream, mode, forceUpdateAll);
                                system.Host.WriteCompleted (streamName, true, ctx);
-                       } catch (Exception) {
+                       } catch (Exception ex) {
+                               saveEx = ex;
                                system.Host.WriteCompleted (streamName, false, ctx);
                                throw;
                        } finally {
                                stream.Close ();
+                               if (saveEnd != null)
+                                       saveEnd (this, new ConfigurationSaveEventArgs (streamName, false, saveEx, ctx));
                        }
                }
                
@@ -393,6 +432,11 @@ namespace System.Configuration {
                [MonoInternalNote ("Detect if file has changed")]
                public void SaveAs (string filename, ConfigurationSaveMode mode, bool forceUpdateAll)
                {
+                       if (!forceUpdateAll && (mode != ConfigurationSaveMode.Full) && !HasValues (mode)) {
+                               ResetModified ();
+                               return;
+                       }
+                       
                        string dir = Path.GetDirectoryName (Path.GetFullPath (filename));
                        if (!Directory.Exists (dir))
                                Directory.CreateDirectory (dir);
@@ -430,8 +474,10 @@ namespace System.Configuration {
                                
                                SaveData (tw, mode, forceUpdateAll);
                                tw.WriteEndElement ();
+                               ResetModified ();
                        }
                        finally {
+                               tw.Flush ();
                                tw.Close ();
                        }
                }
@@ -440,35 +486,52 @@ namespace System.Configuration {
                {
                        rootGroup.WriteRootData (tw, this, mode);
                }
+
+               bool HasValues (ConfigurationSaveMode mode)
+               {
+                       foreach (ConfigurationLocation loc in Locations) {
+                               if (loc.OpenedConfiguration == null)
+                                       continue;
+                               if (loc.OpenedConfiguration.HasValues (mode))
+                                       return true;
+                       }
+
+                       return rootGroup.HasValues (this, mode);
+               }
+
+               void ResetModified ()
+               {
+                       foreach (ConfigurationLocation loc in Locations) {
+                               if (loc.OpenedConfiguration == null)
+                                       continue;
+                               loc.OpenedConfiguration.ResetModified ();
+                       }
+                       
+                       rootGroup.ResetModified (this);
+               }
                
                bool Load ()
                {
                        if (String.IsNullOrEmpty (streamName))
                                return true;
 
-                       XmlTextReader reader = null;
                        Stream stream = null;
-                       
-                       // FIXME: we should remove this kind of hack that
-                       // hides the actual error
                        try {
                                stream = system.Host.OpenStreamForRead (streamName);
-                       } catch (Exception) {
+                               if (stream == null)
+                                       return false;
+                       } catch {
                                return false;
                        }
 
-                       try {
-                               reader = new XmlTextReader (stream);
+                       using (XmlTextReader reader = new ConfigXmlTextReader (stream, streamName)) {
                                ReadConfigFile (reader, streamName);
-                       } finally {
-                               if (reader != null)
-                                       reader.Close();
                        }
+                       ResetModified ();
                        return true;
                }
 
-
-               void ReadConfigFile (XmlTextReader reader, string fileName)
+               void ReadConfigFile (XmlReader reader, string fileName)
                {
                        reader.MoveToContent ();
 
@@ -504,18 +567,18 @@ namespace System.Configuration {
                        
                        rootGroup.ReadRootData (reader, this, true);
                }
-               
-               internal void ReadData (XmlTextReader reader, bool allowOverride)
+
+               internal void ReadData (XmlReader reader, bool allowOverride)
                {
                        rootGroup.ReadData (this, reader, allowOverride);
                }
                
 
-               private void ThrowException (string text, XmlTextReader reader)
+               private void ThrowException (string text, XmlReader reader)
                {
-                       throw new ConfigurationException (text, streamName, reader.LineNumber);
+                       IXmlLineInfo li = reader as IXmlLineInfo;
+                       throw new ConfigurationErrorsException (text, streamName, li != null ? li.LineNumber : 0);
                }
        }
 }
 
-#endif