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;
51 internal Configuration Configuration {
52 get { return _configuration; }
53 set { _configuration = value; }
56 protected ConfigurationElement ()
60 internal virtual void InitFromProperty (PropertyInformation propertyInfo)
62 elementInfo = new ElementInformation (this, propertyInfo);
66 public ElementInformation ElementInformation {
68 if (elementInfo == null)
69 elementInfo = new ElementInformation (this, null);
74 internal string RawXml {
75 get { return rawXml; }
77 // FIXME: this hack is nasty. We should make
78 // some refactory on the entire assembly.
79 if (rawXml == null || value != null)
84 protected internal virtual void Init ()
88 protected internal virtual ConfigurationElementProperty ElementProperty {
90 if (elementProperty == null)
91 elementProperty = new ConfigurationElementProperty (ElementInformation.Validator);
92 return elementProperty;
97 protected ContextInformation EvaluationContext {
99 if (Configuration != null)
100 return Configuration.EvaluationContext;
101 throw new NotImplementedException ();
105 ConfigurationLockCollection lockAllAttributesExcept;
106 public ConfigurationLockCollection LockAllAttributesExcept {
108 if (lockAllAttributesExcept == null) {
109 lockAllAttributesExcept = new ConfigurationLockCollection (this, ConfigurationLockType.Attribute | ConfigurationLockType.Exclude);
112 return lockAllAttributesExcept;
116 ConfigurationLockCollection lockAllElementsExcept;
117 public ConfigurationLockCollection LockAllElementsExcept {
119 if (lockAllElementsExcept == null) {
120 lockAllElementsExcept = new ConfigurationLockCollection (this, ConfigurationLockType.Element | ConfigurationLockType.Exclude);
123 return lockAllElementsExcept;
127 ConfigurationLockCollection lockAttributes;
128 public ConfigurationLockCollection LockAttributes {
130 if (lockAttributes == null) {
131 lockAttributes = new ConfigurationLockCollection (this, ConfigurationLockType.Attribute);
134 return lockAttributes;
138 ConfigurationLockCollection lockElements;
139 public ConfigurationLockCollection LockElements {
141 if (lockElements == null) {
142 lockElements = new ConfigurationLockCollection (this, ConfigurationLockType.Element);
150 public bool LockItem {
151 get { return lockItem; }
152 set { lockItem = value; }
156 protected virtual void ListErrors (IList list)
158 throw new NotImplementedException ();
162 protected void SetPropertyValue (ConfigurationProperty prop, object value, bool ignoreLocks)
165 /* XXX all i know for certain is that Validation happens here */
166 prop.Validate (value);
168 /* XXX presumably the actual setting of the
169 * property happens here instead of in the
170 * set_Item code below, but that would mean
171 * the Value needs to be stuffed in the
172 * property, not the propertyinfo (or else the
173 * property needs a ref to the property info
174 * to correctly set the value). */
176 catch (Exception e) {
177 throw new ConfigurationErrorsException (String.Format ("The value for the property '{0}' is not valid. The error is: {1}", prop.Name, e.Message), e);
181 internal ConfigurationPropertyCollection GetKeyProperties ()
183 if (keyProps != null) return keyProps;
185 ConfigurationPropertyCollection tmpkeyProps = new ConfigurationPropertyCollection ();
186 foreach (ConfigurationProperty prop in Properties) {
188 tmpkeyProps.Add (prop);
191 return keyProps = tmpkeyProps;
194 internal ConfigurationElementCollection GetDefaultCollection ()
196 if (defaultCollection != null) return defaultCollection;
198 ConfigurationProperty defaultCollectionProp = null;
200 foreach (ConfigurationProperty prop in Properties) {
201 if (prop.IsDefaultCollection) {
202 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 ()
266 foreach (ConfigurationProperty prop in Properties) {
271 code += o.GetHashCode ();
277 internal virtual bool HasValues ()
279 foreach (PropertyInformation pi in ElementInformation.Properties)
280 if (pi.ValueOrigin != PropertyValueOrigin.Default)
286 internal virtual bool HasLocalModifications ()
288 foreach (PropertyInformation pi in ElementInformation.Properties)
289 if (pi.ValueOrigin == PropertyValueOrigin.SetHere && pi.IsModified)
295 protected internal virtual void DeserializeElement (XmlReader reader, bool serializeCollectionKey)
297 Hashtable readProps = new Hashtable ();
299 reader.MoveToContent ();
300 while (reader.MoveToNextAttribute ())
302 PropertyInformation prop = ElementInformation.Properties [reader.LocalName];
303 if (prop == null || (serializeCollectionKey && !prop.IsKey)) {
304 /* handle the built in ConfigurationElement attributes here */
305 if (reader.LocalName == "lockAllAttributesExcept") {
306 LockAllAttributesExcept.SetFromList (reader.Value);
308 else if (reader.LocalName == "lockAllElementsExcept") {
309 LockAllElementsExcept.SetFromList (reader.Value);
311 else if (reader.LocalName == "lockAttributes") {
312 LockAttributes.SetFromList (reader.Value);
314 else if (reader.LocalName == "lockElements") {
315 LockElements.SetFromList (reader.Value);
317 else if (reader.LocalName == "lockItem") {
318 LockItem = (reader.Value.ToLowerInvariant () == "true");
320 else if (reader.LocalName == "xmlns") {
322 } else if (this is ConfigurationSection && reader.LocalName == "configSource") {
324 } else if (!OnDeserializeUnrecognizedAttribute (reader.LocalName, reader.Value))
325 throw new ConfigurationErrorsException ("Unrecognized attribute '" + reader.LocalName + "'.", reader);
330 if (readProps.ContainsKey (prop))
331 throw new ConfigurationErrorsException ("The attribute '" + prop.Name + "' may only appear once in this element.", reader);
335 value = reader.Value;
336 ValidateValue (prop.Property, value);
337 prop.SetStringValue (value);
338 } catch (ConfigurationErrorsException) {
340 } catch (ConfigurationException) {
342 } catch (Exception ex) {
343 string msg = String.Format ("The value for the property '{0}' is not valid. The error is: {1}", prop.Name, ex.Message);
344 throw new ConfigurationErrorsException (msg, reader);
346 readProps [prop] = prop.Name;
348 ConfigXmlTextReader _reader = reader as ConfigXmlTextReader;
350 prop.Source = _reader.Filename;
351 prop.LineNumber = _reader.LineNumber;
354 reader.MoveToElement ();
355 if (reader.IsEmptyElement) {
358 int depth = reader.Depth;
360 reader.ReadStartElement ();
361 reader.MoveToContent ();
364 if (reader.NodeType != XmlNodeType.Element) {
369 PropertyInformation prop = ElementInformation.Properties [reader.LocalName];
370 if (prop == null || (serializeCollectionKey && !prop.IsKey)) {
371 if (!OnDeserializeUnrecognizedElement (reader.LocalName, reader)) {
373 ConfigurationElementCollection c = GetDefaultCollection ();
374 if (c != null && c.OnDeserializeUnrecognizedElement (reader.LocalName, reader))
377 throw new ConfigurationErrorsException ("Unrecognized element '" + reader.LocalName + "'.", reader);
383 throw new ConfigurationException ("Property '" + prop.Name + "' is not a ConfigurationElement.");
385 if (readProps.Contains (prop))
386 throw new ConfigurationErrorsException ("The element <" + prop.Name + "> may only appear once in this section.", reader);
388 ConfigurationElement val = (ConfigurationElement) prop.Value;
389 val.DeserializeElement (reader, serializeCollectionKey);
390 readProps [prop] = prop.Name;
392 if(depth == reader.Depth)
395 } while (depth < reader.Depth);
400 foreach (PropertyInformation prop in ElementInformation.Properties)
401 if (!String.IsNullOrEmpty(prop.Name) && prop.IsRequired && !readProps.ContainsKey (prop)) {
402 PropertyInformation p = ElementInformation.Properties [prop.Name];
404 object val = OnRequiredPropertyNotFound (prop.Name);
405 if (!object.Equals (val, prop.DefaultValue)) {
407 prop.IsModified = false;
415 protected virtual bool OnDeserializeUnrecognizedAttribute (string name, string value)
420 protected virtual bool OnDeserializeUnrecognizedElement (string element, XmlReader reader)
425 protected virtual object OnRequiredPropertyNotFound (string name)
427 throw new ConfigurationErrorsException ("Required attribute '" + name + "' not found.");
430 protected virtual void PreSerialize (XmlWriter writer)
434 protected virtual void PostDeserialize ()
438 protected internal virtual void InitializeDefault ()
442 protected internal virtual bool IsModified ()
447 protected internal virtual void SetReadOnly ()
452 public virtual bool IsReadOnly ()
457 protected internal virtual void Reset (ConfigurationElement parentElement)
459 if (parentElement != null)
460 ElementInformation.Reset (parentElement.ElementInformation);
462 InitializeDefault ();
465 protected internal virtual void ResetModified ()
468 foreach (PropertyInformation p in ElementInformation.Properties)
469 p.IsModified = false;
472 protected internal virtual bool SerializeElement (XmlWriter writer, bool serializeCollectionKey)
474 PreSerialize (writer);
476 if (serializeCollectionKey) {
477 ConfigurationPropertyCollection props = GetKeyProperties ();
478 foreach (ConfigurationProperty prop in props)
479 writer.WriteAttributeString (prop.Name, prop.ConvertToString (this[prop.Name]));
480 return props.Count > 0;
483 bool wroteData = false;
485 foreach (PropertyInformation prop in ElementInformation.Properties)
487 if (prop.IsElement || prop.ValueOrigin == PropertyValueOrigin.Default)
490 if (!object.Equals (prop.Value, prop.DefaultValue)) {
491 writer.WriteAttributeString (prop.Name, prop.GetStringValue ());
496 foreach (PropertyInformation prop in ElementInformation.Properties)
501 ConfigurationElement val = (ConfigurationElement) prop.Value;
503 wroteData = val.SerializeToXmlElement (writer, prop.Name) || wroteData;
508 protected internal virtual bool SerializeToXmlElement (
509 XmlWriter writer, string elementName)
514 if (elementName != null && elementName != "")
515 writer.WriteStartElement (elementName);
516 bool res = SerializeElement (writer, false);
517 if (elementName != null && elementName != "")
518 writer.WriteEndElement ();
522 protected internal virtual void Unmerge (
523 ConfigurationElement source, ConfigurationElement parent,
524 ConfigurationSaveMode updateMode)
526 if (parent != null && source.GetType() != parent.GetType())
527 throw new ConfigurationException ("Can't unmerge two elements of different type");
529 foreach (PropertyInformation prop in source.ElementInformation.Properties)
531 if (prop.ValueOrigin == PropertyValueOrigin.Default)
534 PropertyInformation unmergedProp = ElementInformation.Properties [prop.Name];
536 object sourceValue = prop.Value;
537 if (parent == null || !parent.HasValue (prop.Name)) {
538 unmergedProp.Value = sourceValue;
541 else if (sourceValue != null) {
542 object parentValue = parent [prop.Name];
543 if (prop.IsElement) {
544 if (parentValue != null) {
545 ConfigurationElement copy = (ConfigurationElement) unmergedProp.Value;
546 copy.Unmerge ((ConfigurationElement) sourceValue, (ConfigurationElement) parentValue, updateMode);
549 unmergedProp.Value = sourceValue;
552 if (!object.Equals (sourceValue, parentValue) ||
553 (updateMode == ConfigurationSaveMode.Full) ||
554 (updateMode == ConfigurationSaveMode.Modified && prop.ValueOrigin == PropertyValueOrigin.SetHere))
555 unmergedProp.Value = sourceValue;
561 internal bool HasValue (string propName)
563 PropertyInformation info = ElementInformation.Properties [propName];
564 return info != null && info.ValueOrigin != PropertyValueOrigin.Default;
567 internal bool IsReadFromConfig (string propName)
569 PropertyInformation info = ElementInformation.Properties [propName];
570 return info != null && info.ValueOrigin == PropertyValueOrigin.SetHere;
573 void ValidateValue (ConfigurationProperty p, string value)
575 ConfigurationValidatorBase validator;
576 if (p == null || (validator = p.Validator) == null)
579 if (!validator.CanValidate (p.Type))
580 throw new ConfigurationException (
581 String.Format ("Validator does not support type {0}", p.Type));
582 validator.Validate (p.ConvertFromString (value));
586 internal class ElementMap
589 const string elementMapsKey = "ElementMap_elementMaps";
590 static Hashtable elementMaps
594 Hashtable tbl = (Hashtable) AppDomain.CurrentDomain.GetData (elementMapsKey);
596 lock (typeof (ElementMap)) {
597 tbl = (Hashtable) AppDomain.CurrentDomain.GetData (elementMapsKey);
599 tbl = Hashtable.Synchronized (new Hashtable ());
600 AppDomain.CurrentDomain.SetData (elementMapsKey, tbl);
608 static readonly Hashtable elementMaps = Hashtable.Synchronized (new Hashtable ());
611 readonly ConfigurationPropertyCollection properties;
612 readonly ConfigurationCollectionAttribute collectionAttribute;
614 public static ElementMap GetMap (Type t)
616 ElementMap map = elementMaps [t] as ElementMap;
617 if (map != null) return map;
618 map = new ElementMap (t);
619 elementMaps [t] = map;
623 public ElementMap (Type t)
625 properties = new ConfigurationPropertyCollection ();
627 collectionAttribute = Attribute.GetCustomAttribute (t, typeof(ConfigurationCollectionAttribute)) as ConfigurationCollectionAttribute;
629 PropertyInfo[] props = t.GetProperties (BindingFlags.Public|BindingFlags.NonPublic|BindingFlags.Static|BindingFlags.Instance);
630 foreach (PropertyInfo prop in props)
632 ConfigurationPropertyAttribute at = Attribute.GetCustomAttribute (prop, typeof(ConfigurationPropertyAttribute)) as ConfigurationPropertyAttribute;
633 if (at == null) continue;
634 string name = at.Name != null ? at.Name : prop.Name;
636 ConfigurationValidatorAttribute validatorAttr = Attribute.GetCustomAttribute (prop, typeof (ConfigurationValidatorAttribute)) as ConfigurationValidatorAttribute;
637 ConfigurationValidatorBase validator = validatorAttr != null ? validatorAttr.ValidatorInstance : null;
639 TypeConverterAttribute convertAttr = (TypeConverterAttribute) Attribute.GetCustomAttribute (prop, typeof (TypeConverterAttribute));
640 TypeConverter converter = convertAttr != null ? (TypeConverter) Activator.CreateInstance (Type.GetType (convertAttr.ConverterTypeName), true) : null;
641 ConfigurationProperty cp = new ConfigurationProperty (name, prop.PropertyType, at.DefaultValue, converter, validator, at.Options);
643 cp.CollectionAttribute = Attribute.GetCustomAttribute (prop, typeof(ConfigurationCollectionAttribute)) as ConfigurationCollectionAttribute;
648 public ConfigurationCollectionAttribute CollectionAttribute
650 get { return collectionAttribute; }
653 public bool HasProperties
655 get { return properties.Count > 0; }
658 public ConfigurationPropertyCollection Properties