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; }
71 // FIXME: this hack is nasty. We should make
72 // some refactory on the entire assembly.
73 if (rawXml == null || value != null)
78 protected internal virtual void Init ()
82 protected internal virtual ConfigurationElementProperty ElementProperty {
84 if (elementProperty == null)
85 elementProperty = new ConfigurationElementProperty (ElementInformation.Validator);
86 return elementProperty;
91 protected ContextInformation EvaluationContext {
93 throw new NotImplementedException ();
97 ConfigurationLockCollection lockAllAttributesExcept;
98 public ConfigurationLockCollection LockAllAttributesExcept {
100 if (lockAllAttributesExcept == null) {
101 lockAllAttributesExcept = new ConfigurationLockCollection (this, ConfigurationLockType.Attribute | ConfigurationLockType.Exclude);
104 return lockAllAttributesExcept;
108 ConfigurationLockCollection lockAllElementsExcept;
109 public ConfigurationLockCollection LockAllElementsExcept {
111 if (lockAllElementsExcept == null) {
112 lockAllElementsExcept = new ConfigurationLockCollection (this, ConfigurationLockType.Element | ConfigurationLockType.Exclude);
115 return lockAllElementsExcept;
119 ConfigurationLockCollection lockAttributes;
120 public ConfigurationLockCollection LockAttributes {
122 if (lockAttributes == null) {
123 lockAttributes = new ConfigurationLockCollection (this, ConfigurationLockType.Attribute);
126 return lockAttributes;
130 ConfigurationLockCollection lockElements;
131 public ConfigurationLockCollection LockElements {
133 if (lockElements == null) {
134 lockElements = new ConfigurationLockCollection (this, ConfigurationLockType.Element);
142 public bool LockItem {
143 get { return lockItem; }
144 set { lockItem = value; }
148 protected virtual void ListErrors (IList list)
150 throw new NotImplementedException ();
154 protected void SetPropertyValue (ConfigurationProperty prop, object value, bool ignoreLocks)
157 /* XXX all i know for certain is that Validation happens here */
158 prop.Validate (value);
160 /* XXX presumably the actual setting of the
161 * property happens here instead of in the
162 * set_Item code below, but that would mean
163 * the Value needs to be stuffed in the
164 * property, not the propertyinfo (or else the
165 * property needs a ref to the property info
166 * to correctly set the value). */
168 catch (Exception e) {
169 throw new ConfigurationErrorsException (String.Format ("The value for the property '{0}' is not valid. The error is: {1}", prop.Name, e.Message), e);
173 internal ConfigurationPropertyCollection GetKeyProperties ()
175 if (keyProps != null) return keyProps;
177 if (map != null && map.Properties == Properties)
178 keyProps = map.KeyProperties;
180 keyProps = new ConfigurationPropertyCollection ();
181 foreach (ConfigurationProperty prop in Properties) {
189 internal ConfigurationElementCollection GetDefaultCollection ()
191 if (defaultCollection != null) return defaultCollection;
193 ConfigurationProperty defaultCollectionProp = null;
195 if (map != null && map.Properties == Properties) {
196 defaultCollectionProp = map.DefaultCollectionProperty;
199 foreach (ConfigurationProperty prop in Properties) {
200 if (prop.IsDefaultCollection) {
201 defaultCollectionProp = prop;
207 if (defaultCollectionProp != null) {
208 defaultCollection = this [defaultCollectionProp] as ConfigurationElementCollection;
211 return defaultCollection;
214 protected internal object this [ConfigurationProperty property] {
215 get { return this [property.Name]; }
216 set { this [property.Name] = value; }
219 protected internal object this [string property_name] {
221 PropertyInformation pi = ElementInformation.Properties [property_name];
223 throw new InvalidOperationException ("Property '" + property_name + "' not found in configuration element");
229 PropertyInformation pi = ElementInformation.Properties [property_name];
231 throw new InvalidOperationException ("Property '" + property_name + "' not found in configuration element");
233 SetPropertyValue (pi.Property, value, false);
240 protected internal virtual ConfigurationPropertyCollection Properties {
243 map = ElementMap.GetMap (GetType());
244 return map.Properties;
248 public override bool Equals (object compareTo)
250 ConfigurationElement other = compareTo as ConfigurationElement;
251 if (other == null) return false;
252 if (GetType() != other.GetType()) return false;
254 foreach (ConfigurationProperty prop in Properties) {
255 if (!object.Equals (this [prop], other [prop]))
261 public override int GetHashCode ()
264 foreach (ConfigurationProperty prop in Properties)
265 code += this [prop].GetHashCode ();
269 internal virtual bool HasValues ()
271 foreach (PropertyInformation pi in ElementInformation.Properties)
272 if (pi.ValueOrigin != PropertyValueOrigin.Default)
277 protected internal virtual void DeserializeElement (XmlReader reader, bool serializeCollectionKey)
279 Hashtable readProps = new Hashtable ();
281 reader.MoveToContent ();
282 while (reader.MoveToNextAttribute ())
284 PropertyInformation prop = ElementInformation.Properties [reader.LocalName];
285 if (prop == null || (serializeCollectionKey && !prop.IsKey)) {
286 /* handle the built in ConfigurationElement attributes here */
287 if (reader.LocalName == "lockAllAttributesExcept") {
288 LockAllAttributesExcept.SetFromList (reader.Value);
290 else if (reader.LocalName == "lockAllElementsExcept") {
291 LockAllElementsExcept.SetFromList (reader.Value);
293 else if (reader.LocalName == "lockAttributes") {
294 LockAttributes.SetFromList (reader.Value);
296 else if (reader.LocalName == "lockElements") {
297 LockElements.SetFromList (reader.Value);
299 else if (reader.LocalName == "lockItem") {
300 LockItem = (reader.Value.ToLower() == "true");
302 else if (reader.LocalName == "xmlns") {
305 else if (!OnDeserializeUnrecognizedAttribute (reader.LocalName, reader.Value))
306 throw new ConfigurationException ("Unrecognized attribute '" + reader.LocalName + "'.");
311 if (readProps.ContainsKey (prop))
312 throw new ConfigurationException ("The attribute '" + prop.Name + "' may only appear once in this element.");
314 prop.SetStringValue (reader.Value);
315 readProps [prop] = prop.Name;
318 reader.MoveToElement ();
319 if (reader.IsEmptyElement) {
324 int depth = reader.Depth;
326 reader.ReadStartElement ();
327 reader.MoveToContent ();
330 if (reader.NodeType != XmlNodeType.Element) {
335 PropertyInformation prop = ElementInformation.Properties [reader.LocalName];
336 if (prop == null || (serializeCollectionKey && !prop.IsKey)) {
337 if (!OnDeserializeUnrecognizedElement (reader.LocalName, reader)) {
339 ConfigurationElementCollection c = GetDefaultCollection ();
340 if (c != null && c.OnDeserializeUnrecognizedElement (reader.LocalName, reader))
343 throw new ConfigurationException ("Unrecognized element '" + reader.LocalName + "'.");
349 throw new ConfigurationException ("Property '" + prop.Name + "' is not a ConfigurationElement.");
351 if (readProps.Contains (prop))
352 throw new ConfigurationException ("The element <" + prop.Name + "> may only appear once in this section.");
354 ConfigurationElement val = (ConfigurationElement) prop.Value;
355 val.DeserializeElement (reader, serializeCollectionKey);
356 readProps [prop] = prop.Name;
360 } while (depth < reader.Depth);
362 if (reader.NodeType == XmlNodeType.EndElement)
368 foreach (PropertyInformation prop in ElementInformation.Properties)
369 if (!String.IsNullOrEmpty(prop.Name) && prop.IsRequired && !readProps.ContainsKey (prop)) {
370 object val = OnRequiredPropertyNotFound (prop.Name);
371 if (!object.Equals (val, prop.DefaultValue)) {
373 prop.IsModified = false;
380 protected virtual bool OnDeserializeUnrecognizedAttribute (string name, string value)
385 protected virtual bool OnDeserializeUnrecognizedElement (string element, XmlReader reader)
390 protected virtual object OnRequiredPropertyNotFound (string name)
392 throw new ConfigurationErrorsException ("Required attribute '" + name + "' not found.");
395 protected virtual void PreSerialize (XmlWriter writer)
399 protected virtual void PostDeserialize ()
403 protected internal virtual void InitializeDefault ()
407 protected internal virtual bool IsModified ()
412 protected internal virtual void SetReadOnly ()
417 public virtual bool IsReadOnly ()
422 protected internal virtual void Reset (ConfigurationElement parentElement)
424 if (parentElement != null)
425 ElementInformation.Reset (parentElement.ElementInformation);
427 InitializeDefault ();
430 protected internal virtual void ResetModified ()
433 foreach (PropertyInformation p in ElementInformation.Properties)
434 p.IsModified = false;
437 protected internal virtual bool SerializeElement (XmlWriter writer, bool serializeCollectionKey)
439 PreSerialize (writer);
441 if (serializeCollectionKey) {
442 ConfigurationPropertyCollection props = GetKeyProperties ();
443 foreach (ConfigurationProperty prop in props)
444 writer.WriteAttributeString (prop.Name, prop.ConvertToString (this[prop.Name]));
445 return props.Count > 0;
448 bool wroteData = false;
450 foreach (PropertyInformation prop in ElementInformation.Properties)
452 if (prop.IsElement || prop.ValueOrigin == PropertyValueOrigin.Default)
455 if (!object.Equals (prop.Value, prop.DefaultValue)) {
456 writer.WriteAttributeString (prop.Name, prop.GetStringValue ());
461 foreach (PropertyInformation prop in ElementInformation.Properties)
466 ConfigurationElement val = (ConfigurationElement) prop.Value;
467 if (val != null && val.HasValues ()) {
468 wroteData = val.SerializeToXmlElement (writer, prop.Name) || wroteData;
474 protected internal virtual bool SerializeToXmlElement (
475 XmlWriter writer, string elementName)
477 if (elementName != null && elementName != "")
478 writer.WriteStartElement (elementName);
479 bool res = SerializeElement (writer, false);
480 if (elementName != null && elementName != "")
481 writer.WriteEndElement ();
485 protected internal virtual void Unmerge (
486 ConfigurationElement source, ConfigurationElement parent,
487 ConfigurationSaveMode updateMode)
489 if (parent != null && source.GetType() != parent.GetType())
490 throw new ConfigurationException ("Can't unmerge two elements of different type");
492 foreach (PropertyInformation prop in source.ElementInformation.Properties)
494 if (prop.ValueOrigin == PropertyValueOrigin.Default)
497 PropertyInformation unmergedProp = ElementInformation.Properties [prop.Name];
499 object sourceValue = prop.Value;
500 if (parent == null || !parent.HasValue (prop.Name)) {
501 unmergedProp.Value = sourceValue;
504 else if (sourceValue != null) {
505 object parentValue = parent [prop.Name];
506 if (prop.IsElement) {
507 if (parentValue != null) {
508 ConfigurationElement copy = (ConfigurationElement) unmergedProp.Value;
509 copy.Unmerge ((ConfigurationElement) sourceValue, (ConfigurationElement) parentValue, updateMode);
512 unmergedProp.Value = sourceValue;
515 if (!object.Equals (sourceValue, parentValue) ||
516 (updateMode == ConfigurationSaveMode.Full) ||
517 (updateMode == ConfigurationSaveMode.Modified && prop.ValueOrigin == PropertyValueOrigin.SetHere))
518 unmergedProp.Value = sourceValue;
524 internal bool HasValue (string propName)
526 PropertyInformation info = ElementInformation.Properties [propName];
527 return info != null && info.ValueOrigin != PropertyValueOrigin.Default;
530 internal bool IsReadFromConfig (string propName)
532 PropertyInformation info = ElementInformation.Properties [propName];
533 return info != null && info.ValueOrigin == PropertyValueOrigin.SetHere;
537 internal class ElementMap
539 static Hashtable elementMaps = new Hashtable ();
541 ConfigurationPropertyCollection properties;
542 ConfigurationPropertyCollection keyProperties;
543 ConfigurationProperty defaultCollectionProperty;
545 ConfigurationCollectionAttribute collectionAttribute;
547 public static ElementMap GetMap (Type t)
550 ElementMap map = elementMaps [t] as ElementMap;
551 if (map != null) return map;
552 map = new ElementMap (t);
553 elementMaps [t] = map;
558 public ElementMap (Type t)
560 ReflectProperties (t);
563 protected void ReflectProperties (Type t)
565 collectionAttribute = Attribute.GetCustomAttribute (t, typeof(ConfigurationCollectionAttribute)) as ConfigurationCollectionAttribute;
567 PropertyInfo[] props = t.GetProperties ();
568 foreach (PropertyInfo prop in props)
570 ConfigurationPropertyAttribute at = Attribute.GetCustomAttribute (prop, typeof(ConfigurationPropertyAttribute)) as ConfigurationPropertyAttribute;
571 if (at == null) continue;
572 string name = at.Name != null ? at.Name : prop.Name;
574 ConfigurationValidatorAttribute validatorAttr = Attribute.GetCustomAttribute (t, typeof(ConfigurationValidatorAttribute)) as ConfigurationValidatorAttribute;
575 ConfigurationValidatorBase validator = validatorAttr != null ? validatorAttr.ValidatorInstance : null;
577 TypeConverterAttribute convertAttr = (TypeConverterAttribute) Attribute.GetCustomAttribute (t, typeof (TypeConverterAttribute));
578 TypeConverter converter = convertAttr != null ? (TypeConverter) Activator.CreateInstance (Type.GetType (convertAttr.ConverterTypeName)) : null;
579 ConfigurationProperty cp = new ConfigurationProperty (name, prop.PropertyType, at.DefaultValue, converter, validator, at.Options);
581 cp.CollectionAttribute = Attribute.GetCustomAttribute (prop, typeof(ConfigurationCollectionAttribute)) as ConfigurationCollectionAttribute;
583 if (properties == null) properties = new ConfigurationPropertyCollection ();
588 public bool HasProperties
590 get { return properties != null && properties.Count > 0; }
593 public ConfigurationPropertyCollection Properties
596 if (properties == null) properties = new ConfigurationPropertyCollection ();
601 public ConfigurationPropertyCollection KeyProperties {
603 if (keyProperties == null) {
604 keyProperties = new ConfigurationPropertyCollection ();
606 if (properties != null)
607 foreach (ConfigurationProperty p in properties)
608 if (p.IsKey) keyProperties.Add (p);
610 return keyProperties;
614 public ConfigurationCollectionAttribute CollectionAttribute {
615 get { return collectionAttribute; }
618 public ConfigurationProperty DefaultCollectionProperty {
620 if (defaultCollectionProperty == null) {
621 if (properties != null)
622 foreach (ConfigurationProperty p in properties) {
623 if (p.IsDefaultCollection) {
624 defaultCollectionProperty = p;
629 return defaultCollectionProperty;