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;
49 Configuration _configuration;
52 internal Configuration Configuration {
53 get { return _configuration; }
54 set { _configuration = value; }
57 protected ConfigurationElement ()
61 internal virtual void InitFromProperty (PropertyInformation propertyInfo)
63 elementInfo = new ElementInformation (this, propertyInfo);
67 public ElementInformation ElementInformation {
69 if (elementInfo == null)
70 elementInfo = new ElementInformation (this, null);
75 internal string RawXml {
76 get { return rawXml; }
78 // FIXME: this hack is nasty. We should make
79 // some refactory on the entire assembly.
80 if (rawXml == null || value != null)
85 protected internal virtual void Init ()
89 protected internal virtual ConfigurationElementProperty ElementProperty {
91 if (elementProperty == null)
92 elementProperty = new ConfigurationElementProperty (ElementInformation.Validator);
93 return elementProperty;
98 protected ContextInformation EvaluationContext {
100 if (Configuration != null)
101 return Configuration.EvaluationContext;
102 throw new NotImplementedException ();
106 ConfigurationLockCollection lockAllAttributesExcept;
107 public ConfigurationLockCollection LockAllAttributesExcept {
109 if (lockAllAttributesExcept == null) {
110 lockAllAttributesExcept = new ConfigurationLockCollection (this, ConfigurationLockType.Attribute | ConfigurationLockType.Exclude);
113 return lockAllAttributesExcept;
117 ConfigurationLockCollection lockAllElementsExcept;
118 public ConfigurationLockCollection LockAllElementsExcept {
120 if (lockAllElementsExcept == null) {
121 lockAllElementsExcept = new ConfigurationLockCollection (this, ConfigurationLockType.Element | ConfigurationLockType.Exclude);
124 return lockAllElementsExcept;
128 ConfigurationLockCollection lockAttributes;
129 public ConfigurationLockCollection LockAttributes {
131 if (lockAttributes == null) {
132 lockAttributes = new ConfigurationLockCollection (this, ConfigurationLockType.Attribute);
135 return lockAttributes;
139 ConfigurationLockCollection lockElements;
140 public ConfigurationLockCollection LockElements {
142 if (lockElements == null) {
143 lockElements = new ConfigurationLockCollection (this, ConfigurationLockType.Element);
151 public bool LockItem {
152 get { return lockItem; }
153 set { lockItem = value; }
157 protected virtual void ListErrors (IList list)
159 throw new NotImplementedException ();
163 protected void SetPropertyValue (ConfigurationProperty prop, object value, bool ignoreLocks)
167 /* XXX all i know for certain is that Validation happens here */
168 prop.Validate (value);
170 /* XXX presumably the actual setting of the
171 * property happens here instead of in the
172 * set_Item code below, but that would mean
173 * the Value needs to be stuffed in the
174 * property, not the propertyinfo (or else the
175 * property needs a ref to the property info
176 * to correctly set the value). */
179 catch (Exception e) {
180 throw new ConfigurationErrorsException (String.Format ("The value for the property '{0}' on type {1} is not valid.", prop.Name, this.ElementInformation.Type), e);
184 internal ConfigurationPropertyCollection GetKeyProperties ()
186 if (keyProps != null) return keyProps;
188 ConfigurationPropertyCollection tmpkeyProps = new ConfigurationPropertyCollection ();
189 foreach (ConfigurationProperty prop in Properties) {
191 tmpkeyProps.Add (prop);
194 return keyProps = tmpkeyProps;
197 internal ConfigurationElementCollection GetDefaultCollection ()
199 if (defaultCollection != null) return defaultCollection;
201 ConfigurationProperty defaultCollectionProp = null;
203 foreach (ConfigurationProperty prop in Properties) {
204 if (prop.IsDefaultCollection) {
205 defaultCollectionProp = prop;
210 if (defaultCollectionProp != null) {
211 defaultCollection = this [defaultCollectionProp] as ConfigurationElementCollection;
214 return defaultCollection;
217 protected internal object this [ConfigurationProperty property] {
218 get { return this [property.Name]; }
219 set { this [property.Name] = value; }
222 protected internal object this [string property_name] {
224 PropertyInformation pi = ElementInformation.Properties [property_name];
226 throw new InvalidOperationException ("Property '" + property_name + "' not found in configuration element");
232 PropertyInformation pi = ElementInformation.Properties [property_name];
234 throw new InvalidOperationException ("Property '" + property_name + "' not found in configuration element");
236 SetPropertyValue (pi.Property, value, false);
243 protected internal virtual ConfigurationPropertyCollection Properties {
246 map = ElementMap.GetMap (GetType());
247 return map.Properties;
251 public override bool Equals (object compareTo)
253 ConfigurationElement other = compareTo as ConfigurationElement;
254 if (other == null) return false;
255 if (GetType() != other.GetType()) return false;
257 foreach (ConfigurationProperty prop in Properties) {
258 if (!object.Equals (this [prop], other [prop]))
264 public override int GetHashCode ()
269 foreach (ConfigurationProperty prop in Properties) {
274 code += o.GetHashCode ();
280 internal virtual bool HasValues ()
282 foreach (PropertyInformation pi in ElementInformation.Properties)
283 if (pi.ValueOrigin != PropertyValueOrigin.Default)
289 internal virtual bool HasLocalModifications ()
291 foreach (PropertyInformation pi in ElementInformation.Properties)
292 if (pi.ValueOrigin == PropertyValueOrigin.SetHere && pi.IsModified)
298 protected internal virtual void DeserializeElement (XmlReader reader, bool serializeCollectionKey)
300 Hashtable readProps = new Hashtable ();
302 reader.MoveToContent ();
303 elementPresent = true;
305 while (reader.MoveToNextAttribute ())
307 PropertyInformation prop = ElementInformation.Properties [reader.LocalName];
308 if (prop == null || (serializeCollectionKey && !prop.IsKey)) {
309 /* handle the built in ConfigurationElement attributes here */
310 if (reader.LocalName == "lockAllAttributesExcept") {
311 LockAllAttributesExcept.SetFromList (reader.Value);
313 else if (reader.LocalName == "lockAllElementsExcept") {
314 LockAllElementsExcept.SetFromList (reader.Value);
316 else if (reader.LocalName == "lockAttributes") {
317 LockAttributes.SetFromList (reader.Value);
319 else if (reader.LocalName == "lockElements") {
320 LockElements.SetFromList (reader.Value);
322 else if (reader.LocalName == "lockItem") {
323 LockItem = (reader.Value.ToLowerInvariant () == "true");
325 else if (reader.LocalName == "xmlns") {
327 } else if (this is ConfigurationSection && reader.LocalName == "configSource") {
329 } else if (!OnDeserializeUnrecognizedAttribute (reader.LocalName, reader.Value))
330 throw new ConfigurationErrorsException ("Unrecognized attribute '" + reader.LocalName + "'.", reader);
335 if (readProps.ContainsKey (prop))
336 throw new ConfigurationErrorsException ("The attribute '" + prop.Name + "' may only appear once in this element.", reader);
340 value = reader.Value;
341 ValidateValue (prop.Property, value);
342 prop.SetStringValue (value);
343 } catch (ConfigurationErrorsException) {
345 } catch (ConfigurationException) {
347 } catch (Exception ex) {
348 string msg = String.Format ("The value for the property '{0}' is not valid. The error is: {1}", prop.Name, ex.Message);
349 throw new ConfigurationErrorsException (msg, reader);
351 readProps [prop] = prop.Name;
353 ConfigXmlTextReader _reader = reader as ConfigXmlTextReader;
354 if (_reader != null){
355 prop.Source = _reader.Filename;
356 prop.LineNumber = _reader.LineNumber;
360 reader.MoveToElement ();
361 if (reader.IsEmptyElement) {
364 int depth = reader.Depth;
366 reader.ReadStartElement ();
367 reader.MoveToContent ();
370 if (reader.NodeType != XmlNodeType.Element) {
375 PropertyInformation prop = ElementInformation.Properties [reader.LocalName];
376 if (prop == null || (serializeCollectionKey && !prop.IsKey)) {
377 if (!OnDeserializeUnrecognizedElement (reader.LocalName, reader)) {
379 ConfigurationElementCollection c = GetDefaultCollection ();
380 if (c != null && c.OnDeserializeUnrecognizedElement (reader.LocalName, reader))
383 throw new ConfigurationErrorsException ("Unrecognized element '" + reader.LocalName + "'.", reader);
389 throw new ConfigurationErrorsException ("Property '" + prop.Name + "' is not a ConfigurationElement.");
391 if (readProps.Contains (prop))
392 throw new ConfigurationErrorsException ("The element <" + prop.Name + "> may only appear once in this section.", reader);
394 ConfigurationElement val = (ConfigurationElement) prop.Value;
395 val.DeserializeElement (reader, serializeCollectionKey);
396 readProps [prop] = prop.Name;
398 if(depth == reader.Depth)
401 } while (depth < reader.Depth);
406 foreach (PropertyInformation prop in ElementInformation.Properties)
407 if (!String.IsNullOrEmpty(prop.Name) && prop.IsRequired && !readProps.ContainsKey (prop)) {
408 PropertyInformation p = ElementInformation.Properties [prop.Name];
410 object val = OnRequiredPropertyNotFound (prop.Name);
411 if (!object.Equals (val, prop.DefaultValue)) {
413 prop.IsModified = false;
421 protected virtual bool OnDeserializeUnrecognizedAttribute (string name, string value)
426 protected virtual bool OnDeserializeUnrecognizedElement (string element, XmlReader reader)
431 protected virtual object OnRequiredPropertyNotFound (string name)
433 throw new ConfigurationErrorsException ("Required attribute '" + name + "' not found.");
436 protected virtual void PreSerialize (XmlWriter writer)
440 protected virtual void PostDeserialize ()
444 protected internal virtual void InitializeDefault ()
448 protected internal virtual bool IsModified ()
453 protected internal virtual void SetReadOnly ()
458 public virtual bool IsReadOnly ()
463 protected internal virtual void Reset (ConfigurationElement parentElement)
465 elementPresent = false;
467 if (parentElement != null)
468 ElementInformation.Reset (parentElement.ElementInformation);
470 InitializeDefault ();
473 protected internal virtual void ResetModified ()
476 foreach (PropertyInformation p in ElementInformation.Properties)
477 p.IsModified = false;
480 protected internal virtual bool SerializeElement (XmlWriter writer, bool serializeCollectionKey)
482 PreSerialize (writer);
484 if (serializeCollectionKey) {
485 ConfigurationPropertyCollection props = GetKeyProperties ();
486 foreach (ConfigurationProperty prop in props)
487 writer.WriteAttributeString (prop.Name, prop.ConvertToString (this[prop.Name]));
488 return props.Count > 0;
491 bool wroteData = false;
493 foreach (PropertyInformation prop in ElementInformation.Properties)
495 if (prop.IsElement || prop.ValueOrigin == PropertyValueOrigin.Default)
498 if (!object.Equals (prop.Value, prop.DefaultValue)) {
499 writer.WriteAttributeString (prop.Name, prop.GetStringValue ());
504 foreach (PropertyInformation prop in ElementInformation.Properties)
509 ConfigurationElement val = (ConfigurationElement) prop.Value;
511 wroteData = val.SerializeToXmlElement (writer, prop.Name) || wroteData;
516 protected internal virtual bool SerializeToXmlElement (
517 XmlWriter writer, string elementName)
522 if (elementName != null && elementName != "")
523 writer.WriteStartElement (elementName);
524 bool res = SerializeElement (writer, false);
525 if (elementName != null && elementName != "")
526 writer.WriteEndElement ();
530 protected internal virtual void Unmerge (
531 ConfigurationElement source, ConfigurationElement parent,
532 ConfigurationSaveMode updateMode)
534 if (parent != null && source.GetType() != parent.GetType())
535 throw new ConfigurationErrorsException ("Can't unmerge two elements of different type");
537 foreach (PropertyInformation prop in source.ElementInformation.Properties)
539 if (prop.ValueOrigin == PropertyValueOrigin.Default)
542 PropertyInformation unmergedProp = ElementInformation.Properties [prop.Name];
544 object sourceValue = prop.Value;
545 if (parent == null || !parent.HasValue (prop.Name)) {
546 unmergedProp.Value = sourceValue;
549 else if (sourceValue != null) {
550 object parentValue = parent [prop.Name];
551 if (prop.IsElement) {
552 if (parentValue != null) {
553 ConfigurationElement copy = (ConfigurationElement) unmergedProp.Value;
554 copy.Unmerge ((ConfigurationElement) sourceValue, (ConfigurationElement) parentValue, updateMode);
557 unmergedProp.Value = sourceValue;
560 if (!object.Equals (sourceValue, parentValue) ||
561 (updateMode == ConfigurationSaveMode.Full) ||
562 (updateMode == ConfigurationSaveMode.Modified && prop.ValueOrigin == PropertyValueOrigin.SetHere))
563 unmergedProp.Value = sourceValue;
569 internal bool HasValue (string propName)
571 PropertyInformation info = ElementInformation.Properties [propName];
572 return info != null && info.ValueOrigin != PropertyValueOrigin.Default;
575 internal bool IsReadFromConfig (string propName)
577 PropertyInformation info = ElementInformation.Properties [propName];
578 return info != null && info.ValueOrigin == PropertyValueOrigin.SetHere;
581 internal bool IsElementPresent
583 get { return elementPresent; }
586 void ValidateValue (ConfigurationProperty p, string value)
588 ConfigurationValidatorBase validator;
589 if (p == null || (validator = p.Validator) == null)
592 if (!validator.CanValidate (p.Type))
593 throw new ConfigurationErrorsException (
594 String.Format ("Validator does not support type {0}", p.Type));
595 validator.Validate (p.ConvertFromString (value));
599 internal class ElementMap
602 const string elementMapsKey = "ElementMap_elementMaps";
603 static Hashtable elementMaps
607 Hashtable tbl = (Hashtable) AppDomain.CurrentDomain.GetData (elementMapsKey);
609 lock (typeof (ElementMap)) {
610 tbl = (Hashtable) AppDomain.CurrentDomain.GetData (elementMapsKey);
612 tbl = Hashtable.Synchronized (new Hashtable ());
613 AppDomain.CurrentDomain.SetData (elementMapsKey, tbl);
621 static readonly Hashtable elementMaps = Hashtable.Synchronized (new Hashtable ());
624 readonly ConfigurationPropertyCollection properties;
625 readonly ConfigurationCollectionAttribute collectionAttribute;
627 public static ElementMap GetMap (Type t)
629 ElementMap map = elementMaps [t] as ElementMap;
630 if (map != null) return map;
631 map = new ElementMap (t);
632 elementMaps [t] = map;
636 public ElementMap (Type t)
638 properties = new ConfigurationPropertyCollection ();
640 collectionAttribute = Attribute.GetCustomAttribute (t, typeof(ConfigurationCollectionAttribute)) as ConfigurationCollectionAttribute;
642 PropertyInfo[] props = t.GetProperties (BindingFlags.Public|BindingFlags.NonPublic|BindingFlags.Static|BindingFlags.Instance);
643 foreach (PropertyInfo prop in props)
645 ConfigurationPropertyAttribute at = Attribute.GetCustomAttribute (prop, typeof(ConfigurationPropertyAttribute)) as ConfigurationPropertyAttribute;
646 if (at == null) continue;
647 string name = at.Name != null ? at.Name : prop.Name;
649 ConfigurationValidatorAttribute validatorAttr = Attribute.GetCustomAttribute (prop, typeof (ConfigurationValidatorAttribute)) as ConfigurationValidatorAttribute;
650 ConfigurationValidatorBase validator = validatorAttr != null ? validatorAttr.ValidatorInstance : null;
652 TypeConverterAttribute convertAttr = (TypeConverterAttribute) Attribute.GetCustomAttribute (prop, typeof (TypeConverterAttribute));
653 TypeConverter converter = convertAttr != null ? (TypeConverter) Activator.CreateInstance (Type.GetType (convertAttr.ConverterTypeName), true) : null;
654 ConfigurationProperty cp = new ConfigurationProperty (name, prop.PropertyType, at.DefaultValue, converter, validator, at.Options);
656 cp.CollectionAttribute = Attribute.GetCustomAttribute (prop, typeof(ConfigurationCollectionAttribute)) as ConfigurationCollectionAttribute;
661 public ConfigurationCollectionAttribute CollectionAttribute
663 get { return collectionAttribute; }
666 public bool HasProperties
668 get { return properties.Count > 0; }
671 public ConfigurationPropertyCollection Properties