should be able instantiate types with private ctors
[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 (!OnDeserializeUnrecognizedElement (reader.LocalName, reader)) {
338                                                         if (prop == null) {
339                                                                 ConfigurationElementCollection c = GetDefaultCollection ();
340                                                                 if (c != null && c.OnDeserializeUnrecognizedElement (reader.LocalName, reader))
341                                                                         continue;
342                                                         }
343                                                         throw new ConfigurationException ("Unrecognized element '" + reader.LocalName + "'.");
344                                                 }
345                                                 continue;
346                                         }
347                                         
348                                         if (!prop.IsElement)
349                                                 throw new ConfigurationException ("Property '" + prop.Name + "' is not a ConfigurationElement.");
350                                         
351                                         if (readProps.Contains (prop))
352                                                 throw new ConfigurationException ("The element <" + prop.Name + "> may only appear once in this section.");
353                                         
354                                         ConfigurationElement val = (ConfigurationElement) prop.Value;
355                                         val.DeserializeElement (reader, serializeCollectionKey);
356                                         readProps [prop] = prop.Name;
357
358                                         reader.Read();
359
360                                 } while (depth < reader.Depth);
361
362                                 if (reader.NodeType == XmlNodeType.EndElement)
363                                         reader.Read ();
364                         }
365                         
366                         modified = false;
367                                 
368                         foreach (PropertyInformation prop in ElementInformation.Properties)
369                                 if (!String.IsNullOrEmpty(prop.Name) && prop.IsRequired && !readProps.ContainsKey (prop)) {
370                                         object val = OnRequiredPropertyNotFound (prop.Name);
371                                         if (!object.Equals (val, prop.DefaultValue)) {
372                                                 prop.Value = val;
373                                                 prop.IsModified = false;
374                                         }
375                                 }
376
377                         PostDeserialize ();
378                 }
379
380                 protected virtual bool OnDeserializeUnrecognizedAttribute (string name, string value)
381                 {
382                         return false;
383                 }
384
385                 protected virtual bool OnDeserializeUnrecognizedElement (string element, XmlReader reader)
386                 {
387                         return false;
388                 }
389                 
390                 protected virtual object OnRequiredPropertyNotFound (string name)
391                 {
392                         throw new ConfigurationErrorsException ("Required attribute '" + name + "' not found.");
393                 }
394                 
395                 protected virtual void PreSerialize (XmlWriter writer)
396                 {
397                 }
398
399                 protected virtual void PostDeserialize ()
400                 {
401                 }
402
403                 protected internal virtual void InitializeDefault ()
404                 {
405                 }
406
407                 protected internal virtual bool IsModified ()
408                 {
409                         return modified;
410                 }
411                 
412                 protected internal virtual void SetReadOnly ()
413                 {
414                         readOnly = true;
415                 }
416                 
417                 public virtual bool IsReadOnly ()
418                 {
419                         return readOnly;
420                 }
421
422                 protected internal virtual void Reset (ConfigurationElement parentElement)
423                 {
424                         if (parentElement != null)
425                                 ElementInformation.Reset (parentElement.ElementInformation);
426                         else
427                                 InitializeDefault ();
428                 }
429
430                 protected internal virtual void ResetModified ()
431                 {
432                         modified = false;
433                         foreach (PropertyInformation p in ElementInformation.Properties)
434                                 p.IsModified = false;
435                 }
436
437                 protected internal virtual bool SerializeElement (XmlWriter writer, bool serializeCollectionKey)
438                 {
439                         PreSerialize (writer);
440                         
441                         if (serializeCollectionKey) {
442                                 ConfigurationPropertyCollection props = GetKeyProperties ();
443                                 foreach (ConfigurationProperty prop in props)
444                                         writer.WriteAttributeString (prop.Name, prop.ConvertToString (this[prop.Name]));
445                                 return props.Count > 0;
446                         }
447                         
448                         bool wroteData = false;
449                         
450                         foreach (PropertyInformation prop in ElementInformation.Properties)
451                         {
452                                 if (prop.IsElement || prop.ValueOrigin == PropertyValueOrigin.Default)
453                                         continue;
454                                 
455                                 if (!object.Equals (prop.Value, prop.DefaultValue)) {
456                                         writer.WriteAttributeString (prop.Name, prop.GetStringValue ());
457                                         wroteData = true;
458                                 }
459                         }
460                         
461                         foreach (PropertyInformation prop in ElementInformation.Properties)
462                         {
463                                 if (!prop.IsElement)
464                                         continue;
465                                 
466                                 ConfigurationElement val = (ConfigurationElement) prop.Value;
467                                 if (val != null && val.HasValues ()) {
468                                         wroteData = val.SerializeToXmlElement (writer, prop.Name) || wroteData;
469                                 }
470                         }
471                         return wroteData;
472                 }
473                                 
474                 protected internal virtual bool SerializeToXmlElement (
475                                 XmlWriter writer, string elementName)
476                 {
477                         if (elementName != null && elementName != "")
478                                 writer.WriteStartElement (elementName);
479                         bool res = SerializeElement (writer, false);
480                         if (elementName != null && elementName != "")
481                                 writer.WriteEndElement ();
482                         return res;
483                 }
484
485                 protected internal virtual void Unmerge (
486                                 ConfigurationElement source, ConfigurationElement parent,
487                                 ConfigurationSaveMode updateMode)
488                 {
489                         if (parent != null && source.GetType() != parent.GetType())
490                                 throw new ConfigurationException ("Can't unmerge two elements of different type");
491                         
492                         foreach (PropertyInformation prop in source.ElementInformation.Properties)
493                         {
494                                 if (prop.ValueOrigin == PropertyValueOrigin.Default)
495                                         continue;
496                                 
497                                 PropertyInformation unmergedProp = ElementInformation.Properties [prop.Name];
498                                 
499                                 object sourceValue = prop.Value;
500                                 if      (parent == null || !parent.HasValue (prop.Name)) {
501                                         unmergedProp.Value = sourceValue;
502                                         continue;
503                                 }
504                                 else if (sourceValue != null) {
505                                         object parentValue = parent [prop.Name];
506                                         if (prop.IsElement) {
507                                                 if (parentValue != null) {
508                                                         ConfigurationElement copy = (ConfigurationElement) unmergedProp.Value;
509                                                         copy.Unmerge ((ConfigurationElement) sourceValue, (ConfigurationElement) parentValue, updateMode);
510                                                 }
511                                                 else
512                                                         unmergedProp.Value = sourceValue;
513                                         }
514                                         else {
515                                                 if (!object.Equals (sourceValue, parentValue) || 
516                                                         (updateMode == ConfigurationSaveMode.Full) ||
517                                                         (updateMode == ConfigurationSaveMode.Modified && prop.ValueOrigin == PropertyValueOrigin.SetHere))
518                                                         unmergedProp.Value = sourceValue;
519                                         }
520                                 }
521                         }
522                 }
523                 
524                 internal bool HasValue (string propName)
525                 {
526                         PropertyInformation info = ElementInformation.Properties [propName];
527                         return info != null && info.ValueOrigin != PropertyValueOrigin.Default;
528                 }
529                 
530                 internal bool IsReadFromConfig (string propName)
531                 {
532                         PropertyInformation info = ElementInformation.Properties [propName];
533                         return info != null && info.ValueOrigin == PropertyValueOrigin.SetHere;
534                 }
535         }
536         
537         internal class ElementMap
538         {
539                 static Hashtable elementMaps = new Hashtable ();
540                 
541                 ConfigurationPropertyCollection properties;
542                 ConfigurationPropertyCollection keyProperties;
543                 ConfigurationProperty defaultCollectionProperty;
544
545                 ConfigurationCollectionAttribute collectionAttribute;
546                 
547                 public static ElementMap GetMap (Type t)
548                 {
549                         lock (elementMaps) {
550                                 ElementMap map = elementMaps [t] as ElementMap;
551                                 if (map != null) return map;
552                                 map = new ElementMap (t);
553                                 elementMaps [t] = map;
554                                 return map;
555                         }
556                 }
557                 
558                 public ElementMap (Type t)
559                 {
560                         ReflectProperties (t);
561                 }
562                 
563                 protected void ReflectProperties (Type t)
564                 {
565                         collectionAttribute = Attribute.GetCustomAttribute (t, typeof(ConfigurationCollectionAttribute)) as ConfigurationCollectionAttribute;
566                         
567                         PropertyInfo[] props = t.GetProperties ();
568                         foreach (PropertyInfo prop in props)
569                         {
570                                 ConfigurationPropertyAttribute at = Attribute.GetCustomAttribute (prop, typeof(ConfigurationPropertyAttribute)) as ConfigurationPropertyAttribute;
571                                 if (at == null) continue;
572                                 string name = at.Name != null ? at.Name : prop.Name;
573
574                                 ConfigurationValidatorAttribute validatorAttr = Attribute.GetCustomAttribute (t, typeof(ConfigurationValidatorAttribute)) as ConfigurationValidatorAttribute;
575                                 ConfigurationValidatorBase validator = validatorAttr != null ? validatorAttr.ValidatorInstance : null;
576
577                                 TypeConverterAttribute convertAttr = (TypeConverterAttribute) Attribute.GetCustomAttribute (t, typeof (TypeConverterAttribute));
578                                 TypeConverter converter = convertAttr != null ? (TypeConverter) Activator.CreateInstance (Type.GetType (convertAttr.ConverterTypeName)) : null;
579                                 ConfigurationProperty cp = new ConfigurationProperty (name, prop.PropertyType, at.DefaultValue, converter, validator, at.Options);
580                                 
581                                 cp.CollectionAttribute = Attribute.GetCustomAttribute (prop, typeof(ConfigurationCollectionAttribute)) as ConfigurationCollectionAttribute;
582                                 
583                                 if (properties == null) properties = new ConfigurationPropertyCollection ();
584                                 properties.Add (cp);
585                         }
586                 }
587                 
588                 public bool HasProperties
589                 {
590                         get { return properties != null && properties.Count > 0; }
591                 }
592                 
593                 public ConfigurationPropertyCollection Properties
594                 {
595                         get {
596                                 if (properties == null) properties = new ConfigurationPropertyCollection ();
597                                 return properties;
598                         }
599                 }
600                 
601                 public ConfigurationPropertyCollection KeyProperties {
602                         get {
603                                 if (keyProperties == null) {
604                                         keyProperties = new ConfigurationPropertyCollection ();
605                                         
606                                         if (properties != null)
607                                                 foreach (ConfigurationProperty p in properties)
608                                                         if (p.IsKey) keyProperties.Add (p);
609                                 }
610                                 return keyProperties;
611                         }
612                 }
613                 
614                 public ConfigurationCollectionAttribute CollectionAttribute {
615                         get { return collectionAttribute; }
616                 }
617                 
618                 public ConfigurationProperty DefaultCollectionProperty {
619                         get {
620                                 if (defaultCollectionProperty == null) {
621                                         if (properties != null)
622                                                 foreach (ConfigurationProperty p in properties) {
623                                                         if (p.IsDefaultCollection) {
624                                                                 defaultCollectionProperty = p;
625                                                                 break;
626                                                         }
627                                                 }
628                                 }
629                                 return defaultCollectionProperty;
630                         }
631                 }
632         }
633 }
634
635 #endif