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;
352 if (_reader != null){
353 prop.Source = _reader.Filename;
354 prop.LineNumber = _reader.LineNumber;
358 reader.MoveToElement ();
359 if (reader.IsEmptyElement) {
362 int depth = reader.Depth;
364 reader.ReadStartElement ();
365 reader.MoveToContent ();
368 if (reader.NodeType != XmlNodeType.Element) {
373 PropertyInformation prop = ElementInformation.Properties [reader.LocalName];
374 if (prop == null || (serializeCollectionKey && !prop.IsKey)) {
375 if (!OnDeserializeUnrecognizedElement (reader.LocalName, reader)) {
377 ConfigurationElementCollection c = GetDefaultCollection ();
378 if (c != null && c.OnDeserializeUnrecognizedElement (reader.LocalName, reader))
381 throw new ConfigurationErrorsException ("Unrecognized element '" + reader.LocalName + "'.", reader);
387 throw new ConfigurationErrorsException ("Property '" + prop.Name + "' is not a ConfigurationElement.");
389 if (readProps.Contains (prop))
390 throw new ConfigurationErrorsException ("The element <" + prop.Name + "> may only appear once in this section.", reader);
392 ConfigurationElement val = (ConfigurationElement) prop.Value;
393 val.DeserializeElement (reader, serializeCollectionKey);
394 readProps [prop] = prop.Name;
396 if(depth == reader.Depth)
399 } while (depth < reader.Depth);
404 foreach (PropertyInformation prop in ElementInformation.Properties)
405 if (!String.IsNullOrEmpty(prop.Name) && prop.IsRequired && !readProps.ContainsKey (prop)) {
406 PropertyInformation p = ElementInformation.Properties [prop.Name];
408 object val = OnRequiredPropertyNotFound (prop.Name);
409 if (!object.Equals (val, prop.DefaultValue)) {
411 prop.IsModified = false;
419 protected virtual bool OnDeserializeUnrecognizedAttribute (string name, string value)
424 protected virtual bool OnDeserializeUnrecognizedElement (string element, XmlReader reader)
429 protected virtual object OnRequiredPropertyNotFound (string name)
431 throw new ConfigurationErrorsException ("Required attribute '" + name + "' not found.");
434 protected virtual void PreSerialize (XmlWriter writer)
438 protected virtual void PostDeserialize ()
442 protected internal virtual void InitializeDefault ()
446 protected internal virtual bool IsModified ()
451 protected internal virtual void SetReadOnly ()
456 public virtual bool IsReadOnly ()
461 protected internal virtual void Reset (ConfigurationElement parentElement)
463 elementPresent = false;
465 if (parentElement != null)
466 ElementInformation.Reset (parentElement.ElementInformation);
468 InitializeDefault ();
471 protected internal virtual void ResetModified ()
474 foreach (PropertyInformation p in ElementInformation.Properties)
475 p.IsModified = false;
478 protected internal virtual bool SerializeElement (XmlWriter writer, bool serializeCollectionKey)
480 PreSerialize (writer);
482 if (serializeCollectionKey) {
483 ConfigurationPropertyCollection props = GetKeyProperties ();
484 foreach (ConfigurationProperty prop in props)
485 writer.WriteAttributeString (prop.Name, prop.ConvertToString (this[prop.Name]));
486 return props.Count > 0;
489 bool wroteData = false;
491 foreach (PropertyInformation prop in ElementInformation.Properties)
493 if (prop.IsElement || prop.ValueOrigin == PropertyValueOrigin.Default)
496 if (!object.Equals (prop.Value, prop.DefaultValue)) {
497 writer.WriteAttributeString (prop.Name, prop.GetStringValue ());
502 foreach (PropertyInformation prop in ElementInformation.Properties)
507 ConfigurationElement val = (ConfigurationElement) prop.Value;
509 wroteData = val.SerializeToXmlElement (writer, prop.Name) || wroteData;
514 protected internal virtual bool SerializeToXmlElement (
515 XmlWriter writer, string elementName)
520 if (elementName != null && elementName != "")
521 writer.WriteStartElement (elementName);
522 bool res = SerializeElement (writer, false);
523 if (elementName != null && elementName != "")
524 writer.WriteEndElement ();
528 protected internal virtual void Unmerge (
529 ConfigurationElement source, ConfigurationElement parent,
530 ConfigurationSaveMode updateMode)
532 if (parent != null && source.GetType() != parent.GetType())
533 throw new ConfigurationErrorsException ("Can't unmerge two elements of different type");
535 foreach (PropertyInformation prop in source.ElementInformation.Properties)
537 if (prop.ValueOrigin == PropertyValueOrigin.Default)
540 PropertyInformation unmergedProp = ElementInformation.Properties [prop.Name];
542 object sourceValue = prop.Value;
543 if (parent == null || !parent.HasValue (prop.Name)) {
544 unmergedProp.Value = sourceValue;
547 else if (sourceValue != null) {
548 object parentValue = parent [prop.Name];
549 if (prop.IsElement) {
550 if (parentValue != null) {
551 ConfigurationElement copy = (ConfigurationElement) unmergedProp.Value;
552 copy.Unmerge ((ConfigurationElement) sourceValue, (ConfigurationElement) parentValue, updateMode);
555 unmergedProp.Value = sourceValue;
558 if (!object.Equals (sourceValue, parentValue) ||
559 (updateMode == ConfigurationSaveMode.Full) ||
560 (updateMode == ConfigurationSaveMode.Modified && prop.ValueOrigin == PropertyValueOrigin.SetHere))
561 unmergedProp.Value = sourceValue;
567 internal bool HasValue (string propName)
569 PropertyInformation info = ElementInformation.Properties [propName];
570 return info != null && info.ValueOrigin != PropertyValueOrigin.Default;
573 internal bool IsReadFromConfig (string propName)
575 PropertyInformation info = ElementInformation.Properties [propName];
576 return info != null && info.ValueOrigin == PropertyValueOrigin.SetHere;
579 internal bool IsElementPresent
581 get { return elementPresent; }
584 void ValidateValue (ConfigurationProperty p, string value)
586 ConfigurationValidatorBase validator;
587 if (p == null || (validator = p.Validator) == null)
590 if (!validator.CanValidate (p.Type))
591 throw new ConfigurationErrorsException (
592 String.Format ("Validator does not support type {0}", p.Type));
593 validator.Validate (p.ConvertFromString (value));
597 internal class ElementMap
600 const string elementMapsKey = "ElementMap_elementMaps";
601 static Hashtable elementMaps
605 Hashtable tbl = (Hashtable) AppDomain.CurrentDomain.GetData (elementMapsKey);
607 lock (typeof (ElementMap)) {
608 tbl = (Hashtable) AppDomain.CurrentDomain.GetData (elementMapsKey);
610 tbl = Hashtable.Synchronized (new Hashtable ());
611 AppDomain.CurrentDomain.SetData (elementMapsKey, tbl);
619 static readonly Hashtable elementMaps = Hashtable.Synchronized (new Hashtable ());
622 readonly ConfigurationPropertyCollection properties;
623 readonly ConfigurationCollectionAttribute collectionAttribute;
625 public static ElementMap GetMap (Type t)
627 ElementMap map = elementMaps [t] as ElementMap;
628 if (map != null) return map;
629 map = new ElementMap (t);
630 elementMaps [t] = map;
634 public ElementMap (Type t)
636 properties = new ConfigurationPropertyCollection ();
638 collectionAttribute = Attribute.GetCustomAttribute (t, typeof(ConfigurationCollectionAttribute)) as ConfigurationCollectionAttribute;
640 PropertyInfo[] props = t.GetProperties (BindingFlags.Public|BindingFlags.NonPublic|BindingFlags.Static|BindingFlags.Instance);
641 foreach (PropertyInfo prop in props)
643 ConfigurationPropertyAttribute at = Attribute.GetCustomAttribute (prop, typeof(ConfigurationPropertyAttribute)) as ConfigurationPropertyAttribute;
644 if (at == null) continue;
645 string name = at.Name != null ? at.Name : prop.Name;
647 ConfigurationValidatorAttribute validatorAttr = Attribute.GetCustomAttribute (prop, typeof (ConfigurationValidatorAttribute)) as ConfigurationValidatorAttribute;
648 ConfigurationValidatorBase validator = validatorAttr != null ? validatorAttr.ValidatorInstance : null;
650 TypeConverterAttribute convertAttr = (TypeConverterAttribute) Attribute.GetCustomAttribute (prop, typeof (TypeConverterAttribute));
651 TypeConverter converter = convertAttr != null ? (TypeConverter) Activator.CreateInstance (Type.GetType (convertAttr.ConverterTypeName), true) : null;
652 ConfigurationProperty cp = new ConfigurationProperty (name, prop.PropertyType, at.DefaultValue, converter, validator, at.Options);
654 cp.CollectionAttribute = Attribute.GetCustomAttribute (prop, typeof(ConfigurationCollectionAttribute)) as ConfigurationCollectionAttribute;
659 public ConfigurationCollectionAttribute CollectionAttribute
661 get { return collectionAttribute; }
664 public bool HasProperties
666 get { return properties.Count > 0; }
669 public ConfigurationPropertyCollection Properties