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; }
70 set { rawXml = value; }
73 protected internal virtual void Init ()
77 protected internal virtual ConfigurationElementProperty ElementProperty {
79 if (elementProperty == null)
80 elementProperty = new ConfigurationElementProperty (ElementInformation.Validator);
81 return elementProperty;
86 protected ContextInformation EvaluationContext {
88 throw new NotImplementedException ();
93 public ConfigurationLockCollection LockAllAttributesExcept {
95 throw new NotImplementedException ();
100 public ConfigurationLockCollection LockAllElementsExcept {
102 throw new NotImplementedException ();
107 ConfigurationLockCollection lockAttributes;
108 public ConfigurationLockCollection LockAttributes {
110 if (lockAttributes == null) {
111 lockAttributes = new ConfigurationLockCollection (this, ConfigurationLockType.Attribute);
114 return lockAttributes;
119 public ConfigurationLockCollection LockElements {
121 throw new NotImplementedException ();
126 public bool LockItem {
128 throw new NotImplementedException ();
131 throw new NotImplementedException ();
136 public void ListErrors (IList list)
138 throw new NotImplementedException ();
142 public void SetPropertyValue (ConfigurationProperty prop, object value, bool ignoreLocks)
145 /* XXX all i know for certain is that Validation happens here */
146 prop.Validate (value);
148 /* XXX presumably the actual setting of the
149 * property happens here instead of in the
150 * set_Item code below, but that would mean
151 * the Value needs to be stuffed in the
152 * property, not the propertyinfo (or else the
153 * property needs a ref to the property info
154 * to correctly set the value). */
156 catch (Exception e) {
157 throw new ConfigurationErrorsException (String.Format ("The value for the property '{0}' is not valid. The error is: {1}", prop.Name, e.Message), e);
161 internal ConfigurationPropertyCollection GetKeyProperties ()
163 if (keyProps != null) return keyProps;
165 if (map != null && map.Properties == Properties)
166 keyProps = map.KeyProperties;
168 keyProps = new ConfigurationPropertyCollection ();
169 foreach (ConfigurationProperty prop in Properties) {
177 internal ConfigurationElementCollection GetDefaultCollection ()
179 if (defaultCollection != null) return defaultCollection;
181 ConfigurationProperty defaultCollectionProp = null;
183 if (map != null && map.Properties == Properties) {
184 defaultCollectionProp = map.DefaultCollectionProperty;
187 foreach (ConfigurationProperty prop in Properties) {
188 if (prop.IsDefaultCollection) {
189 defaultCollectionProp = prop;
195 if (defaultCollectionProp != null) {
196 defaultCollection = this [defaultCollectionProp] as ConfigurationElementCollection;
199 return defaultCollection;
202 protected internal object this [ConfigurationProperty property] {
203 get { return this [property.Name]; }
204 set { this [property.Name] = value; }
207 protected internal object this [string property_name] {
209 PropertyInformation pi = ElementInformation.Properties [property_name];
211 throw new InvalidOperationException ("Property '" + property_name + "' not found in configuration element");
217 PropertyInformation pi = ElementInformation.Properties [property_name];
219 throw new InvalidOperationException ("Property '" + property_name + "' not found in configuration element");
221 SetPropertyValue (pi.Property, value, false);
228 protected internal virtual ConfigurationPropertyCollection Properties {
231 map = ElementMap.GetMap (GetType());
232 return map.Properties;
236 public override bool Equals (object compareTo)
238 ConfigurationElement other = compareTo as ConfigurationElement;
239 if (other == null) return false;
240 if (GetType() != other.GetType()) return false;
242 foreach (ConfigurationProperty prop in Properties) {
243 if (!object.Equals (this [prop], other [prop]))
249 public override int GetHashCode ()
252 foreach (ConfigurationProperty prop in Properties)
253 code += this [prop].GetHashCode ();
257 internal virtual bool HasValues ()
259 foreach (PropertyInformation pi in ElementInformation.Properties)
260 if (pi.ValueOrigin != PropertyValueOrigin.Default)
265 protected internal virtual void DeserializeElement (XmlReader reader, bool serializeCollectionKey)
267 Hashtable readProps = new Hashtable ();
269 reader.MoveToContent ();
270 while (reader.MoveToNextAttribute ())
272 PropertyInformation prop = ElementInformation.Properties [reader.LocalName];
273 if (prop == null || (serializeCollectionKey && !prop.IsKey)) {
274 if (!OnDeserializeUnrecognizedAttribute (reader.LocalName, reader.Value))
275 throw new ConfigurationException ("Unrecognized attribute '" + reader.LocalName + "'.");
279 if (readProps.ContainsKey (prop))
280 throw new ConfigurationException ("The attribute '" + prop.Name + "' may only appear once in this element.");
282 prop.SetStringValue (reader.Value);
283 readProps [prop] = prop.Name;
286 reader.MoveToElement ();
287 if (reader.IsEmptyElement) {
292 int depth = reader.Depth;
294 reader.ReadStartElement ();
295 reader.MoveToContent ();
298 if (reader.NodeType != XmlNodeType.Element) {
303 PropertyInformation prop = ElementInformation.Properties [reader.LocalName];
304 if (prop == null || (serializeCollectionKey && !prop.IsKey)) {
306 ConfigurationElementCollection c = GetDefaultCollection ();
307 if (c != null && c.OnDeserializeUnrecognizedElement (reader.LocalName, reader))
311 if (!OnDeserializeUnrecognizedElement (reader.LocalName, reader)) {
312 throw new ConfigurationException ("Unrecognized element '" + reader.LocalName + "'.");
318 throw new ConfigurationException ("Property '" + prop.Name + "' is not a ConfigurationElement.");
320 if (readProps.Contains (prop))
321 throw new ConfigurationException ("The element <" + prop.Name + "> may only appear once in this section.");
323 ConfigurationElement val = (ConfigurationElement) prop.Value;
324 val.DeserializeElement (reader, serializeCollectionKey);
325 readProps [prop] = prop.Name;
329 } while (depth < reader.Depth);
331 if (reader.NodeType == XmlNodeType.EndElement)
337 foreach (PropertyInformation prop in ElementInformation.Properties)
338 if (prop.IsRequired && !readProps.ContainsKey (prop)) {
339 object val = OnRequiredPropertyNotFound (prop.Name);
340 if (!object.Equals (val, prop.DefaultValue)) {
342 prop.IsModified = false;
349 protected virtual bool OnDeserializeUnrecognizedAttribute (string name, string value)
354 protected virtual bool OnDeserializeUnrecognizedElement (string element, XmlReader reader)
359 protected virtual object OnRequiredPropertyNotFound (string name)
361 throw new ConfigurationErrorsException ("Required attribute '" + name + "' not found.");
364 protected virtual void PreSerialize (XmlWriter writer)
368 protected virtual void PostDeserialize ()
372 protected internal virtual void InitializeDefault ()
376 protected internal virtual bool IsModified ()
381 protected internal virtual void SetReadOnly ()
386 public virtual bool IsReadOnly ()
391 protected internal virtual void Reset (ConfigurationElement parentElement)
393 if (parentElement != null)
394 ElementInformation.Reset (parentElement.ElementInformation);
396 InitializeDefault ();
399 protected internal virtual void ResetModified ()
402 foreach (PropertyInformation p in ElementInformation.Properties)
403 p.IsModified = false;
406 protected internal virtual bool SerializeElement (XmlWriter writer, bool serializeCollectionKey)
408 PreSerialize (writer);
410 if (serializeCollectionKey) {
411 ConfigurationPropertyCollection props = GetKeyProperties ();
412 foreach (ConfigurationProperty prop in props)
413 writer.WriteAttributeString (prop.Name, prop.ConvertToString (this[prop.Name]));
414 return props.Count > 0;
417 bool wroteData = false;
419 foreach (PropertyInformation prop in ElementInformation.Properties)
421 if (prop.IsElement || prop.ValueOrigin == PropertyValueOrigin.Default)
424 if (!object.Equals (prop.Value, prop.DefaultValue)) {
425 writer.WriteAttributeString (prop.Name, prop.GetStringValue ());
430 foreach (PropertyInformation prop in ElementInformation.Properties)
435 ConfigurationElement val = (ConfigurationElement) prop.Value;
436 if (val != null && val.HasValues ()) {
437 wroteData = val.SerializeToXmlElement (writer, prop.Name) || wroteData;
443 protected internal virtual bool SerializeToXmlElement (
444 XmlWriter writer, string elementName)
446 writer.WriteStartElement (elementName);
447 bool res = SerializeElement (writer, false);
448 writer.WriteEndElement ();
452 protected internal virtual void Unmerge (
453 ConfigurationElement source, ConfigurationElement parent,
454 ConfigurationSaveMode updateMode)
456 if (parent != null && source.GetType() != parent.GetType())
457 throw new ConfigurationException ("Can't unmerge two elements of different type");
459 foreach (PropertyInformation prop in source.ElementInformation.Properties)
461 if (prop.ValueOrigin == PropertyValueOrigin.Default)
464 PropertyInformation unmergedProp = ElementInformation.Properties [prop.Name];
466 object sourceValue = prop.Value;
467 if (parent == null || !parent.HasValue (prop.Name)) {
468 unmergedProp.Value = sourceValue;
471 else if (sourceValue != null) {
472 object parentValue = parent [prop.Name];
473 if (prop.IsElement) {
474 if (parentValue != null) {
475 ConfigurationElement copy = (ConfigurationElement) unmergedProp.Value;
476 copy.Unmerge ((ConfigurationElement) sourceValue, (ConfigurationElement) parentValue, updateMode);
479 unmergedProp.Value = sourceValue;
482 if (!object.Equals (sourceValue, parentValue) ||
483 (updateMode == ConfigurationSaveMode.Full) ||
484 (updateMode == ConfigurationSaveMode.Modified && prop.ValueOrigin == PropertyValueOrigin.SetHere))
485 unmergedProp.Value = sourceValue;
491 internal bool HasValue (string propName)
493 PropertyInformation info = ElementInformation.Properties [propName];
494 return info != null && info.ValueOrigin != PropertyValueOrigin.Default;
497 internal bool IsReadFromConfig (string propName)
499 PropertyInformation info = ElementInformation.Properties [propName];
500 return info != null && info.ValueOrigin == PropertyValueOrigin.SetHere;
504 internal class ElementMap
506 static Hashtable elementMaps = new Hashtable ();
508 ConfigurationPropertyCollection properties;
509 ConfigurationPropertyCollection keyProperties;
510 ConfigurationProperty defaultCollectionProperty;
512 ConfigurationCollectionAttribute collectionAttribute;
514 public static ElementMap GetMap (Type t)
517 ElementMap map = elementMaps [t] as ElementMap;
518 if (map != null) return map;
519 map = new ElementMap (t);
520 elementMaps [t] = map;
525 public ElementMap (Type t)
527 ReflectProperties (t);
530 protected void ReflectProperties (Type t)
532 collectionAttribute = Attribute.GetCustomAttribute (t, typeof(ConfigurationCollectionAttribute)) as ConfigurationCollectionAttribute;
534 PropertyInfo[] props = t.GetProperties ();
535 foreach (PropertyInfo prop in props)
537 ConfigurationPropertyAttribute at = Attribute.GetCustomAttribute (prop, typeof(ConfigurationPropertyAttribute)) as ConfigurationPropertyAttribute;
538 if (at == null) continue;
539 string name = at.Name != null ? at.Name : prop.Name;
542 /* if we have no default value, don't bother to check further */
543 at.DefaultValue != null && at.DefaultValue != ConfigurationProperty.NoDefaultValue
547 Convert.ChangeType (at.DefaultValue, prop.PropertyType);
550 throw new ConfigurationErrorsException (String.Format ("The default value for property '{0}' has a different type than the one of the property itself",
555 ConfigurationValidatorAttribute validatorAttr = Attribute.GetCustomAttribute (t, typeof(ConfigurationValidatorAttribute)) as ConfigurationValidatorAttribute;
556 ConfigurationValidatorBase validator = validatorAttr != null ? validatorAttr.ValidatorInstance : new DefaultValidator();
558 TypeConverter converter = TypeDescriptor.GetConverter (prop.PropertyType);
559 ConfigurationProperty cp = new ConfigurationProperty (name, prop.PropertyType, at.DefaultValue, converter, validator, at.Options);
561 cp.CollectionAttribute = Attribute.GetCustomAttribute (prop, typeof(ConfigurationCollectionAttribute)) as ConfigurationCollectionAttribute;
563 if (properties == null) properties = new ConfigurationPropertyCollection ();
568 public bool HasProperties
570 get { return properties != null && properties.Count > 0; }
573 public ConfigurationPropertyCollection Properties
576 if (properties == null) properties = new ConfigurationPropertyCollection ();
581 public ConfigurationPropertyCollection KeyProperties {
583 if (keyProperties == null) {
584 keyProperties = new ConfigurationPropertyCollection ();
586 if (properties != null)
587 foreach (ConfigurationProperty p in properties)
588 if (p.IsKey) keyProperties.Add (p);
590 return keyProperties;
594 public ConfigurationCollectionAttribute CollectionAttribute {
595 get { return collectionAttribute; }
598 public ConfigurationProperty DefaultCollectionProperty {
600 if (defaultCollectionProperty == null) {
601 if (properties != null)
602 foreach (ConfigurationProperty p in properties) {
603 if (p.IsDefaultCollection) defaultCollectionProperty = p;
607 return defaultCollectionProperty;