merge r67228-r67235, r67237, r67251 and r67256-67259 to trunk (they are
[mono.git] / mcs / class / System.Configuration / System.Configuration / ConfigurationElement.cs
1 //
2 // System.Configuration.ConfigurationElement.cs
3 //
4 // Authors:
5 //      Duncan Mak (duncan@ximian.com)
6 //      Lluis Sanchez Gual (lluis@novell.com)
7 //
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:
15 // 
16 // The above copyright notice and this permission notice shall be
17 // included in all copies or substantial portions of the Software.
18 // 
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.
26 //
27 // Copyright (C) 2004 Novell, Inc (http://www.novell.com)
28 //
29
30 #if NET_2_0
31 using System.Collections;
32 using System.Xml;
33 using System.Reflection;
34 using System.IO;
35 using System.ComponentModel;
36
37 namespace System.Configuration
38 {
39         public abstract class ConfigurationElement
40         {
41                 string rawXml;
42                 bool modified;
43                 ElementMap map;
44                 ConfigurationPropertyCollection keyProps;
45                 ConfigurationElementCollection defaultCollection;
46                 bool readOnly;
47                 ElementInformation elementInfo;
48                 ConfigurationElementProperty elementProperty;
49
50                 protected ConfigurationElement ()
51                 {
52                 }
53                 
54                 internal virtual void InitFromProperty (PropertyInformation propertyInfo)
55                 {
56                         elementInfo = new ElementInformation (this, propertyInfo);
57                         Init ();
58                 }
59                 
60                 public ElementInformation ElementInformation {
61                         get {
62                                 if (elementInfo == null)
63                                         elementInfo = new ElementInformation (this, null);
64                                 return elementInfo;
65                         }
66                 }
67
68                 internal string RawXml {
69                         get { return rawXml; }
70                         set {
71                                 // FIXME: this hack is nasty. We should make
72                                 // some refactory on the entire assembly.
73                                 if (rawXml == null || value != null)
74                                         rawXml = value;
75                         }
76                 }
77
78                 protected internal virtual void Init ()
79                 {
80                 }
81
82                 protected internal virtual ConfigurationElementProperty ElementProperty {
83                         get {
84                                 if (elementProperty == null)
85                                         elementProperty = new ConfigurationElementProperty (ElementInformation.Validator);
86                                 return elementProperty;
87                         }
88                 }
89
90                 [MonoTODO]
91                 protected ContextInformation EvaluationContext {
92                         get {
93                                 throw new NotImplementedException ();
94                         }
95                 }
96
97                 ConfigurationLockCollection lockAllAttributesExcept;
98                 public ConfigurationLockCollection LockAllAttributesExcept {
99                         get {
100                                 if (lockAllAttributesExcept == null) {
101                                         lockAllAttributesExcept = new ConfigurationLockCollection (this, ConfigurationLockType.Attribute | ConfigurationLockType.Exclude);
102                                 }
103
104                                 return lockAllAttributesExcept;
105                         }
106                 }
107
108                 ConfigurationLockCollection lockAllElementsExcept;
109                 public ConfigurationLockCollection LockAllElementsExcept {
110                         get {
111                                 if (lockAllElementsExcept == null) {
112                                         lockAllElementsExcept = new ConfigurationLockCollection (this, ConfigurationLockType.Element | ConfigurationLockType.Exclude);
113                                 }
114
115                                 return lockAllElementsExcept;
116                         }
117                 }
118
119                 ConfigurationLockCollection lockAttributes;
120                 public ConfigurationLockCollection LockAttributes {
121                         get {
122                                 if (lockAttributes == null) {
123                                         lockAttributes = new ConfigurationLockCollection (this, ConfigurationLockType.Attribute);
124                                 }
125
126                                 return lockAttributes;
127                         }
128                 }
129
130                 ConfigurationLockCollection lockElements;
131                 public ConfigurationLockCollection LockElements {
132                         get {
133                                 if (lockElements == null) {
134                                         lockElements = new ConfigurationLockCollection (this, ConfigurationLockType.Element);
135                                 }
136
137                                 return lockElements;
138                         }
139                 }
140
141                 bool lockItem;
142                 public bool LockItem {
143                         get { return lockItem; }
144                         set { lockItem = value; }
145                 }
146
147                 [MonoTODO]
148                 protected virtual void ListErrors (IList list)
149                 {
150                         throw new NotImplementedException ();
151                 }
152
153                 [MonoTODO]
154                 protected void SetPropertyValue (ConfigurationProperty prop, object value, bool ignoreLocks)
155                 {
156                         try {
157                                 /* XXX all i know for certain is that Validation happens here */
158                                 prop.Validate (value);
159
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). */
167                         }
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);
170                         }
171                 }
172
173                 internal ConfigurationPropertyCollection GetKeyProperties ()
174                 {
175                         if (keyProps != null) return keyProps;
176                         
177                         if (map != null && map.Properties == Properties)
178                                 keyProps = map.KeyProperties;
179                         else {
180                                 keyProps = new ConfigurationPropertyCollection ();
181                                 foreach (ConfigurationProperty prop in Properties) {
182                                         if (prop.IsKey)
183                                                 keyProps.Add (prop);
184                                 }
185                         }
186                         return keyProps;
187                 }
188
189                 internal ConfigurationElementCollection GetDefaultCollection ()
190                 {
191                         if (defaultCollection != null) return defaultCollection;
192
193                         ConfigurationProperty defaultCollectionProp = null;
194
195                         if (map != null && map.Properties == Properties) {
196                                 defaultCollectionProp = map.DefaultCollectionProperty;
197                         }
198                         else {
199                                 foreach (ConfigurationProperty prop in Properties) {
200                                         if (prop.IsDefaultCollection) {
201                                                 defaultCollectionProp = prop;
202                                                 break;
203                                         }
204                                 }
205                         }
206
207                         if (defaultCollectionProp != null) {
208                                 defaultCollection = this [defaultCollectionProp] as ConfigurationElementCollection;
209                         }
210
211                         return defaultCollection;
212                 }
213
214                 protected internal object this [ConfigurationProperty property] {
215                         get { return this [property.Name]; }
216                         set { this [property.Name] = value; }
217                 }
218
219                 protected internal object this [string property_name] {
220                         get {
221                                 PropertyInformation pi = ElementInformation.Properties [property_name];
222                                 if (pi == null)
223                                         throw new InvalidOperationException ("Property '" + property_name + "' not found in configuration element");
224
225                                 return pi.Value;
226                         }
227
228                         set {
229                                 PropertyInformation pi = ElementInformation.Properties [property_name];
230                                 if (pi == null)
231                                         throw new InvalidOperationException ("Property '" + property_name + "' not found in configuration element");
232
233                                 SetPropertyValue (pi.Property, value, false);
234
235                                 pi.Value = value;
236                                 modified = true;
237                         }
238                 }
239
240                 protected internal virtual ConfigurationPropertyCollection Properties {
241                         get {
242                                 if (map == null)
243                                         map = ElementMap.GetMap (GetType());
244                                 return map.Properties;
245                         }
246                 }
247
248                 public override bool Equals (object compareTo)
249                 {
250                         ConfigurationElement other = compareTo as ConfigurationElement;
251                         if (other == null) return false;
252                         if (GetType() != other.GetType()) return false;
253                         
254                         foreach (ConfigurationProperty prop in Properties) {
255                                 if (!object.Equals (this [prop], other [prop]))
256                                         return false;
257                         }
258                         return true;
259                 }
260
261                 public override int GetHashCode ()
262                 {
263                         int code = 0;
264                         foreach (ConfigurationProperty prop in Properties)
265                                 code += this [prop].GetHashCode ();
266                         return code;
267                 }
268
269                 internal virtual bool HasValues ()
270                 {
271                         foreach (PropertyInformation pi in ElementInformation.Properties)
272                                 if (pi.ValueOrigin != PropertyValueOrigin.Default)
273                                         return true;
274                         return false;
275                 }
276                 
277                 protected internal virtual void DeserializeElement (XmlReader reader, bool serializeCollectionKey)
278                 {
279                         Hashtable readProps = new Hashtable ();
280                         
281                         reader.MoveToContent ();
282                         while (reader.MoveToNextAttribute ())
283                         {
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);
289                                         }
290                                         else if (reader.LocalName == "lockAllElementsExcept") {
291                                                 LockAllElementsExcept.SetFromList (reader.Value);
292                                         }
293                                         else if (reader.LocalName == "lockAttributes") {
294                                                 LockAttributes.SetFromList (reader.Value);
295                                         }
296                                         else if (reader.LocalName == "lockElements") {
297                                                 LockElements.SetFromList (reader.Value);
298                                         }
299                                         else if (reader.LocalName == "lockItem") {
300                                                 LockItem = (reader.Value.ToLower() == "true");
301                                         }
302                                         else if (reader.LocalName == "xmlns") {
303                                                 /* ignore */
304                                         }
305                                         else if (!OnDeserializeUnrecognizedAttribute (reader.LocalName, reader.Value))
306                                                 throw new ConfigurationException ("Unrecognized attribute '" + reader.LocalName + "'.");
307
308                                         continue;
309                                 }
310                                 
311                                 if (readProps.ContainsKey (prop))
312                                         throw new ConfigurationException ("The attribute '" + prop.Name + "' may only appear once in this element.");
313                                 
314                                 prop.SetStringValue (reader.Value);
315                                 readProps [prop] = prop.Name;
316                         }
317                         
318                         reader.MoveToElement ();
319                         if (reader.IsEmptyElement) {
320                                 reader.Skip ();
321                         }
322                         else {
323
324                                 int depth = reader.Depth;
325
326                                 reader.ReadStartElement ();
327                                 reader.MoveToContent ();
328
329                                 do {
330                                         if (reader.NodeType != XmlNodeType.Element) {
331                                                 reader.Skip ();
332                                                 continue;
333                                         }
334                                         
335                                         PropertyInformation prop = ElementInformation.Properties [reader.LocalName];
336                                         if (prop == null || (serializeCollectionKey && !prop.IsKey)) {
337                                                 if (prop == null) {
338                                                         ConfigurationElementCollection c = GetDefaultCollection ();
339                                                         if (c != null && c.OnDeserializeUnrecognizedElement (reader.LocalName, reader))
340                                                                 continue;
341                                                 }
342
343                                                 if (!OnDeserializeUnrecognizedElement (reader.LocalName, reader)) {
344                                                         throw new ConfigurationException ("Unrecognized element '" + reader.LocalName + "'.");
345                                                 }
346                                                 continue;
347                                         }
348                                         
349                                         if (!prop.IsElement)
350                                                 throw new ConfigurationException ("Property '" + prop.Name + "' is not a ConfigurationElement.");
351                                         
352                                         if (readProps.Contains (prop))
353                                                 throw new ConfigurationException ("The element <" + prop.Name + "> may only appear once in this section.");
354                                         
355                                         ConfigurationElement val = (ConfigurationElement) prop.Value;
356                                         val.DeserializeElement (reader, serializeCollectionKey);
357                                         readProps [prop] = prop.Name;
358
359                                         reader.Read();
360
361                                 } while (depth < reader.Depth);
362
363                                 if (reader.NodeType == XmlNodeType.EndElement)
364                                         reader.Read ();
365                         }
366                         
367                         modified = false;
368                                 
369                         foreach (PropertyInformation prop in ElementInformation.Properties)
370                                 if (prop.IsRequired && !readProps.ContainsKey (prop)) {
371                                         object val = OnRequiredPropertyNotFound (prop.Name);
372                                         if (!object.Equals (val, prop.DefaultValue)) {
373                                                 prop.Value = val;
374                                                 prop.IsModified = false;
375                                         }
376                                 }
377
378                         PostDeserialize ();
379                 }
380
381                 protected virtual bool OnDeserializeUnrecognizedAttribute (string name, string value)
382                 {
383                         return false;
384                 }
385
386                 protected virtual bool OnDeserializeUnrecognizedElement (string element, XmlReader reader)
387                 {
388                         return false;
389                 }
390                 
391                 protected virtual object OnRequiredPropertyNotFound (string name)
392                 {
393                         throw new ConfigurationErrorsException ("Required attribute '" + name + "' not found.");
394                 }
395                 
396                 protected virtual void PreSerialize (XmlWriter writer)
397                 {
398                 }
399
400                 protected virtual void PostDeserialize ()
401                 {
402                 }
403
404                 protected internal virtual void InitializeDefault ()
405                 {
406                 }
407
408                 protected internal virtual bool IsModified ()
409                 {
410                         return modified;
411                 }
412                 
413                 protected internal virtual void SetReadOnly ()
414                 {
415                         readOnly = true;
416                 }
417                 
418                 public virtual bool IsReadOnly ()
419                 {
420                         return readOnly;
421                 }
422
423                 protected internal virtual void Reset (ConfigurationElement parentElement)
424                 {
425                         if (parentElement != null)
426                                 ElementInformation.Reset (parentElement.ElementInformation);
427                         else
428                                 InitializeDefault ();
429                 }
430
431                 protected internal virtual void ResetModified ()
432                 {
433                         modified = false;
434                         foreach (PropertyInformation p in ElementInformation.Properties)
435                                 p.IsModified = false;
436                 }
437
438                 protected internal virtual bool SerializeElement (XmlWriter writer, bool serializeCollectionKey)
439                 {
440                         PreSerialize (writer);
441                         
442                         if (serializeCollectionKey) {
443                                 ConfigurationPropertyCollection props = GetKeyProperties ();
444                                 foreach (ConfigurationProperty prop in props)
445                                         writer.WriteAttributeString (prop.Name, prop.ConvertToString (this[prop.Name]));
446                                 return props.Count > 0;
447                         }
448                         
449                         bool wroteData = false;
450                         
451                         foreach (PropertyInformation prop in ElementInformation.Properties)
452                         {
453                                 if (prop.IsElement || prop.ValueOrigin == PropertyValueOrigin.Default)
454                                         continue;
455                                 
456                                 if (!object.Equals (prop.Value, prop.DefaultValue)) {
457                                         writer.WriteAttributeString (prop.Name, prop.GetStringValue ());
458                                         wroteData = true;
459                                 }
460                         }
461                         
462                         foreach (PropertyInformation prop in ElementInformation.Properties)
463                         {
464                                 if (!prop.IsElement)
465                                         continue;
466                                 
467                                 ConfigurationElement val = (ConfigurationElement) prop.Value;
468                                 if (val != null && val.HasValues ()) {
469                                         wroteData = val.SerializeToXmlElement (writer, prop.Name) || wroteData;
470                                 }
471                         }
472                         return wroteData;
473                 }
474                                 
475                 protected internal virtual bool SerializeToXmlElement (
476                                 XmlWriter writer, string elementName)
477                 {
478                         if (elementName != null && elementName != "")
479                                 writer.WriteStartElement (elementName);
480                         bool res = SerializeElement (writer, false);
481                         if (elementName != null && elementName != "")
482                                 writer.WriteEndElement ();
483                         return res;
484                 }
485
486                 protected internal virtual void Unmerge (
487                                 ConfigurationElement source, ConfigurationElement parent,
488                                 ConfigurationSaveMode updateMode)
489                 {
490                         if (parent != null && source.GetType() != parent.GetType())
491                                 throw new ConfigurationException ("Can't unmerge two elements of different type");
492                         
493                         foreach (PropertyInformation prop in source.ElementInformation.Properties)
494                         {
495                                 if (prop.ValueOrigin == PropertyValueOrigin.Default)
496                                         continue;
497                                 
498                                 PropertyInformation unmergedProp = ElementInformation.Properties [prop.Name];
499                                 
500                                 object sourceValue = prop.Value;
501                                 if      (parent == null || !parent.HasValue (prop.Name)) {
502                                         unmergedProp.Value = sourceValue;
503                                         continue;
504                                 }
505                                 else if (sourceValue != null) {
506                                         object parentValue = parent [prop.Name];
507                                         if (prop.IsElement) {
508                                                 if (parentValue != null) {
509                                                         ConfigurationElement copy = (ConfigurationElement) unmergedProp.Value;
510                                                         copy.Unmerge ((ConfigurationElement) sourceValue, (ConfigurationElement) parentValue, updateMode);
511                                                 }
512                                                 else
513                                                         unmergedProp.Value = sourceValue;
514                                         }
515                                         else {
516                                                 if (!object.Equals (sourceValue, parentValue) || 
517                                                         (updateMode == ConfigurationSaveMode.Full) ||
518                                                         (updateMode == ConfigurationSaveMode.Modified && prop.ValueOrigin == PropertyValueOrigin.SetHere))
519                                                         unmergedProp.Value = sourceValue;
520                                         }
521                                 }
522                         }
523                 }
524                 
525                 internal bool HasValue (string propName)
526                 {
527                         PropertyInformation info = ElementInformation.Properties [propName];
528                         return info != null && info.ValueOrigin != PropertyValueOrigin.Default;
529                 }
530                 
531                 internal bool IsReadFromConfig (string propName)
532                 {
533                         PropertyInformation info = ElementInformation.Properties [propName];
534                         return info != null && info.ValueOrigin == PropertyValueOrigin.SetHere;
535                 }
536         }
537         
538         internal class ElementMap
539         {
540                 static Hashtable elementMaps = new Hashtable ();
541                 
542                 ConfigurationPropertyCollection properties;
543                 ConfigurationPropertyCollection keyProperties;
544                 ConfigurationProperty defaultCollectionProperty;
545
546                 ConfigurationCollectionAttribute collectionAttribute;
547                 
548                 public static ElementMap GetMap (Type t)
549                 {
550                         lock (elementMaps) {
551                                 ElementMap map = elementMaps [t] as ElementMap;
552                                 if (map != null) return map;
553                                 map = new ElementMap (t);
554                                 elementMaps [t] = map;
555                                 return map;
556                         }
557                 }
558                 
559                 public ElementMap (Type t)
560                 {
561                         ReflectProperties (t);
562                 }
563                 
564                 protected void ReflectProperties (Type t)
565                 {
566                         collectionAttribute = Attribute.GetCustomAttribute (t, typeof(ConfigurationCollectionAttribute)) as ConfigurationCollectionAttribute;
567                         
568                         PropertyInfo[] props = t.GetProperties ();
569                         foreach (PropertyInfo prop in props)
570                         {
571                                 ConfigurationPropertyAttribute at = Attribute.GetCustomAttribute (prop, typeof(ConfigurationPropertyAttribute)) as ConfigurationPropertyAttribute;
572                                 if (at == null) continue;
573                                 string name = at.Name != null ? at.Name : prop.Name;
574
575                                 if (
576                                     /* if we have no default value, don't bother to check further */
577                                     at.DefaultValue != null && at.DefaultValue != ConfigurationProperty.NoDefaultValue
578                                     )
579                                 {
580                                         try {
581                                                 Convert.ChangeType (at.DefaultValue, prop.PropertyType);
582                                         }
583                                         catch {
584                                                 throw new ConfigurationErrorsException (String.Format ("The default value for property '{0}' has a different type than the one of the property itself",
585                                                                                                        name));
586                                         }
587                                 }
588
589                                 ConfigurationValidatorAttribute validatorAttr = Attribute.GetCustomAttribute (t, typeof(ConfigurationValidatorAttribute)) as ConfigurationValidatorAttribute;
590                                 ConfigurationValidatorBase validator = validatorAttr != null ? validatorAttr.ValidatorInstance : new DefaultValidator();
591                                 
592                                 TypeConverter converter = TypeDescriptor.GetConverter (prop.PropertyType);
593                                 ConfigurationProperty cp = new ConfigurationProperty (name, prop.PropertyType, at.DefaultValue, converter, validator, at.Options);
594                                 
595                                 cp.CollectionAttribute = Attribute.GetCustomAttribute (prop, typeof(ConfigurationCollectionAttribute)) as ConfigurationCollectionAttribute;
596                                 
597                                 if (properties == null) properties = new ConfigurationPropertyCollection ();
598                                 properties.Add (cp);
599                         }
600                 }
601                 
602                 public bool HasProperties
603                 {
604                         get { return properties != null && properties.Count > 0; }
605                 }
606                 
607                 public ConfigurationPropertyCollection Properties
608                 {
609                         get {
610                                 if (properties == null) properties = new ConfigurationPropertyCollection ();
611                                 return properties;
612                         }
613                 }
614                 
615                 public ConfigurationPropertyCollection KeyProperties {
616                         get {
617                                 if (keyProperties == null) {
618                                         keyProperties = new ConfigurationPropertyCollection ();
619                                         
620                                         if (properties != null)
621                                                 foreach (ConfigurationProperty p in properties)
622                                                         if (p.IsKey) keyProperties.Add (p);
623                                 }
624                                 return keyProperties;
625                         }
626                 }
627                 
628                 public ConfigurationCollectionAttribute CollectionAttribute {
629                         get { return collectionAttribute; }
630                 }
631                 
632                 public ConfigurationProperty DefaultCollectionProperty {
633                         get {
634                                 if (defaultCollectionProperty == null) {
635                                         if (properties != null)
636                                                 foreach (ConfigurationProperty p in properties) {
637                                                         if (p.IsDefaultCollection) {
638                                                                 defaultCollectionProperty = p;
639                                                                 break;
640                                                         }
641                                                 }
642                                 }
643                                 return defaultCollectionProperty;
644                         }
645                 }
646         }
647 }
648
649 #endif