2 // System.Configuration.ConfigurationElement.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;
33 using System.Reflection;
35 using System.ComponentModel;
37 namespace System.Configuration
39 public abstract class ConfigurationElement
44 ConfigurationPropertyCollection keyProps;
46 ElementInformation elementInfo;
47 ConfigurationElementProperty elementProperty;
49 protected ConfigurationElement ()
53 internal virtual void InitFromProperty (PropertyInformation propertyInfo)
55 elementInfo = new ElementInformation (this, propertyInfo);
59 public ElementInformation ElementInformation {
61 if (elementInfo == null)
62 elementInfo = new ElementInformation (this, null);
67 internal string RawXml {
68 get { return rawXml; }
69 set { rawXml = value; }
72 protected internal virtual void Init ()
76 public ConfigurationElementProperty ElementProperty {
78 if (elementProperty == null)
79 elementProperty = new ConfigurationElementProperty (ElementInformation.Validator);
80 return elementProperty;
85 protected ContextInformation EvaluationContext {
87 throw new NotImplementedException ();
92 public ConfigurationLockCollection LockAllAttributesExcept {
94 throw new NotImplementedException ();
99 public ConfigurationLockCollection LockAllElementsExcept {
101 throw new NotImplementedException ();
106 ConfigurationLockCollection lockAttributes;
107 public ConfigurationLockCollection LockAttributes {
109 if (lockAttributes == null) {
110 lockAttributes = new ConfigurationLockCollection (this, ConfigurationLockType.Attribute);
113 return lockAttributes;
118 public ConfigurationLockCollection LockElements {
120 throw new NotImplementedException ();
125 public bool LockItem {
127 throw new NotImplementedException ();
130 throw new NotImplementedException ();
135 public void ListErrors (IList list)
137 throw new NotImplementedException ();
141 public void SetPropertyValue (ConfigurationProperty prop, object value, bool ignoreLocks)
143 throw new NotImplementedException ();
146 internal ConfigurationPropertyCollection GetKeyProperties ()
148 if (keyProps != null) return keyProps;
150 if (map.Properties == Properties)
151 keyProps = map.KeyProperties;
153 keyProps = new ConfigurationPropertyCollection ();
154 foreach (ConfigurationProperty prop in Properties) {
162 protected internal object this [ConfigurationProperty property] {
163 get { return this [property.Name]; }
164 set { this [property.Name] = value; }
167 protected internal object this [string property_name] {
169 PropertyInformation pi = ElementInformation.Properties [property_name];
171 throw new InvalidOperationException ("Property '" + property_name + "' not found in configuration element");
177 PropertyInformation pi = ElementInformation.Properties [property_name];
179 throw new InvalidOperationException ("Property '" + property_name + "' not found in configuration element");
186 protected internal virtual ConfigurationPropertyCollection Properties {
189 map = ElementMap.GetMap (GetType());
190 return map.Properties;
194 public override bool Equals (object compareTo)
196 ConfigurationElement other = compareTo as ConfigurationElement;
197 if (other == null) return false;
198 if (GetType() != other.GetType()) return false;
200 foreach (ConfigurationProperty prop in Properties) {
201 if (!object.Equals (this [prop], other [prop]))
207 public override int GetHashCode ()
210 foreach (ConfigurationProperty prop in Properties)
211 code += this [prop].GetHashCode ();
215 internal virtual bool HasValues ()
217 foreach (PropertyInformation pi in ElementInformation.Properties)
218 if (pi.ValueOrigin != PropertyValueOrigin.Default)
223 protected internal virtual void DeserializeElement (XmlReader reader, bool serializeCollectionKey)
225 Hashtable readProps = new Hashtable ();
227 reader.MoveToContent ();
228 while (reader.MoveToNextAttribute ())
230 PropertyInformation prop = ElementInformation.Properties [reader.LocalName];
231 if (prop == null || (serializeCollectionKey && !prop.IsKey)) {
232 if (!OnDeserializeUnrecognizedAttribute (reader.LocalName, reader.Value))
233 throw new ConfigurationException ("Unrecognized attribute '" + reader.LocalName + "'.");
237 if (readProps.ContainsKey (prop))
238 throw new ConfigurationException ("The attribute '" + prop.Name + "' may only appear once in this element.");
240 prop.SetStringValue (reader.Value);
241 readProps [prop] = prop.Name;
244 reader.MoveToElement ();
245 if (reader.IsEmptyElement) {
249 reader.ReadStartElement ();
250 reader.MoveToContent ();
252 while (reader.NodeType != XmlNodeType.EndElement)
254 if (reader.NodeType != XmlNodeType.Element) {
259 PropertyInformation prop = ElementInformation.Properties [reader.LocalName];
260 if (prop == null || (serializeCollectionKey && !prop.IsKey)) {
261 if (!OnDeserializeUnrecognizedElement (reader.LocalName, reader))
262 throw new ConfigurationException ("Unrecognized element '" + reader.LocalName + "'.");
267 throw new ConfigurationException ("Property '" + prop.Name + "' is not a ConfigurationElement.");
269 if (readProps.Contains (prop))
270 throw new ConfigurationException ("The element <" + prop.Name + "> may only appear once in this section.");
272 ConfigurationElement val = (ConfigurationElement) prop.Value;
273 val.DeserializeElement (reader, serializeCollectionKey);
274 readProps [prop] = prop.Name;
280 foreach (PropertyInformation prop in ElementInformation.Properties)
281 if (prop.IsRequired && !readProps.ContainsKey (prop)) {
282 object val = OnRequiredPropertyNotFound (prop.Name);
283 if (!object.Equals (val, prop.DefaultValue)) {
285 prop.IsModified = false;
292 protected virtual bool OnDeserializeUnrecognizedAttribute (string name, string value)
297 protected virtual bool OnDeserializeUnrecognizedElement (string element, XmlReader reader)
302 protected virtual object OnRequiredPropertyNotFound (string name)
304 throw new ConfigurationErrorsException ("Required attribute '" + name + "' not found.");
307 protected virtual void PreSerialize (XmlWriter writer)
311 protected virtual void PostDeserialize ()
315 protected internal virtual void InitializeDefault ()
319 protected internal virtual bool IsModified ()
324 protected internal virtual void SetReadOnly ()
329 public virtual bool IsReadOnly ()
334 protected internal virtual void Reset (ConfigurationElement parentElement)
336 if (parentElement != null)
337 ElementInformation.Reset (parentElement.ElementInformation);
339 InitializeDefault ();
342 protected internal virtual void ResetModified ()
345 foreach (PropertyInformation p in ElementInformation.Properties)
346 p.IsModified = false;
349 protected internal virtual bool SerializeElement (XmlWriter writer, bool serializeCollectionKey)
351 PreSerialize (writer);
353 if (serializeCollectionKey) {
354 ConfigurationPropertyCollection props = GetKeyProperties ();
355 foreach (ConfigurationProperty prop in props)
356 writer.WriteAttributeString (prop.Name, prop.ConvertToString (this[prop.Name]));
357 return props.Count > 0;
360 bool wroteData = false;
362 foreach (PropertyInformation prop in ElementInformation.Properties)
364 if (prop.IsElement || prop.ValueOrigin == PropertyValueOrigin.Default)
367 if (!object.Equals (prop.Value, prop.DefaultValue)) {
368 writer.WriteAttributeString (prop.Name, prop.GetStringValue ());
373 foreach (PropertyInformation prop in ElementInformation.Properties)
378 ConfigurationElement val = (ConfigurationElement) prop.Value;
379 if (val != null && val.HasValues ()) {
380 wroteData = val.SerializeToXmlElement (writer, prop.Name) || wroteData;
386 protected internal virtual bool SerializeToXmlElement (
387 XmlWriter writer, string elementName)
389 writer.WriteStartElement (elementName);
390 bool res = SerializeElement (writer, false);
391 writer.WriteEndElement ();
395 protected internal virtual void Unmerge (
396 ConfigurationElement source, ConfigurationElement parent,
397 ConfigurationSaveMode updateMode)
399 if (parent != null && source.GetType() != parent.GetType())
400 throw new ConfigurationException ("Can't unmerge two elements of different type");
402 foreach (PropertyInformation prop in source.ElementInformation.Properties)
404 if (prop.ValueOrigin == PropertyValueOrigin.Default)
407 PropertyInformation unmergedProp = ElementInformation.Properties [prop.Name];
409 object sourceValue = prop.Value;
410 if (parent == null || !parent.HasValue (prop.Name)) {
411 unmergedProp.Value = sourceValue;
414 else if (sourceValue != null) {
415 object parentValue = parent [prop.Name];
416 if (prop.IsElement) {
417 if (parentValue != null) {
418 ConfigurationElement copy = (ConfigurationElement) unmergedProp.Value;
419 copy.Unmerge ((ConfigurationElement) sourceValue, (ConfigurationElement) parentValue, updateMode);
422 unmergedProp.Value = sourceValue;
425 if (!object.Equals (sourceValue, parentValue) ||
426 (updateMode == ConfigurationSaveMode.Full) ||
427 (updateMode == ConfigurationSaveMode.Modified && prop.ValueOrigin == PropertyValueOrigin.SetHere))
428 unmergedProp.Value = sourceValue;
434 internal bool HasValue (string propName)
436 PropertyInformation info = ElementInformation.Properties [propName];
437 return info != null && info.ValueOrigin != PropertyValueOrigin.Default;
440 internal bool IsReadFromConfig (string propName)
442 PropertyInformation info = ElementInformation.Properties [propName];
443 return info != null && info.ValueOrigin == PropertyValueOrigin.SetHere;
447 internal class ElementMap
449 static Hashtable elementMaps = new Hashtable ();
451 ConfigurationPropertyCollection properties;
452 ConfigurationPropertyCollection keyProperties;
454 ConfigurationCollectionAttribute collectionAttribute;
456 public static ElementMap GetMap (Type t)
459 ElementMap map = elementMaps [t] as ElementMap;
460 if (map != null) return map;
461 map = new ElementMap (t);
462 elementMaps [t] = map;
467 public ElementMap (Type t)
469 ReflectProperties (t);
472 protected void ReflectProperties (Type t)
474 collectionAttribute = Attribute.GetCustomAttribute (t, typeof(ConfigurationCollectionAttribute)) as ConfigurationCollectionAttribute;
476 PropertyInfo[] props = t.GetProperties ();
477 foreach (PropertyInfo prop in props)
479 ConfigurationPropertyAttribute at = Attribute.GetCustomAttribute (prop, typeof(ConfigurationPropertyAttribute)) as ConfigurationPropertyAttribute;
480 if (at == null) continue;
481 string name = at.Name != null ? at.Name : prop.Name;
484 /* if we have no default value, don't bother to check further */
485 at.DefaultValue != null && at.DefaultValue != ConfigurationProperty.NoDefaultValue
489 Convert.ChangeType (at.DefaultValue, prop.PropertyType);
492 throw new ConfigurationErrorsException (String.Format ("The default value for property '{0}' has a different type than the one of the property itself",
497 ConfigurationValidatorAttribute validatorAttr = Attribute.GetCustomAttribute (t, typeof(ConfigurationValidatorAttribute)) as ConfigurationValidatorAttribute;
498 ConfigurationValidatorBase validator = validatorAttr != null ? validatorAttr.ValidatorInstance : new DefaultValidator();
500 TypeConverter converter = TypeDescriptor.GetConverter (prop.PropertyType);
501 ConfigurationProperty cp = new ConfigurationProperty (name, prop.PropertyType, at.DefaultValue, converter, validator, at.Options);
503 cp.CollectionAttribute = Attribute.GetCustomAttribute (prop, typeof(ConfigurationCollectionAttribute)) as ConfigurationCollectionAttribute;
505 if (properties == null) properties = new ConfigurationPropertyCollection ();
510 public bool HasProperties
512 get { return properties != null && properties.Count > 0; }
515 public ConfigurationPropertyCollection Properties
518 if (properties == null) properties = new ConfigurationPropertyCollection ();
523 public ConfigurationPropertyCollection KeyProperties {
525 if (keyProperties == null) {
526 keyProperties = new ConfigurationPropertyCollection ();
528 if (properties != null)
529 foreach (ConfigurationProperty p in properties)
530 if (p.IsKey) keyProperties.Add (p);
532 return keyProperties;
536 public ConfigurationCollectionAttribute CollectionAttribute {
537 get { return collectionAttribute; }