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;
45 ConfigurationElementCollection defaultCollection;
47 ElementInformation elementInfo;
48 ConfigurationElementProperty elementProperty;
50 protected ConfigurationElement ()
54 internal virtual void InitFromProperty (PropertyInformation propertyInfo)
56 elementInfo = new ElementInformation (this, propertyInfo);
60 public ElementInformation ElementInformation {
62 if (elementInfo == null)
63 elementInfo = new ElementInformation (this, null);
68 internal string RawXml {
69 get { return rawXml; }
70 set { rawXml = value; }
73 protected internal virtual void Init ()
77 public ConfigurationElementProperty ElementProperty {
79 if (elementProperty == null)
80 elementProperty = new ConfigurationElementProperty (ElementInformation.Validator);
81 return elementProperty;
86 protected ContextInformation EvaluationContext {
88 throw new NotImplementedException ();
93 public ConfigurationLockCollection LockAllAttributesExcept {
95 throw new NotImplementedException ();
100 public ConfigurationLockCollection LockAllElementsExcept {
102 throw new NotImplementedException ();
107 ConfigurationLockCollection lockAttributes;
108 public ConfigurationLockCollection LockAttributes {
110 if (lockAttributes == null) {
111 lockAttributes = new ConfigurationLockCollection (this, ConfigurationLockType.Attribute);
114 return lockAttributes;
119 public ConfigurationLockCollection LockElements {
121 throw new NotImplementedException ();
126 public bool LockItem {
128 throw new NotImplementedException ();
131 throw new NotImplementedException ();
136 public void ListErrors (IList list)
138 throw new NotImplementedException ();
142 public void SetPropertyValue (ConfigurationProperty prop, object value, bool ignoreLocks)
144 throw new NotImplementedException ();
147 internal ConfigurationPropertyCollection GetKeyProperties ()
149 if (keyProps != null) return keyProps;
151 if (map != null && map.Properties == Properties)
152 keyProps = map.KeyProperties;
154 keyProps = new ConfigurationPropertyCollection ();
155 foreach (ConfigurationProperty prop in Properties) {
163 internal ConfigurationElementCollection GetDefaultCollection ()
165 if (defaultCollection != null) return defaultCollection;
167 ConfigurationProperty defaultCollectionProp = null;
169 if (map != null && map.Properties == Properties) {
170 defaultCollectionProp = map.DefaultCollectionProperty;
173 foreach (ConfigurationProperty prop in Properties) {
174 if (prop.IsDefaultCollection) {
175 defaultCollectionProp = prop;
181 if (defaultCollectionProp != null) {
182 defaultCollection = this [defaultCollectionProp] as ConfigurationElementCollection;
185 return defaultCollection;
188 protected internal object this [ConfigurationProperty property] {
189 get { return this [property.Name]; }
190 set { this [property.Name] = value; }
193 protected internal object this [string property_name] {
195 PropertyInformation pi = ElementInformation.Properties [property_name];
197 throw new InvalidOperationException ("Property '" + property_name + "' not found in configuration element");
203 PropertyInformation pi = ElementInformation.Properties [property_name];
205 throw new InvalidOperationException ("Property '" + property_name + "' not found in configuration element");
212 protected internal virtual ConfigurationPropertyCollection Properties {
215 map = ElementMap.GetMap (GetType());
216 return map.Properties;
220 public override bool Equals (object compareTo)
222 ConfigurationElement other = compareTo as ConfigurationElement;
223 if (other == null) return false;
224 if (GetType() != other.GetType()) return false;
226 foreach (ConfigurationProperty prop in Properties) {
227 if (!object.Equals (this [prop], other [prop]))
233 public override int GetHashCode ()
236 foreach (ConfigurationProperty prop in Properties)
237 code += this [prop].GetHashCode ();
241 internal virtual bool HasValues ()
243 foreach (PropertyInformation pi in ElementInformation.Properties)
244 if (pi.ValueOrigin != PropertyValueOrigin.Default)
249 protected internal virtual void DeserializeElement (XmlReader reader, bool serializeCollectionKey)
251 Hashtable readProps = new Hashtable ();
253 reader.MoveToContent ();
254 while (reader.MoveToNextAttribute ())
256 PropertyInformation prop = ElementInformation.Properties [reader.LocalName];
257 if (prop == null || (serializeCollectionKey && !prop.IsKey)) {
258 if (!OnDeserializeUnrecognizedAttribute (reader.LocalName, reader.Value))
259 throw new ConfigurationException ("Unrecognized attribute '" + reader.LocalName + "'.");
263 if (readProps.ContainsKey (prop))
264 throw new ConfigurationException ("The attribute '" + prop.Name + "' may only appear once in this element.");
266 prop.SetStringValue (reader.Value);
267 readProps [prop] = prop.Name;
270 reader.MoveToElement ();
271 if (reader.IsEmptyElement) {
276 int depth = reader.Depth;
278 reader.ReadStartElement ();
279 reader.MoveToContent ();
282 if (reader.NodeType != XmlNodeType.Element) {
287 PropertyInformation prop = ElementInformation.Properties [reader.LocalName];
288 if (prop == null || (serializeCollectionKey && !prop.IsKey)) {
290 ConfigurationElementCollection c = GetDefaultCollection ();
291 if (c != null && c.OnDeserializeUnrecognizedElement (reader.LocalName, reader))
295 if (!OnDeserializeUnrecognizedElement (reader.LocalName, reader)) {
296 throw new ConfigurationException ("Unrecognized element '" + reader.LocalName + "'.");
302 throw new ConfigurationException ("Property '" + prop.Name + "' is not a ConfigurationElement.");
304 if (readProps.Contains (prop))
305 throw new ConfigurationException ("The element <" + prop.Name + "> may only appear once in this section.");
307 ConfigurationElement val = (ConfigurationElement) prop.Value;
308 val.DeserializeElement (reader, serializeCollectionKey);
309 readProps [prop] = prop.Name;
313 } while (depth < reader.Depth);
315 if (reader.NodeType == XmlNodeType.EndElement)
321 foreach (PropertyInformation prop in ElementInformation.Properties)
322 if (prop.IsRequired && !readProps.ContainsKey (prop)) {
323 object val = OnRequiredPropertyNotFound (prop.Name);
324 if (!object.Equals (val, prop.DefaultValue)) {
326 prop.IsModified = false;
333 protected virtual bool OnDeserializeUnrecognizedAttribute (string name, string value)
338 protected virtual bool OnDeserializeUnrecognizedElement (string element, XmlReader reader)
343 protected virtual object OnRequiredPropertyNotFound (string name)
345 throw new ConfigurationErrorsException ("Required attribute '" + name + "' not found.");
348 protected virtual void PreSerialize (XmlWriter writer)
352 protected virtual void PostDeserialize ()
356 protected internal virtual void InitializeDefault ()
360 protected internal virtual bool IsModified ()
365 protected internal virtual void SetReadOnly ()
370 public virtual bool IsReadOnly ()
375 protected internal virtual void Reset (ConfigurationElement parentElement)
377 if (parentElement != null)
378 ElementInformation.Reset (parentElement.ElementInformation);
380 InitializeDefault ();
383 protected internal virtual void ResetModified ()
386 foreach (PropertyInformation p in ElementInformation.Properties)
387 p.IsModified = false;
390 protected internal virtual bool SerializeElement (XmlWriter writer, bool serializeCollectionKey)
392 PreSerialize (writer);
394 if (serializeCollectionKey) {
395 ConfigurationPropertyCollection props = GetKeyProperties ();
396 foreach (ConfigurationProperty prop in props)
397 writer.WriteAttributeString (prop.Name, prop.ConvertToString (this[prop.Name]));
398 return props.Count > 0;
401 bool wroteData = false;
403 foreach (PropertyInformation prop in ElementInformation.Properties)
405 if (prop.IsElement || prop.ValueOrigin == PropertyValueOrigin.Default)
408 if (!object.Equals (prop.Value, prop.DefaultValue)) {
409 writer.WriteAttributeString (prop.Name, prop.GetStringValue ());
414 foreach (PropertyInformation prop in ElementInformation.Properties)
419 ConfigurationElement val = (ConfigurationElement) prop.Value;
420 if (val != null && val.HasValues ()) {
421 wroteData = val.SerializeToXmlElement (writer, prop.Name) || wroteData;
427 protected internal virtual bool SerializeToXmlElement (
428 XmlWriter writer, string elementName)
430 writer.WriteStartElement (elementName);
431 bool res = SerializeElement (writer, false);
432 writer.WriteEndElement ();
436 protected internal virtual void Unmerge (
437 ConfigurationElement source, ConfigurationElement parent,
438 ConfigurationSaveMode updateMode)
440 if (parent != null && source.GetType() != parent.GetType())
441 throw new ConfigurationException ("Can't unmerge two elements of different type");
443 foreach (PropertyInformation prop in source.ElementInformation.Properties)
445 if (prop.ValueOrigin == PropertyValueOrigin.Default)
448 PropertyInformation unmergedProp = ElementInformation.Properties [prop.Name];
450 object sourceValue = prop.Value;
451 if (parent == null || !parent.HasValue (prop.Name)) {
452 unmergedProp.Value = sourceValue;
455 else if (sourceValue != null) {
456 object parentValue = parent [prop.Name];
457 if (prop.IsElement) {
458 if (parentValue != null) {
459 ConfigurationElement copy = (ConfigurationElement) unmergedProp.Value;
460 copy.Unmerge ((ConfigurationElement) sourceValue, (ConfigurationElement) parentValue, updateMode);
463 unmergedProp.Value = sourceValue;
466 if (!object.Equals (sourceValue, parentValue) ||
467 (updateMode == ConfigurationSaveMode.Full) ||
468 (updateMode == ConfigurationSaveMode.Modified && prop.ValueOrigin == PropertyValueOrigin.SetHere))
469 unmergedProp.Value = sourceValue;
475 internal bool HasValue (string propName)
477 PropertyInformation info = ElementInformation.Properties [propName];
478 return info != null && info.ValueOrigin != PropertyValueOrigin.Default;
481 internal bool IsReadFromConfig (string propName)
483 PropertyInformation info = ElementInformation.Properties [propName];
484 return info != null && info.ValueOrigin == PropertyValueOrigin.SetHere;
488 internal class ElementMap
490 static Hashtable elementMaps = new Hashtable ();
492 ConfigurationPropertyCollection properties;
493 ConfigurationPropertyCollection keyProperties;
494 ConfigurationProperty defaultCollectionProperty;
496 ConfigurationCollectionAttribute collectionAttribute;
498 public static ElementMap GetMap (Type t)
501 ElementMap map = elementMaps [t] as ElementMap;
502 if (map != null) return map;
503 map = new ElementMap (t);
504 elementMaps [t] = map;
509 public ElementMap (Type t)
511 ReflectProperties (t);
514 protected void ReflectProperties (Type t)
516 collectionAttribute = Attribute.GetCustomAttribute (t, typeof(ConfigurationCollectionAttribute)) as ConfigurationCollectionAttribute;
518 PropertyInfo[] props = t.GetProperties ();
519 foreach (PropertyInfo prop in props)
521 ConfigurationPropertyAttribute at = Attribute.GetCustomAttribute (prop, typeof(ConfigurationPropertyAttribute)) as ConfigurationPropertyAttribute;
522 if (at == null) continue;
523 string name = at.Name != null ? at.Name : prop.Name;
526 /* if we have no default value, don't bother to check further */
527 at.DefaultValue != null && at.DefaultValue != ConfigurationProperty.NoDefaultValue
531 Convert.ChangeType (at.DefaultValue, prop.PropertyType);
534 throw new ConfigurationErrorsException (String.Format ("The default value for property '{0}' has a different type than the one of the property itself",
539 ConfigurationValidatorAttribute validatorAttr = Attribute.GetCustomAttribute (t, typeof(ConfigurationValidatorAttribute)) as ConfigurationValidatorAttribute;
540 ConfigurationValidatorBase validator = validatorAttr != null ? validatorAttr.ValidatorInstance : new DefaultValidator();
542 TypeConverter converter = TypeDescriptor.GetConverter (prop.PropertyType);
543 ConfigurationProperty cp = new ConfigurationProperty (name, prop.PropertyType, at.DefaultValue, converter, validator, at.Options);
545 cp.CollectionAttribute = Attribute.GetCustomAttribute (prop, typeof(ConfigurationCollectionAttribute)) as ConfigurationCollectionAttribute;
547 if (properties == null) properties = new ConfigurationPropertyCollection ();
552 public bool HasProperties
554 get { return properties != null && properties.Count > 0; }
557 public ConfigurationPropertyCollection Properties
560 if (properties == null) properties = new ConfigurationPropertyCollection ();
565 public ConfigurationPropertyCollection KeyProperties {
567 if (keyProperties == null) {
568 keyProperties = new ConfigurationPropertyCollection ();
570 if (properties != null)
571 foreach (ConfigurationProperty p in properties)
572 if (p.IsKey) keyProperties.Add (p);
574 return keyProperties;
578 public ConfigurationCollectionAttribute CollectionAttribute {
579 get { return collectionAttribute; }
582 public ConfigurationProperty DefaultCollectionProperty {
584 if (defaultCollectionProperty == null) {
585 if (properties != null)
586 foreach (ConfigurationProperty p in properties) {
587 if (p.IsDefaultCollection) defaultCollectionProperty = p;
591 return defaultCollectionProperty;