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)) {
338 ConfigurationElementCollection c = GetDefaultCollection ();
339 if (c != null && c.OnDeserializeUnrecognizedElement (reader.LocalName, reader))
343 if (!OnDeserializeUnrecognizedElement (reader.LocalName, reader)) {
344 throw new ConfigurationException ("Unrecognized element '" + reader.LocalName + "'.");
350 throw new ConfigurationException ("Property '" + prop.Name + "' is not a ConfigurationElement.");
352 if (readProps.Contains (prop))
353 throw new ConfigurationException ("The element <" + prop.Name + "> may only appear once in this section.");
355 ConfigurationElement val = (ConfigurationElement) prop.Value;
356 val.DeserializeElement (reader, serializeCollectionKey);
357 readProps [prop] = prop.Name;
361 } while (depth < reader.Depth);
363 if (reader.NodeType == XmlNodeType.EndElement)
369 foreach (PropertyInformation prop in ElementInformation.Properties)
370 if (prop.IsRequired && !readProps.ContainsKey (prop)) {
371 object val = OnRequiredPropertyNotFound (prop.Name);
372 if (!object.Equals (val, prop.DefaultValue)) {
374 prop.IsModified = false;
381 protected virtual bool OnDeserializeUnrecognizedAttribute (string name, string value)
386 protected virtual bool OnDeserializeUnrecognizedElement (string element, XmlReader reader)
391 protected virtual object OnRequiredPropertyNotFound (string name)
393 throw new ConfigurationErrorsException ("Required attribute '" + name + "' not found.");
396 protected virtual void PreSerialize (XmlWriter writer)
400 protected virtual void PostDeserialize ()
404 protected internal virtual void InitializeDefault ()
408 protected internal virtual bool IsModified ()
413 protected internal virtual void SetReadOnly ()
418 public virtual bool IsReadOnly ()
423 protected internal virtual void Reset (ConfigurationElement parentElement)
425 if (parentElement != null)
426 ElementInformation.Reset (parentElement.ElementInformation);
428 InitializeDefault ();
431 protected internal virtual void ResetModified ()
434 foreach (PropertyInformation p in ElementInformation.Properties)
435 p.IsModified = false;
438 protected internal virtual bool SerializeElement (XmlWriter writer, bool serializeCollectionKey)
440 PreSerialize (writer);
442 if (serializeCollectionKey) {
443 ConfigurationPropertyCollection props = GetKeyProperties ();
444 foreach (ConfigurationProperty prop in props)
445 writer.WriteAttributeString (prop.Name, prop.ConvertToString (this[prop.Name]));
446 return props.Count > 0;
449 bool wroteData = false;
451 foreach (PropertyInformation prop in ElementInformation.Properties)
453 if (prop.IsElement || prop.ValueOrigin == PropertyValueOrigin.Default)
456 if (!object.Equals (prop.Value, prop.DefaultValue)) {
457 writer.WriteAttributeString (prop.Name, prop.GetStringValue ());
462 foreach (PropertyInformation prop in ElementInformation.Properties)
467 ConfigurationElement val = (ConfigurationElement) prop.Value;
468 if (val != null && val.HasValues ()) {
469 wroteData = val.SerializeToXmlElement (writer, prop.Name) || wroteData;
475 protected internal virtual bool SerializeToXmlElement (
476 XmlWriter writer, string elementName)
478 if (elementName != null && elementName != "")
479 writer.WriteStartElement (elementName);
480 bool res = SerializeElement (writer, false);
481 if (elementName != null && elementName != "")
482 writer.WriteEndElement ();
486 protected internal virtual void Unmerge (
487 ConfigurationElement source, ConfigurationElement parent,
488 ConfigurationSaveMode updateMode)
490 if (parent != null && source.GetType() != parent.GetType())
491 throw new ConfigurationException ("Can't unmerge two elements of different type");
493 foreach (PropertyInformation prop in source.ElementInformation.Properties)
495 if (prop.ValueOrigin == PropertyValueOrigin.Default)
498 PropertyInformation unmergedProp = ElementInformation.Properties [prop.Name];
500 object sourceValue = prop.Value;
501 if (parent == null || !parent.HasValue (prop.Name)) {
502 unmergedProp.Value = sourceValue;
505 else if (sourceValue != null) {
506 object parentValue = parent [prop.Name];
507 if (prop.IsElement) {
508 if (parentValue != null) {
509 ConfigurationElement copy = (ConfigurationElement) unmergedProp.Value;
510 copy.Unmerge ((ConfigurationElement) sourceValue, (ConfigurationElement) parentValue, updateMode);
513 unmergedProp.Value = sourceValue;
516 if (!object.Equals (sourceValue, parentValue) ||
517 (updateMode == ConfigurationSaveMode.Full) ||
518 (updateMode == ConfigurationSaveMode.Modified && prop.ValueOrigin == PropertyValueOrigin.SetHere))
519 unmergedProp.Value = sourceValue;
525 internal bool HasValue (string propName)
527 PropertyInformation info = ElementInformation.Properties [propName];
528 return info != null && info.ValueOrigin != PropertyValueOrigin.Default;
531 internal bool IsReadFromConfig (string propName)
533 PropertyInformation info = ElementInformation.Properties [propName];
534 return info != null && info.ValueOrigin == PropertyValueOrigin.SetHere;
538 internal class ElementMap
540 static Hashtable elementMaps = new Hashtable ();
542 ConfigurationPropertyCollection properties;
543 ConfigurationPropertyCollection keyProperties;
544 ConfigurationProperty defaultCollectionProperty;
546 ConfigurationCollectionAttribute collectionAttribute;
548 public static ElementMap GetMap (Type t)
551 ElementMap map = elementMaps [t] as ElementMap;
552 if (map != null) return map;
553 map = new ElementMap (t);
554 elementMaps [t] = map;
559 public ElementMap (Type t)
561 ReflectProperties (t);
564 protected void ReflectProperties (Type t)
566 collectionAttribute = Attribute.GetCustomAttribute (t, typeof(ConfigurationCollectionAttribute)) as ConfigurationCollectionAttribute;
568 PropertyInfo[] props = t.GetProperties ();
569 foreach (PropertyInfo prop in props)
571 ConfigurationPropertyAttribute at = Attribute.GetCustomAttribute (prop, typeof(ConfigurationPropertyAttribute)) as ConfigurationPropertyAttribute;
572 if (at == null) continue;
573 string name = at.Name != null ? at.Name : prop.Name;
576 /* if we have no default value, don't bother to check further */
577 at.DefaultValue != null && at.DefaultValue != ConfigurationProperty.NoDefaultValue
581 Convert.ChangeType (at.DefaultValue, prop.PropertyType);
584 throw new ConfigurationErrorsException (String.Format ("The default value for property '{0}' has a different type than the one of the property itself",
589 ConfigurationValidatorAttribute validatorAttr = Attribute.GetCustomAttribute (t, typeof(ConfigurationValidatorAttribute)) as ConfigurationValidatorAttribute;
590 ConfigurationValidatorBase validator = validatorAttr != null ? validatorAttr.ValidatorInstance : new DefaultValidator();
592 TypeConverter converter = TypeDescriptor.GetConverter (prop.PropertyType);
593 ConfigurationProperty cp = new ConfigurationProperty (name, prop.PropertyType, at.DefaultValue, converter, validator, at.Options);
595 cp.CollectionAttribute = Attribute.GetCustomAttribute (prop, typeof(ConfigurationCollectionAttribute)) as ConfigurationCollectionAttribute;
597 if (properties == null) properties = new ConfigurationPropertyCollection ();
602 public bool HasProperties
604 get { return properties != null && properties.Count > 0; }
607 public ConfigurationPropertyCollection Properties
610 if (properties == null) properties = new ConfigurationPropertyCollection ();
615 public ConfigurationPropertyCollection KeyProperties {
617 if (keyProperties == null) {
618 keyProperties = new ConfigurationPropertyCollection ();
620 if (properties != null)
621 foreach (ConfigurationProperty p in properties)
622 if (p.IsKey) keyProperties.Add (p);
624 return keyProperties;
628 public ConfigurationCollectionAttribute CollectionAttribute {
629 get { return collectionAttribute; }
632 public ConfigurationProperty DefaultCollectionProperty {
634 if (defaultCollectionProperty == null) {
635 if (properties != null)
636 foreach (ConfigurationProperty p in properties) {
637 if (p.IsDefaultCollection) {
638 defaultCollectionProperty = p;
643 return defaultCollectionProperty;