2 // System.Configuration.SectionGroupInfo.cs
5 // Lluis Sanchez (lluis@novell.com)
7 // Permission is hereby granted, free of charge, to any person obtaining
8 // a copy of this software and associated documentation files (the
9 // "Software"), to deal in the Software without restriction, including
10 // without limitation the rights to use, copy, modify, merge, publish,
11 // distribute, sublicense, and/or sell copies of the Software, and to
12 // permit persons to whom the Software is furnished to do so, subject to
13 // the following conditions:
15 // The above copyright notice and this permission notice shall be
16 // included in all copies or substantial portions of the Software.
18 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
19 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
20 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
21 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
22 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
23 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
24 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
26 // Copyright (C) 2004 Novell, Inc (http://www.novell.com)
30 using System.Collections;
31 using System.Collections.Specialized;
36 namespace System.Configuration
38 internal class SectionGroupInfo: ConfigInfo
41 ConfigInfoCollection sections;
42 ConfigInfoCollection groups;
43 static ConfigInfoCollection emptyList = new ConfigInfoCollection ();
45 public SectionGroupInfo ()
47 Type = typeof (ConfigurationSectionGroup);
50 public SectionGroupInfo (string groupName, string typeName)
56 public void AddChild (ConfigInfo data)
60 if (data is SectionInfo) {
61 if (sections == null) sections = new ConfigInfoCollection ();
62 sections [data.Name] = data;
65 if (groups == null) groups = new ConfigInfoCollection ();
66 groups [data.Name] = data;
73 if (sections != null) sections.Clear ();
74 if (groups != null) groups.Clear ();
77 public bool HasChild (string name)
79 if (sections != null && sections [name] != null) return true;
80 return (groups != null && groups [name] != null);
83 public void RemoveChild (string name)
87 sections.Remove (name);
92 public SectionInfo GetChildSection (string name)
95 return sections [name] as SectionInfo;
100 public SectionGroupInfo GetChildGroup (string name)
103 return groups [name] as SectionGroupInfo;
108 public ConfigInfoCollection Sections
110 get { if (sections == null) return emptyList; else return sections; }
113 public ConfigInfoCollection Groups
115 get { if (groups == null) return emptyList; else return groups; }
118 public override bool HasDataContent (Configuration config)
120 foreach (ConfigInfoCollection col in new object[] {Sections, Groups}) {
121 foreach (string key in col) {
122 ConfigInfo cinfo = col [key];
123 if (cinfo.HasDataContent (config))
130 public override bool HasConfigContent (Configuration cfg)
132 if (StreamName == cfg.FileName) return true;
133 foreach (ConfigInfoCollection col in new object[] {Sections, Groups}) {
134 foreach (string key in col) {
135 ConfigInfo cinfo = col [key];
136 if (cinfo.HasConfigContent (cfg))
143 public override void ReadConfig (Configuration cfg, string streamName, XmlReader reader)
145 StreamName = streamName;
146 ConfigHost = cfg.ConfigHost;
148 if (reader.LocalName != "configSections")
150 while (reader.MoveToNextAttribute ()) {
151 if (reader.Name == "name")
153 else if (reader.Name == "type") {
154 TypeName = reader.Value;
158 ThrowException ("Unrecognized attribute", reader);
162 ThrowException ("sectionGroup must have a 'name' attribute", reader);
164 if (Name == "location")
165 ThrowException ("location is a reserved section name", reader);
168 if (TypeName == null)
169 TypeName = "System.Configuration.ConfigurationSectionGroup";
171 if (reader.IsEmptyElement) {
176 reader.ReadStartElement ();
177 reader.MoveToContent ();
179 while (reader.NodeType != XmlNodeType.EndElement)
181 if (reader.NodeType != XmlNodeType.Element) {
186 string name = reader.LocalName;
187 ConfigInfo cinfo = null;
189 if (name == "remove") {
190 ReadRemoveSection (reader);
194 if (name == "clear") {
195 if (reader.HasAttributes)
196 ThrowException ("Unrecognized attribute.", reader);
203 if (name == "section")
204 cinfo = new SectionInfo ();
205 else if (name == "sectionGroup") {
206 cinfo = new SectionGroupInfo ();
208 ThrowException ("Unrecognized element: " + reader.Name, reader);
210 cinfo.ReadConfig (cfg, streamName, reader);
211 ConfigInfo actInfo = Groups [cinfo.Name];
212 if (actInfo == null) actInfo = Sections [cinfo.Name];
214 if (actInfo != null) {
215 if (actInfo.GetType () != cinfo.GetType ())
216 ThrowException ("A section or section group named '" + cinfo.Name + "' already exists", reader);
217 // Merge all the new data
218 actInfo.Merge (cinfo);
220 // Make sure that this section is saved in this configuration file:
221 actInfo.StreamName = streamName;
227 reader.ReadEndElement ();
230 public override void WriteConfig (Configuration cfg, XmlWriter writer, ConfigurationSaveMode mode)
233 writer.WriteStartElement ("sectionGroup");
234 writer.WriteAttributeString ("name", Name);
235 if (TypeName != null && TypeName != "" && TypeName != "System.Configuration.ConfigurationSectionGroup")
236 writer.WriteAttributeString ("type", TypeName);
239 writer.WriteStartElement ("configSections");
241 foreach (ConfigInfoCollection col in new object[] {Sections, Groups}) {
242 foreach (string key in col) {
243 ConfigInfo cinfo = col [key];
244 if (cinfo.HasConfigContent (cfg))
245 cinfo.WriteConfig (cfg, writer, mode);
249 writer.WriteEndElement ();
252 private void ReadRemoveSection (XmlReader reader)
254 if (!reader.MoveToNextAttribute () || reader.Name != "name")
255 ThrowException ("Unrecognized attribute.", reader);
257 string removeValue = reader.Value;
258 if (String.IsNullOrEmpty (removeValue))
259 ThrowException ("Empty name to remove", reader);
261 reader.MoveToElement ();
263 if (!HasChild (removeValue))
264 ThrowException ("No factory for " + removeValue, reader);
266 RemoveChild (removeValue);
270 public void ReadRootData (XmlReader reader, Configuration config, bool overrideAllowed)
272 reader.MoveToContent ();
273 ReadContent (reader, config, overrideAllowed, true);
276 public override void ReadData (Configuration config, XmlReader reader, bool overrideAllowed)
278 reader.MoveToContent ();
279 if (!reader.IsEmptyElement) {
280 reader.ReadStartElement ();
281 ReadContent (reader, config, overrideAllowed, false);
282 reader.MoveToContent ();
283 reader.ReadEndElement ();
288 void ReadContent (XmlReader reader, Configuration config, bool overrideAllowed, bool root)
290 while (reader.NodeType != XmlNodeType.EndElement && reader.NodeType != XmlNodeType.None) {
291 if (reader.NodeType != XmlNodeType.Element) {
296 if (reader.LocalName == "dllmap") {
301 if (reader.LocalName == "location") {
303 ThrowException ("<location> elements are only allowed in <configuration> elements.", reader);
305 string allowOverrideAttr = reader.GetAttribute ("allowOverride");
306 bool allowOverride = allowOverrideAttr == null || allowOverrideAttr.Length == 0 || bool.Parse (allowOverrideAttr);
307 string path = reader.GetAttribute ("path");
308 if (path != null && path.Length > 0) {
309 string xml = reader.ReadOuterXml ();
310 string[] pathList = path.Split (',');
312 foreach (string p in pathList) {
314 if (config.Locations.Find (tpath) != null)
315 ThrowException ("Sections must only appear once per config file.", reader);
317 ConfigurationLocation loc = new ConfigurationLocation (tpath, xml, config, allowOverride);
318 config.Locations.Add (loc);
321 ReadData (config, reader, allowOverride);
326 ConfigInfo data = GetConfigInfo (reader, this);
328 data.ReadData (config, reader, overrideAllowed);
330 ThrowException ("Unrecognized configuration section <" + reader.LocalName + ">", reader);
334 ConfigInfo GetConfigInfo (XmlReader reader, SectionGroupInfo current)
336 ConfigInfo data = null;
337 if (current.sections != null)
338 data = current.sections [reader.LocalName];
341 if (current.groups != null)
342 data = current.groups [reader.LocalName];
345 if (current.groups == null)
347 // It might be a section in descendant sectionGroups
348 foreach (string key in current.groups.AllKeys) {
349 data = GetConfigInfo (reader, (SectionGroupInfo) current.groups [key]);
354 // It might be in the root section group
358 internal override void Merge (ConfigInfo newData)
360 SectionGroupInfo data = newData as SectionGroupInfo;
364 if (data.sections != null && data.sections.Count > 0)
365 foreach (string key in data.sections.AllKeys) {
366 actInfo = sections[key];
369 sections.Add (key, data.sections[key]);
372 if (data.groups != null && data.sections != null && data.sections.Count > 0)
373 foreach (string key in data.groups.AllKeys) {
374 actInfo = groups[key];
377 groups.Add (key, data.groups[key]);
381 public void WriteRootData (XmlWriter writer, Configuration config, ConfigurationSaveMode mode)
383 WriteContent (writer, config, mode, false);
386 public override void WriteData (Configuration config, XmlWriter writer, ConfigurationSaveMode mode)
388 writer.WriteStartElement (Name);
389 WriteContent (writer, config, mode, true);
390 writer.WriteEndElement ();
393 public void WriteContent (XmlWriter writer, Configuration config, ConfigurationSaveMode mode, bool writeElem)
395 foreach (ConfigInfoCollection col in new object[] {Sections, Groups}) {
396 foreach (string key in col) {
397 ConfigInfo cinfo = col [key];
398 if (cinfo.HasDataContent (config))
399 cinfo.WriteData (config, writer, mode);
404 internal override bool HasValues (Configuration config, ConfigurationSaveMode mode)
406 if (modified && (mode == ConfigurationSaveMode.Modified))
409 foreach (ConfigInfoCollection col in new object[] { Sections, Groups}) {
410 foreach (string key in col) {
411 ConfigInfo cinfo = col [key];
412 if (cinfo.HasValues (config, mode))
420 internal override void ResetModified (Configuration config)
423 foreach (ConfigInfoCollection col in new object[] { Sections, Groups}) {
424 foreach (string key in col) {
425 ConfigInfo cinfo = col [key];
426 cinfo.ResetModified (config);
432 internal class ConfigInfoCollection : NameObjectCollectionBase
434 public ConfigInfoCollection ()
435 : base (StringComparer.Ordinal)
439 public ICollection AllKeys
444 public ConfigInfo this [string name]
446 get { return (ConfigInfo) BaseGet (name); }
447 set { BaseSet (name, value); }
450 public ConfigInfo this [int index]
452 get { return (ConfigInfo) BaseGet (index); }
453 set { BaseSet (index, value); }
456 public void Add (string name, ConfigInfo config)
458 BaseAdd (name, config);
466 public string GetKey (int index)
468 return BaseGetKey (index);
471 public void Remove (string name)
476 public void RemoveAt (int index)
478 BaseRemoveAt (index);