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)
166 /* XXX all i know for certain is that Validation happens here */
167 prop.Validate (value);
169 /* XXX presumably the actual setting of the
170 * property happens here instead of in the
171 * set_Item code below, but that would mean
172 * the Value needs to be stuffed in the
173 * property, not the propertyinfo (or else the
174 * property needs a ref to the property info
175 * to correctly set the value). */
177 catch (Exception e) {
178 throw new ConfigurationErrorsException (String.Format ("The value for the property '{0}' is not valid. The error is: {1}", prop.Name, e.Message), e);
182 internal ConfigurationPropertyCollection GetKeyProperties ()
184 if (keyProps != null) return keyProps;
186 ConfigurationPropertyCollection tmpkeyProps = new ConfigurationPropertyCollection ();
187 foreach (ConfigurationProperty prop in Properties) {
189 tmpkeyProps.Add (prop);
192 return keyProps = tmpkeyProps;
195 internal ConfigurationElementCollection GetDefaultCollection ()
197 if (defaultCollection != null) return defaultCollection;
199 ConfigurationProperty defaultCollectionProp = null;
201 foreach (ConfigurationProperty prop in Properties) {
202 if (prop.IsDefaultCollection) {
203 defaultCollectionProp = prop;
208 if (defaultCollectionProp != null) {
209 defaultCollection = this [defaultCollectionProp] as ConfigurationElementCollection;
212 return defaultCollection;
215 protected internal object this [ConfigurationProperty property] {
216 get { return this [property.Name]; }
217 set { this [property.Name] = value; }
220 protected internal object this [string property_name] {
222 PropertyInformation pi = ElementInformation.Properties [property_name];
224 throw new InvalidOperationException ("Property '" + property_name + "' not found in configuration element");
230 PropertyInformation pi = ElementInformation.Properties [property_name];
232 throw new InvalidOperationException ("Property '" + property_name + "' not found in configuration element");
234 SetPropertyValue (pi.Property, value, false);
241 protected internal virtual ConfigurationPropertyCollection Properties {
244 map = ElementMap.GetMap (GetType());
245 return map.Properties;
249 public override bool Equals (object compareTo)
251 ConfigurationElement other = compareTo as ConfigurationElement;
252 if (other == null) return false;
253 if (GetType() != other.GetType()) return false;
255 foreach (ConfigurationProperty prop in Properties) {
256 if (!object.Equals (this [prop], other [prop]))
262 public override int GetHashCode ()
267 foreach (ConfigurationProperty prop in Properties) {
272 code += o.GetHashCode ();
278 internal virtual bool HasValues ()
280 foreach (PropertyInformation pi in ElementInformation.Properties)
281 if (pi.ValueOrigin != PropertyValueOrigin.Default)
287 internal virtual bool HasLocalModifications ()
289 foreach (PropertyInformation pi in ElementInformation.Properties)
290 if (pi.ValueOrigin == PropertyValueOrigin.SetHere && pi.IsModified)
296 protected internal virtual void DeserializeElement (XmlReader reader, bool serializeCollectionKey)
298 Hashtable readProps = new Hashtable ();
300 reader.MoveToContent ();
301 elementPresent = true;
303 while (reader.MoveToNextAttribute ())
305 PropertyInformation prop = ElementInformation.Properties [reader.LocalName];
306 if (prop == null || (serializeCollectionKey && !prop.IsKey)) {
307 /* handle the built in ConfigurationElement attributes here */
308 if (reader.LocalName == "lockAllAttributesExcept") {
309 LockAllAttributesExcept.SetFromList (reader.Value);
311 else if (reader.LocalName == "lockAllElementsExcept") {
312 LockAllElementsExcept.SetFromList (reader.Value);
314 else if (reader.LocalName == "lockAttributes") {
315 LockAttributes.SetFromList (reader.Value);
317 else if (reader.LocalName == "lockElements") {
318 LockElements.SetFromList (reader.Value);
320 else if (reader.LocalName == "lockItem") {
321 LockItem = (reader.Value.ToLowerInvariant () == "true");
323 else if (reader.LocalName == "xmlns") {
325 } else if (this is ConfigurationSection && reader.LocalName == "configSource") {
327 } else if (!OnDeserializeUnrecognizedAttribute (reader.LocalName, reader.Value))
328 throw new ConfigurationErrorsException ("Unrecognized attribute '" + reader.LocalName + "'.", reader);
333 if (readProps.ContainsKey (prop))
334 throw new ConfigurationErrorsException ("The attribute '" + prop.Name + "' may only appear once in this element.", reader);
338 value = reader.Value;
339 ValidateValue (prop.Property, value);
340 prop.SetStringValue (value);
341 } catch (ConfigurationErrorsException) {
343 } catch (ConfigurationException) {
345 } catch (Exception ex) {
346 string msg = String.Format ("The value for the property '{0}' is not valid. The error is: {1}", prop.Name, ex.Message);
347 throw new ConfigurationErrorsException (msg, reader);
349 readProps [prop] = prop.Name;
351 ConfigXmlTextReader _reader = reader as ConfigXmlTextReader;
353 prop.Source = _reader.Filename;
354 prop.LineNumber = _reader.LineNumber;
357 reader.MoveToElement ();
358 if (reader.IsEmptyElement) {
361 int depth = reader.Depth;
363 reader.ReadStartElement ();
364 reader.MoveToContent ();
367 if (reader.NodeType != XmlNodeType.Element) {
372 PropertyInformation prop = ElementInformation.Properties [reader.LocalName];
373 if (prop == null || (serializeCollectionKey && !prop.IsKey)) {
374 if (!OnDeserializeUnrecognizedElement (reader.LocalName, reader)) {
376 ConfigurationElementCollection c = GetDefaultCollection ();
377 if (c != null && c.OnDeserializeUnrecognizedElement (reader.LocalName, reader))
380 throw new ConfigurationErrorsException ("Unrecognized element '" + reader.LocalName + "'.", reader);
386 throw new ConfigurationErrorsException ("Property '" + prop.Name + "' is not a ConfigurationElement.");
388 if (readProps.Contains (prop))
389 throw new ConfigurationErrorsException ("The element <" + prop.Name + "> may only appear once in this section.", reader);
391 ConfigurationElement val = (ConfigurationElement) prop.Value;
392 val.DeserializeElement (reader, serializeCollectionKey);
393 readProps [prop] = prop.Name;
395 if(depth == reader.Depth)
398 } while (depth < reader.Depth);
403 foreach (PropertyInformation prop in ElementInformation.Properties)
404 if (!String.IsNullOrEmpty(prop.Name) && prop.IsRequired && !readProps.ContainsKey (prop)) {
405 PropertyInformation p = ElementInformation.Properties [prop.Name];
407 object val = OnRequiredPropertyNotFound (prop.Name);
408 if (!object.Equals (val, prop.DefaultValue)) {
410 prop.IsModified = false;
418 protected virtual bool OnDeserializeUnrecognizedAttribute (string name, string value)
423 protected virtual bool OnDeserializeUnrecognizedElement (string element, XmlReader reader)
428 protected virtual object OnRequiredPropertyNotFound (string name)
430 throw new ConfigurationErrorsException ("Required attribute '" + name + "' not found.");
433 protected virtual void PreSerialize (XmlWriter writer)
437 protected virtual void PostDeserialize ()
441 protected internal virtual void InitializeDefault ()
445 protected internal virtual bool IsModified ()
450 protected internal virtual void SetReadOnly ()
455 public virtual bool IsReadOnly ()
460 protected internal virtual void Reset (ConfigurationElement parentElement)
462 elementPresent = false;
464 if (parentElement != null)
465 ElementInformation.Reset (parentElement.ElementInformation);
467 InitializeDefault ();
470 protected internal virtual void ResetModified ()
473 foreach (PropertyInformation p in ElementInformation.Properties)
474 p.IsModified = false;
477 protected internal virtual bool SerializeElement (XmlWriter writer, bool serializeCollectionKey)
479 PreSerialize (writer);
481 if (serializeCollectionKey) {
482 ConfigurationPropertyCollection props = GetKeyProperties ();
483 foreach (ConfigurationProperty prop in props)
484 writer.WriteAttributeString (prop.Name, prop.ConvertToString (this[prop.Name]));
485 return props.Count > 0;
488 bool wroteData = false;
490 foreach (PropertyInformation prop in ElementInformation.Properties)
492 if (prop.IsElement || prop.ValueOrigin == PropertyValueOrigin.Default)
495 if (!object.Equals (prop.Value, prop.DefaultValue)) {
496 writer.WriteAttributeString (prop.Name, prop.GetStringValue ());
501 foreach (PropertyInformation prop in ElementInformation.Properties)
506 ConfigurationElement val = (ConfigurationElement) prop.Value;
508 wroteData = val.SerializeToXmlElement (writer, prop.Name) || wroteData;
513 protected internal virtual bool SerializeToXmlElement (
514 XmlWriter writer, string elementName)
519 if (elementName != null && elementName != "")
520 writer.WriteStartElement (elementName);
521 bool res = SerializeElement (writer, false);
522 if (elementName != null && elementName != "")
523 writer.WriteEndElement ();
527 protected internal virtual void Unmerge (
528 ConfigurationElement source, ConfigurationElement parent,
529 ConfigurationSaveMode updateMode)
531 if (parent != null && source.GetType() != parent.GetType())
532 throw new ConfigurationErrorsException ("Can't unmerge two elements of different type");
534 foreach (PropertyInformation prop in source.ElementInformation.Properties)
536 if (prop.ValueOrigin == PropertyValueOrigin.Default)
539 PropertyInformation unmergedProp = ElementInformation.Properties [prop.Name];
541 object sourceValue = prop.Value;
542 if (parent == null || !parent.HasValue (prop.Name)) {
543 unmergedProp.Value = sourceValue;
546 else if (sourceValue != null) {
547 object parentValue = parent [prop.Name];
548 if (prop.IsElement) {
549 if (parentValue != null) {
550 ConfigurationElement copy = (ConfigurationElement) unmergedProp.Value;
551 copy.Unmerge ((ConfigurationElement) sourceValue, (ConfigurationElement) parentValue, updateMode);
554 unmergedProp.Value = sourceValue;
557 if (!object.Equals (sourceValue, parentValue) ||
558 (updateMode == ConfigurationSaveMode.Full) ||
559 (updateMode == ConfigurationSaveMode.Modified && prop.ValueOrigin == PropertyValueOrigin.SetHere))
560 unmergedProp.Value = sourceValue;
566 internal bool HasValue (string propName)
568 PropertyInformation info = ElementInformation.Properties [propName];
569 return info != null && info.ValueOrigin != PropertyValueOrigin.Default;
572 internal bool IsReadFromConfig (string propName)
574 PropertyInformation info = ElementInformation.Properties [propName];
575 return info != null && info.ValueOrigin == PropertyValueOrigin.SetHere;
578 internal bool IsElementPresent
580 get { return elementPresent; }
583 void ValidateValue (ConfigurationProperty p, string value)
585 ConfigurationValidatorBase validator;
586 if (p == null || (validator = p.Validator) == null)
589 if (!validator.CanValidate (p.Type))
590 throw new ConfigurationErrorsException (
591 String.Format ("Validator does not support type {0}", p.Type));
592 validator.Validate (p.ConvertFromString (value));
596 internal class ElementMap
599 const string elementMapsKey = "ElementMap_elementMaps";
600 static Hashtable elementMaps
604 Hashtable tbl = (Hashtable) AppDomain.CurrentDomain.GetData (elementMapsKey);
606 lock (typeof (ElementMap)) {
607 tbl = (Hashtable) AppDomain.CurrentDomain.GetData (elementMapsKey);
609 tbl = Hashtable.Synchronized (new Hashtable ());
610 AppDomain.CurrentDomain.SetData (elementMapsKey, tbl);
618 static readonly Hashtable elementMaps = Hashtable.Synchronized (new Hashtable ());
621 readonly ConfigurationPropertyCollection properties;
622 readonly ConfigurationCollectionAttribute collectionAttribute;
624 public static ElementMap GetMap (Type t)
626 ElementMap map = elementMaps [t] as ElementMap;
627 if (map != null) return map;
628 map = new ElementMap (t);
629 elementMaps [t] = map;
633 public ElementMap (Type t)
635 properties = new ConfigurationPropertyCollection ();
637 collectionAttribute = Attribute.GetCustomAttribute (t, typeof(ConfigurationCollectionAttribute)) as ConfigurationCollectionAttribute;
639 PropertyInfo[] props = t.GetProperties (BindingFlags.Public|BindingFlags.NonPublic|BindingFlags.Static|BindingFlags.Instance);
640 foreach (PropertyInfo prop in props)
642 ConfigurationPropertyAttribute at = Attribute.GetCustomAttribute (prop, typeof(ConfigurationPropertyAttribute)) as ConfigurationPropertyAttribute;
643 if (at == null) continue;
644 string name = at.Name != null ? at.Name : prop.Name;
646 ConfigurationValidatorAttribute validatorAttr = Attribute.GetCustomAttribute (prop, typeof (ConfigurationValidatorAttribute)) as ConfigurationValidatorAttribute;
647 ConfigurationValidatorBase validator = validatorAttr != null ? validatorAttr.ValidatorInstance : null;
649 TypeConverterAttribute convertAttr = (TypeConverterAttribute) Attribute.GetCustomAttribute (prop, typeof (TypeConverterAttribute));
650 TypeConverter converter = convertAttr != null ? (TypeConverter) Activator.CreateInstance (Type.GetType (convertAttr.ConverterTypeName), true) : null;
651 ConfigurationProperty cp = new ConfigurationProperty (name, prop.PropertyType, at.DefaultValue, converter, validator, at.Options);
653 cp.CollectionAttribute = Attribute.GetCustomAttribute (prop, typeof(ConfigurationCollectionAttribute)) as ConfigurationCollectionAttribute;
658 public ConfigurationCollectionAttribute CollectionAttribute
660 get { return collectionAttribute; }
663 public bool HasProperties
665 get { return properties.Count > 0; }
668 public ConfigurationPropertyCollection Properties