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 ConfigurationPropertyCollection tmpkeyProps = new ConfigurationPropertyCollection ();
178 foreach (ConfigurationProperty prop in Properties) {
180 tmpkeyProps.Add (prop);
183 return keyProps = tmpkeyProps;
186 internal ConfigurationElementCollection GetDefaultCollection ()
188 if (defaultCollection != null) return defaultCollection;
190 ConfigurationProperty defaultCollectionProp = null;
192 foreach (ConfigurationProperty prop in Properties) {
193 if (prop.IsDefaultCollection) {
194 defaultCollectionProp = prop;
199 if (defaultCollectionProp != null) {
200 defaultCollection = this [defaultCollectionProp] as ConfigurationElementCollection;
203 return defaultCollection;
206 protected internal object this [ConfigurationProperty property] {
207 get { return this [property.Name]; }
208 set { this [property.Name] = value; }
211 protected internal object this [string property_name] {
213 PropertyInformation pi = ElementInformation.Properties [property_name];
215 throw new InvalidOperationException ("Property '" + property_name + "' not found in configuration element");
221 PropertyInformation pi = ElementInformation.Properties [property_name];
223 throw new InvalidOperationException ("Property '" + property_name + "' not found in configuration element");
225 SetPropertyValue (pi.Property, value, false);
232 protected internal virtual ConfigurationPropertyCollection Properties {
235 map = ElementMap.GetMap (GetType());
236 return map.Properties;
240 public override bool Equals (object compareTo)
242 ConfigurationElement other = compareTo as ConfigurationElement;
243 if (other == null) return false;
244 if (GetType() != other.GetType()) return false;
246 foreach (ConfigurationProperty prop in Properties) {
247 if (!object.Equals (this [prop], other [prop]))
253 public override int GetHashCode ()
258 foreach (ConfigurationProperty prop in Properties) {
263 code += o.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.ToLowerInvariant () == "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.");
316 value = reader.Value;
317 ValidateValue (prop.Property, value);
318 prop.SetStringValue (value);
319 } catch (ConfigurationErrorsException) {
321 } catch (ConfigurationException) {
323 } catch (Exception ex) {
324 string msg = String.Format ("The value of the property '{0}' cannot be parsed.", prop.Name);
325 throw new ConfigurationErrorsException (msg, reader);
327 readProps [prop] = prop.Name;
330 reader.MoveToElement ();
331 if (reader.IsEmptyElement) {
334 int depth = reader.Depth;
336 reader.ReadStartElement ();
337 reader.MoveToContent ();
340 if (reader.NodeType != XmlNodeType.Element) {
345 PropertyInformation prop = ElementInformation.Properties [reader.LocalName];
346 if (prop == null || (serializeCollectionKey && !prop.IsKey)) {
347 if (!OnDeserializeUnrecognizedElement (reader.LocalName, reader)) {
349 ConfigurationElementCollection c = GetDefaultCollection ();
350 if (c != null && c.OnDeserializeUnrecognizedElement (reader.LocalName, reader))
353 throw new ConfigurationException ("Unrecognized element '" + reader.LocalName + "'.");
359 throw new ConfigurationException ("Property '" + prop.Name + "' is not a ConfigurationElement.");
361 if (readProps.Contains (prop))
362 throw new ConfigurationException ("The element <" + prop.Name + "> may only appear once in this section.");
364 ConfigurationElement val = (ConfigurationElement) prop.Value;
365 val.DeserializeElement (reader, serializeCollectionKey);
366 readProps [prop] = prop.Name;
370 } while (depth < reader.Depth);
372 if (reader.NodeType == XmlNodeType.EndElement)
378 foreach (PropertyInformation prop in ElementInformation.Properties)
379 if (!String.IsNullOrEmpty(prop.Name) && prop.IsRequired && !readProps.ContainsKey (prop)) {
380 object val = OnRequiredPropertyNotFound (prop.Name);
381 if (!object.Equals (val, prop.DefaultValue)) {
383 prop.IsModified = false;
390 protected virtual bool OnDeserializeUnrecognizedAttribute (string name, string value)
395 protected virtual bool OnDeserializeUnrecognizedElement (string element, XmlReader reader)
400 protected virtual object OnRequiredPropertyNotFound (string name)
402 throw new ConfigurationErrorsException ("Required attribute '" + name + "' not found.");
405 protected virtual void PreSerialize (XmlWriter writer)
409 protected virtual void PostDeserialize ()
413 protected internal virtual void InitializeDefault ()
417 protected internal virtual bool IsModified ()
422 protected internal virtual void SetReadOnly ()
427 public virtual bool IsReadOnly ()
432 protected internal virtual void Reset (ConfigurationElement parentElement)
434 if (parentElement != null)
435 ElementInformation.Reset (parentElement.ElementInformation);
437 InitializeDefault ();
440 protected internal virtual void ResetModified ()
443 foreach (PropertyInformation p in ElementInformation.Properties)
444 p.IsModified = false;
447 protected internal virtual bool SerializeElement (XmlWriter writer, bool serializeCollectionKey)
449 PreSerialize (writer);
451 if (serializeCollectionKey) {
452 ConfigurationPropertyCollection props = GetKeyProperties ();
453 foreach (ConfigurationProperty prop in props)
454 writer.WriteAttributeString (prop.Name, prop.ConvertToString (this[prop.Name]));
455 return props.Count > 0;
458 bool wroteData = false;
460 foreach (PropertyInformation prop in ElementInformation.Properties)
462 if (prop.IsElement || prop.ValueOrigin == PropertyValueOrigin.Default)
465 if (!object.Equals (prop.Value, prop.DefaultValue)) {
466 writer.WriteAttributeString (prop.Name, prop.GetStringValue ());
471 foreach (PropertyInformation prop in ElementInformation.Properties)
476 ConfigurationElement val = (ConfigurationElement) prop.Value;
478 wroteData = val.SerializeToXmlElement (writer, prop.Name) || wroteData;
483 protected internal virtual bool SerializeToXmlElement (
484 XmlWriter writer, string elementName)
489 if (elementName != null && elementName != "")
490 writer.WriteStartElement (elementName);
491 bool res = SerializeElement (writer, false);
492 if (elementName != null && elementName != "")
493 writer.WriteEndElement ();
497 protected internal virtual void Unmerge (
498 ConfigurationElement source, ConfigurationElement parent,
499 ConfigurationSaveMode updateMode)
501 if (parent != null && source.GetType() != parent.GetType())
502 throw new ConfigurationException ("Can't unmerge two elements of different type");
504 foreach (PropertyInformation prop in source.ElementInformation.Properties)
506 if (prop.ValueOrigin == PropertyValueOrigin.Default)
509 PropertyInformation unmergedProp = ElementInformation.Properties [prop.Name];
511 object sourceValue = prop.Value;
512 if (parent == null || !parent.HasValue (prop.Name)) {
513 unmergedProp.Value = sourceValue;
516 else if (sourceValue != null) {
517 object parentValue = parent [prop.Name];
518 if (prop.IsElement) {
519 if (parentValue != null) {
520 ConfigurationElement copy = (ConfigurationElement) unmergedProp.Value;
521 copy.Unmerge ((ConfigurationElement) sourceValue, (ConfigurationElement) parentValue, updateMode);
524 unmergedProp.Value = sourceValue;
527 if (!object.Equals (sourceValue, parentValue) ||
528 (updateMode == ConfigurationSaveMode.Full) ||
529 (updateMode == ConfigurationSaveMode.Modified && prop.ValueOrigin == PropertyValueOrigin.SetHere))
530 unmergedProp.Value = sourceValue;
536 internal bool HasValue (string propName)
538 PropertyInformation info = ElementInformation.Properties [propName];
539 return info != null && info.ValueOrigin != PropertyValueOrigin.Default;
542 internal bool IsReadFromConfig (string propName)
544 PropertyInformation info = ElementInformation.Properties [propName];
545 return info != null && info.ValueOrigin == PropertyValueOrigin.SetHere;
548 void ValidateValue (ConfigurationProperty p, string value)
550 ConfigurationValidatorBase validator;
551 if (p == null || (validator = p.Validator) == null)
554 if (!validator.CanValidate (p.Type))
555 throw new ConfigurationException (
556 String.Format ("Validator does not support type {0}", p.Type));
557 validator.Validate (p.ConvertFromString (value));
561 internal class ElementMap
564 const string elementMapsKey = "ElementMap_elementMaps";
565 static Hashtable elementMaps
569 Hashtable tbl = (Hashtable) AppDomain.CurrentDomain.GetData (elementMapsKey);
571 lock (typeof (ElementMap)) {
572 tbl = (Hashtable) AppDomain.CurrentDomain.GetData (elementMapsKey);
574 tbl = Hashtable.Synchronized (new Hashtable ());
575 AppDomain.CurrentDomain.SetData (elementMapsKey, tbl);
583 static readonly Hashtable elementMaps = Hashtable.Synchronized (new Hashtable ());
586 readonly ConfigurationPropertyCollection properties;
587 readonly ConfigurationCollectionAttribute collectionAttribute;
589 public static ElementMap GetMap (Type t)
591 ElementMap map = elementMaps [t] as ElementMap;
592 if (map != null) return map;
593 map = new ElementMap (t);
594 elementMaps [t] = map;
598 public ElementMap (Type t)
600 properties = new ConfigurationPropertyCollection ();
602 collectionAttribute = Attribute.GetCustomAttribute (t, typeof(ConfigurationCollectionAttribute)) as ConfigurationCollectionAttribute;
604 PropertyInfo[] props = t.GetProperties ();
605 foreach (PropertyInfo prop in props)
607 ConfigurationPropertyAttribute at = Attribute.GetCustomAttribute (prop, typeof(ConfigurationPropertyAttribute)) as ConfigurationPropertyAttribute;
608 if (at == null) continue;
609 string name = at.Name != null ? at.Name : prop.Name;
611 ConfigurationValidatorAttribute validatorAttr = Attribute.GetCustomAttribute (t, typeof(ConfigurationValidatorAttribute)) as ConfigurationValidatorAttribute;
612 ConfigurationValidatorBase validator = validatorAttr != null ? validatorAttr.ValidatorInstance : null;
614 TypeConverterAttribute convertAttr = (TypeConverterAttribute) Attribute.GetCustomAttribute (t, typeof (TypeConverterAttribute));
615 TypeConverter converter = convertAttr != null ? (TypeConverter) Activator.CreateInstance (Type.GetType (convertAttr.ConverterTypeName)) : null;
616 ConfigurationProperty cp = new ConfigurationProperty (name, prop.PropertyType, at.DefaultValue, converter, validator, at.Options);
618 cp.CollectionAttribute = Attribute.GetCustomAttribute (prop, typeof(ConfigurationCollectionAttribute)) as ConfigurationCollectionAttribute;
623 public ConfigurationCollectionAttribute CollectionAttribute
625 get { return collectionAttribute; }
628 public bool HasProperties
630 get { return properties.Count > 0; }
633 public ConfigurationPropertyCollection Properties