for TARGET_J2EE only:
[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                         ConfigurationPropertyCollection tmpkeyProps = new ConfigurationPropertyCollection ();
178                                 foreach (ConfigurationProperty prop in Properties) {
179                                         if (prop.IsKey)
180                                         tmpkeyProps.Add (prop);
181                                 }
182
183                         return keyProps = tmpkeyProps;
184                 }
185
186                 internal ConfigurationElementCollection GetDefaultCollection ()
187                 {
188                         if (defaultCollection != null) return defaultCollection;
189
190                         ConfigurationProperty defaultCollectionProp = null;
191
192                         foreach (ConfigurationProperty prop in Properties) {
193                                 if (prop.IsDefaultCollection) {
194                                         defaultCollectionProp = prop;
195                                         break;
196                                 }
197                         }
198
199                         if (defaultCollectionProp != null) {
200                                 defaultCollection = this [defaultCollectionProp] as ConfigurationElementCollection;
201                         }
202
203                         return defaultCollection;
204                 }
205
206                 protected internal object this [ConfigurationProperty property] {
207                         get { return this [property.Name]; }
208                         set { this [property.Name] = value; }
209                 }
210
211                 protected internal object this [string property_name] {
212                         get {
213                                 PropertyInformation pi = ElementInformation.Properties [property_name];
214                                 if (pi == null)
215                                         throw new InvalidOperationException ("Property '" + property_name + "' not found in configuration element");
216
217                                 return pi.Value;
218                         }
219
220                         set {
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                                 SetPropertyValue (pi.Property, value, false);
226
227                                 pi.Value = value;
228                                 modified = true;
229                         }
230                 }
231
232                 protected internal virtual ConfigurationPropertyCollection Properties {
233                         get {
234                                 if (map == null)
235                                         map = ElementMap.GetMap (GetType());
236                                 return map.Properties;
237                         }
238                 }
239
240                 public override bool Equals (object compareTo)
241                 {
242                         ConfigurationElement other = compareTo as ConfigurationElement;
243                         if (other == null) return false;
244                         if (GetType() != other.GetType()) return false;
245                         
246                         foreach (ConfigurationProperty prop in Properties) {
247                                 if (!object.Equals (this [prop], other [prop]))
248                                         return false;
249                         }
250                         return true;
251                 }
252
253                 public override int GetHashCode ()
254                 {
255                         int code = 0;
256                         object o;
257                         
258                         foreach (ConfigurationProperty prop in Properties) {
259                                 o = this [prop];
260                                 if (o == null)
261                                         continue;
262                                 
263                                 code += o.GetHashCode ();
264                         }
265                         
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.ToLowerInvariant () == "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                                 string value = null;
315                                 try {
316                                         value = reader.Value;
317                                         ValidateValue (prop.Property, value);
318                                         prop.SetStringValue (value);
319                                 } catch (ConfigurationErrorsException) {
320                                         throw;
321                                 } catch (ConfigurationException) {
322                                         throw;
323                                 } catch (Exception ex) {
324                                         string msg = String.Format ("The value of the property '{0}' cannot be parsed.", prop.Name);
325                                         throw new ConfigurationErrorsException (msg, reader);
326                                 }
327                                 readProps [prop] = prop.Name;
328                         }
329                         
330                         reader.MoveToElement ();
331                         if (reader.IsEmptyElement) {
332                                 reader.Skip ();
333                         } else {
334                                 int depth = reader.Depth;
335
336                                 reader.ReadStartElement ();
337                                 reader.MoveToContent ();
338
339                                 do {
340                                         if (reader.NodeType != XmlNodeType.Element) {
341                                                 reader.Skip ();
342                                                 continue;
343                                         }
344                                         
345                                         PropertyInformation prop = ElementInformation.Properties [reader.LocalName];
346                                         if (prop == null || (serializeCollectionKey && !prop.IsKey)) {
347                                                 if (!OnDeserializeUnrecognizedElement (reader.LocalName, reader)) {
348                                                         if (prop == null) {
349                                                                 ConfigurationElementCollection c = GetDefaultCollection ();
350                                                                 if (c != null && c.OnDeserializeUnrecognizedElement (reader.LocalName, reader))
351                                                                         continue;
352                                                         }
353                                                         throw new ConfigurationException ("Unrecognized element '" + reader.LocalName + "'.");
354                                                 }
355                                                 continue;
356                                         }
357                                         
358                                         if (!prop.IsElement)
359                                                 throw new ConfigurationException ("Property '" + prop.Name + "' is not a ConfigurationElement.");
360                                         
361                                         if (readProps.Contains (prop))
362                                                 throw new ConfigurationException ("The element <" + prop.Name + "> may only appear once in this section.");
363                                         
364                                         ConfigurationElement val = (ConfigurationElement) prop.Value;
365                                         val.DeserializeElement (reader, serializeCollectionKey);
366                                         readProps [prop] = prop.Name;
367
368                                         reader.Read();
369
370                                 } while (depth < reader.Depth);
371
372                                 if (reader.NodeType == XmlNodeType.EndElement)
373                                         reader.Read ();
374                         }
375                         
376                         modified = false;
377                                 
378                         foreach (PropertyInformation prop in ElementInformation.Properties)
379                                 if (!String.IsNullOrEmpty(prop.Name) && prop.IsRequired && !readProps.ContainsKey (prop)) {
380                                         object val = OnRequiredPropertyNotFound (prop.Name);
381                                         if (!object.Equals (val, prop.DefaultValue)) {
382                                                 prop.Value = val;
383                                                 prop.IsModified = false;
384                                         }
385                                 }
386
387                         PostDeserialize ();
388                 }
389
390                 protected virtual bool OnDeserializeUnrecognizedAttribute (string name, string value)
391                 {
392                         return false;
393                 }
394
395                 protected virtual bool OnDeserializeUnrecognizedElement (string element, XmlReader reader)
396                 {
397                         return false;
398                 }
399                 
400                 protected virtual object OnRequiredPropertyNotFound (string name)
401                 {
402                         throw new ConfigurationErrorsException ("Required attribute '" + name + "' not found.");
403                 }
404                 
405                 protected virtual void PreSerialize (XmlWriter writer)
406                 {
407                 }
408
409                 protected virtual void PostDeserialize ()
410                 {
411                 }
412
413                 protected internal virtual void InitializeDefault ()
414                 {
415                 }
416
417                 protected internal virtual bool IsModified ()
418                 {
419                         return modified;
420                 }
421                 
422                 protected internal virtual void SetReadOnly ()
423                 {
424                         readOnly = true;
425                 }
426                 
427                 public virtual bool IsReadOnly ()
428                 {
429                         return readOnly;
430                 }
431
432                 protected internal virtual void Reset (ConfigurationElement parentElement)
433                 {
434                         if (parentElement != null)
435                                 ElementInformation.Reset (parentElement.ElementInformation);
436                         else
437                                 InitializeDefault ();
438                 }
439
440                 protected internal virtual void ResetModified ()
441                 {
442                         modified = false;
443                         foreach (PropertyInformation p in ElementInformation.Properties)
444                                 p.IsModified = false;
445                 }
446
447                 protected internal virtual bool SerializeElement (XmlWriter writer, bool serializeCollectionKey)
448                 {
449                         PreSerialize (writer);
450                         
451                         if (serializeCollectionKey) {
452                                 ConfigurationPropertyCollection props = GetKeyProperties ();
453                                 foreach (ConfigurationProperty prop in props)
454                                         writer.WriteAttributeString (prop.Name, prop.ConvertToString (this[prop.Name]));
455                                 return props.Count > 0;
456                         }
457                         
458                         bool wroteData = false;
459                         
460                         foreach (PropertyInformation prop in ElementInformation.Properties)
461                         {
462                                 if (prop.IsElement || prop.ValueOrigin == PropertyValueOrigin.Default)
463                                         continue;
464                                 
465                                 if (!object.Equals (prop.Value, prop.DefaultValue)) {
466                                         writer.WriteAttributeString (prop.Name, prop.GetStringValue ());
467                                         wroteData = true;
468                                 }
469                         }
470                         
471                         foreach (PropertyInformation prop in ElementInformation.Properties)
472                         {
473                                 if (!prop.IsElement)
474                                         continue;
475                                 
476                                 ConfigurationElement val = (ConfigurationElement) prop.Value;
477                                 if (val != null)
478                                         wroteData = val.SerializeToXmlElement (writer, prop.Name) || wroteData;
479                         }
480                         return wroteData;
481                 }
482                                 
483                 protected internal virtual bool SerializeToXmlElement (
484                                 XmlWriter writer, string elementName)
485                 {
486                         if (!HasValues ())
487                                 return false;
488
489                         if (elementName != null && elementName != "")
490                                 writer.WriteStartElement (elementName);
491                         bool res = SerializeElement (writer, false);
492                         if (elementName != null && elementName != "")
493                                 writer.WriteEndElement ();
494                         return res;
495                 }
496
497                 protected internal virtual void Unmerge (
498                                 ConfigurationElement source, ConfigurationElement parent,
499                                 ConfigurationSaveMode updateMode)
500                 {
501                         if (parent != null && source.GetType() != parent.GetType())
502                                 throw new ConfigurationException ("Can't unmerge two elements of different type");
503                         
504                         foreach (PropertyInformation prop in source.ElementInformation.Properties)
505                         {
506                                 if (prop.ValueOrigin == PropertyValueOrigin.Default)
507                                         continue;
508                                 
509                                 PropertyInformation unmergedProp = ElementInformation.Properties [prop.Name];
510                                 
511                                 object sourceValue = prop.Value;
512                                 if      (parent == null || !parent.HasValue (prop.Name)) {
513                                         unmergedProp.Value = sourceValue;
514                                         continue;
515                                 }
516                                 else if (sourceValue != null) {
517                                         object parentValue = parent [prop.Name];
518                                         if (prop.IsElement) {
519                                                 if (parentValue != null) {
520                                                         ConfigurationElement copy = (ConfigurationElement) unmergedProp.Value;
521                                                         copy.Unmerge ((ConfigurationElement) sourceValue, (ConfigurationElement) parentValue, updateMode);
522                                                 }
523                                                 else
524                                                         unmergedProp.Value = sourceValue;
525                                         }
526                                         else {
527                                                 if (!object.Equals (sourceValue, parentValue) || 
528                                                         (updateMode == ConfigurationSaveMode.Full) ||
529                                                         (updateMode == ConfigurationSaveMode.Modified && prop.ValueOrigin == PropertyValueOrigin.SetHere))
530                                                         unmergedProp.Value = sourceValue;
531                                         }
532                                 }
533                         }
534                 }
535                 
536                 internal bool HasValue (string propName)
537                 {
538                         PropertyInformation info = ElementInformation.Properties [propName];
539                         return info != null && info.ValueOrigin != PropertyValueOrigin.Default;
540                 }
541                 
542                 internal bool IsReadFromConfig (string propName)
543                 {
544                         PropertyInformation info = ElementInformation.Properties [propName];
545                         return info != null && info.ValueOrigin == PropertyValueOrigin.SetHere;
546                 }
547
548                 void ValidateValue (ConfigurationProperty p, string value)
549                 {
550                         ConfigurationValidatorBase validator;
551                         if (p == null || (validator = p.Validator) == null)
552                                 return;
553                         
554                         if (!validator.CanValidate (p.Type))
555                                 throw new ConfigurationException (
556                                         String.Format ("Validator does not support type {0}", p.Type));
557                         validator.Validate (p.ConvertFromString (value));
558                 }
559         }
560         
561         internal class ElementMap
562         {
563 #if TARGET_JVM
564                 const string elementMapsKey = "ElementMap_elementMaps";
565                 static Hashtable elementMaps
566                 {
567                         get
568                         {
569                                 Hashtable tbl = (Hashtable) AppDomain.CurrentDomain.GetData (elementMapsKey);
570                                 if (tbl == null) {
571                                         lock (typeof (ElementMap)) {
572                                                 tbl = (Hashtable) AppDomain.CurrentDomain.GetData (elementMapsKey);
573                                                 if (tbl == null) {
574                                                         tbl = Hashtable.Synchronized (new Hashtable ());
575                                                         AppDomain.CurrentDomain.SetData (elementMapsKey, tbl);
576                                                 }
577                                         }
578                                 }
579                                 return tbl;
580                         }
581                 }
582 #else
583                 static readonly Hashtable elementMaps = Hashtable.Synchronized (new Hashtable ());
584 #endif
585
586                 readonly ConfigurationPropertyCollection properties;
587                 readonly ConfigurationCollectionAttribute collectionAttribute;
588
589                 public static ElementMap GetMap (Type t)
590                 {
591                         ElementMap map = elementMaps [t] as ElementMap;
592                         if (map != null) return map;
593                         map = new ElementMap (t);
594                         elementMaps [t] = map;
595                         return map;
596                 }
597                 
598                 public ElementMap (Type t)
599                 {
600                         properties = new ConfigurationPropertyCollection ();
601                 
602                         collectionAttribute = Attribute.GetCustomAttribute (t, typeof(ConfigurationCollectionAttribute)) as ConfigurationCollectionAttribute;
603                         
604                         PropertyInfo[] props = t.GetProperties ();
605                         foreach (PropertyInfo prop in props)
606                         {
607                                 ConfigurationPropertyAttribute at = Attribute.GetCustomAttribute (prop, typeof(ConfigurationPropertyAttribute)) as ConfigurationPropertyAttribute;
608                                 if (at == null) continue;
609                                 string name = at.Name != null ? at.Name : prop.Name;
610
611                                 ConfigurationValidatorAttribute validatorAttr = Attribute.GetCustomAttribute (t, typeof(ConfigurationValidatorAttribute)) as ConfigurationValidatorAttribute;
612                                 ConfigurationValidatorBase validator = validatorAttr != null ? validatorAttr.ValidatorInstance : null;
613
614                                 TypeConverterAttribute convertAttr = (TypeConverterAttribute) Attribute.GetCustomAttribute (t, typeof (TypeConverterAttribute));
615                                 TypeConverter converter = convertAttr != null ? (TypeConverter) Activator.CreateInstance (Type.GetType (convertAttr.ConverterTypeName)) : null;
616                                 ConfigurationProperty cp = new ConfigurationProperty (name, prop.PropertyType, at.DefaultValue, converter, validator, at.Options);
617
618                                 cp.CollectionAttribute = Attribute.GetCustomAttribute (prop, typeof(ConfigurationCollectionAttribute)) as ConfigurationCollectionAttribute;                             
619                                 properties.Add (cp);
620                         }
621                 }
622
623                 public ConfigurationCollectionAttribute CollectionAttribute
624                 {
625                         get { return collectionAttribute; }
626                 }
627                 
628                 public bool HasProperties
629                 {
630                         get { return properties.Count > 0; }
631                 }
632                 
633                 public ConfigurationPropertyCollection Properties
634                 {
635                         get {
636                                 return properties;
637                         }
638                 }
639         }
640 }
641
642 #endif