Fix null sessions in HttpContextWrapper.Session
[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                 Configuration _configuration;
50                 bool elementPresent;
51
52                 internal Configuration Configuration {
53                         get { return _configuration; }
54                         set { _configuration = value; }
55                 }
56
57                 protected ConfigurationElement ()
58                 {
59                 }
60                 
61                 internal virtual void InitFromProperty (PropertyInformation propertyInfo)
62                 {
63                         elementInfo = new ElementInformation (this, propertyInfo);
64                         Init ();
65                 }
66                 
67                 public ElementInformation ElementInformation {
68                         get {
69                                 if (elementInfo == null)
70                                         elementInfo = new ElementInformation (this, null);
71                                 return elementInfo;
72                         }
73                 }
74
75                 internal string RawXml {
76                         get { return rawXml; }
77                         set {
78                                 // FIXME: this hack is nasty. We should make
79                                 // some refactory on the entire assembly.
80                                 if (rawXml == null || value != null)
81                                         rawXml = value;
82                         }
83                 }
84
85                 protected internal virtual void Init ()
86                 {
87                 }
88
89                 protected internal virtual ConfigurationElementProperty ElementProperty {
90                         get {
91                                 if (elementProperty == null)
92                                         elementProperty = new ConfigurationElementProperty (ElementInformation.Validator);
93                                 return elementProperty;
94                         }
95                 }
96
97                 [MonoTODO]
98                 protected ContextInformation EvaluationContext {
99                         get {
100                                 if (Configuration != null)
101                                         return Configuration.EvaluationContext;
102                                 throw new NotImplementedException ();
103                         }
104                 }
105
106                 ConfigurationLockCollection lockAllAttributesExcept;
107                 public ConfigurationLockCollection LockAllAttributesExcept {
108                         get {
109                                 if (lockAllAttributesExcept == null) {
110                                         lockAllAttributesExcept = new ConfigurationLockCollection (this, ConfigurationLockType.Attribute | ConfigurationLockType.Exclude);
111                                 }
112
113                                 return lockAllAttributesExcept;
114                         }
115                 }
116
117                 ConfigurationLockCollection lockAllElementsExcept;
118                 public ConfigurationLockCollection LockAllElementsExcept {
119                         get {
120                                 if (lockAllElementsExcept == null) {
121                                         lockAllElementsExcept = new ConfigurationLockCollection (this, ConfigurationLockType.Element | ConfigurationLockType.Exclude);
122                                 }
123
124                                 return lockAllElementsExcept;
125                         }
126                 }
127
128                 ConfigurationLockCollection lockAttributes;
129                 public ConfigurationLockCollection LockAttributes {
130                         get {
131                                 if (lockAttributes == null) {
132                                         lockAttributes = new ConfigurationLockCollection (this, ConfigurationLockType.Attribute);
133                                 }
134
135                                 return lockAttributes;
136                         }
137                 }
138
139                 ConfigurationLockCollection lockElements;
140                 public ConfigurationLockCollection LockElements {
141                         get {
142                                 if (lockElements == null) {
143                                         lockElements = new ConfigurationLockCollection (this, ConfigurationLockType.Element);
144                                 }
145
146                                 return lockElements;
147                         }
148                 }
149
150                 bool lockItem;
151                 public bool LockItem {
152                         get { return lockItem; }
153                         set { lockItem = value; }
154                 }
155
156                 [MonoTODO]
157                 protected virtual void ListErrors (IList list)
158                 {
159                         throw new NotImplementedException ();
160                 }
161
162                 [MonoTODO]
163                 protected void SetPropertyValue (ConfigurationProperty prop, object value, bool ignoreLocks)
164                 {
165                         try {
166                                 if (value != null) {
167                                         /* XXX all i know for certain is that Validation happens here */
168                                         prop.Validate (value);
169
170                                         /* XXX presumably the actual setting of the
171                                          * property happens here instead of in the
172                                          * set_Item code below, but that would mean
173                                          * the Value needs to be stuffed in the
174                                          * property, not the propertyinfo (or else the
175                                          * property needs a ref to the property info
176                                          * to correctly set the value). */
177                                 }
178                         }
179                         catch (Exception e) {
180                                 throw new ConfigurationErrorsException (String.Format ("The value for the property '{0}' on type {1} is not valid.", prop.Name, this.ElementInformation.Type), e);
181                         }
182                 }
183
184                 internal ConfigurationPropertyCollection GetKeyProperties ()
185                 {
186                         if (keyProps != null) return keyProps;
187                         
188                         ConfigurationPropertyCollection tmpkeyProps = new ConfigurationPropertyCollection ();
189                                 foreach (ConfigurationProperty prop in Properties) {
190                                         if (prop.IsKey)
191                                         tmpkeyProps.Add (prop);
192                                 }
193
194                         return keyProps = tmpkeyProps;
195                 }
196
197                 internal ConfigurationElementCollection GetDefaultCollection ()
198                 {
199                         if (defaultCollection != null) return defaultCollection;
200
201                         ConfigurationProperty defaultCollectionProp = null;
202
203                         foreach (ConfigurationProperty prop in Properties) {
204                                 if (prop.IsDefaultCollection) {
205                                         defaultCollectionProp = prop;
206                                         break;
207                                 }
208                         }
209
210                         if (defaultCollectionProp != null) {
211                                 defaultCollection = this [defaultCollectionProp] as ConfigurationElementCollection;
212                         }
213
214                         return defaultCollection;
215                 }
216
217                 protected internal object this [ConfigurationProperty property] {
218                         get { return this [property.Name]; }
219                         set { this [property.Name] = value; }
220                 }
221
222                 protected internal object this [string property_name] {
223                         get {
224                                 PropertyInformation pi = ElementInformation.Properties [property_name];
225                                 if (pi == null)
226                                         throw new InvalidOperationException ("Property '" + property_name + "' not found in configuration element");
227
228                                 return pi.Value;
229                         }
230
231                         set {
232                                 PropertyInformation pi = ElementInformation.Properties [property_name];
233                                 if (pi == null)
234                                         throw new InvalidOperationException ("Property '" + property_name + "' not found in configuration element");
235
236                                 SetPropertyValue (pi.Property, value, false);
237
238                                 pi.Value = value;
239                                 modified = true;
240                         }
241                 }
242
243                 protected internal virtual ConfigurationPropertyCollection Properties {
244                         get {
245                                 if (map == null)
246                                         map = ElementMap.GetMap (GetType());
247                                 return map.Properties;
248                         }
249                 }
250
251                 public override bool Equals (object compareTo)
252                 {
253                         ConfigurationElement other = compareTo as ConfigurationElement;
254                         if (other == null) return false;
255                         if (GetType() != other.GetType()) return false;
256                         
257                         foreach (ConfigurationProperty prop in Properties) {
258                                 if (!object.Equals (this [prop], other [prop]))
259                                         return false;
260                         }
261                         return true;
262                 }
263
264                 public override int GetHashCode ()
265                 {
266                         int code = 0;
267                         object o;
268                         
269                         foreach (ConfigurationProperty prop in Properties) {
270                                 o = this [prop];
271                                 if (o == null)
272                                         continue;
273                                 
274                                 code += o.GetHashCode ();
275                         }
276                         
277                         return code;
278                 }
279
280                 internal virtual bool HasValues ()
281                 {
282                         foreach (PropertyInformation pi in ElementInformation.Properties)
283                                 if (pi.ValueOrigin != PropertyValueOrigin.Default)
284                                         return true;
285                         
286                         return false;
287                 }
288
289                 internal virtual bool HasLocalModifications ()
290                 {
291                         foreach (PropertyInformation pi in ElementInformation.Properties)
292                                 if (pi.ValueOrigin == PropertyValueOrigin.SetHere && pi.IsModified)
293                                         return true;
294                         
295                         return false;
296                 }
297                 
298                 protected internal virtual void DeserializeElement (XmlReader reader, bool serializeCollectionKey)
299                 {
300                         Hashtable readProps = new Hashtable ();
301                         
302                         reader.MoveToContent ();
303                         elementPresent = true;
304                         
305                         while (reader.MoveToNextAttribute ())
306                         {
307                                 PropertyInformation prop = ElementInformation.Properties [reader.LocalName];
308                                 if (prop == null || (serializeCollectionKey && !prop.IsKey)) {
309                                         /* handle the built in ConfigurationElement attributes here */
310                                         if (reader.LocalName == "lockAllAttributesExcept") {
311                                                 LockAllAttributesExcept.SetFromList (reader.Value);
312                                         }
313                                         else if (reader.LocalName == "lockAllElementsExcept") {
314                                                 LockAllElementsExcept.SetFromList (reader.Value);
315                                         }
316                                         else if (reader.LocalName == "lockAttributes") {
317                                                 LockAttributes.SetFromList (reader.Value);
318                                         }
319                                         else if (reader.LocalName == "lockElements") {
320                                                 LockElements.SetFromList (reader.Value);
321                                         }
322                                         else if (reader.LocalName == "lockItem") {
323                                                 LockItem = (reader.Value.ToLowerInvariant () == "true");
324                                         }
325                                         else if (reader.LocalName == "xmlns") {
326                                                 /* ignore */
327                                         } else if (this is ConfigurationSection && reader.LocalName == "configSource") {
328                                                 /* ignore */
329                                         } else if (!OnDeserializeUnrecognizedAttribute (reader.LocalName, reader.Value))
330                                                 throw new ConfigurationErrorsException ("Unrecognized attribute '" + reader.LocalName + "'.", reader);
331
332                                         continue;
333                                 }
334                                 
335                                 if (readProps.ContainsKey (prop))
336                                         throw new ConfigurationErrorsException ("The attribute '" + prop.Name + "' may only appear once in this element.", reader);
337
338                                 string value = null;
339                                 try {
340                                         value = reader.Value;
341                                         ValidateValue (prop.Property, value);
342                                         prop.SetStringValue (value);
343                                 } catch (ConfigurationErrorsException) {
344                                         throw;
345                                 } catch (ConfigurationException) {
346                                         throw;
347                                 } catch (Exception ex) {
348                                         string msg = String.Format ("The value for the property '{0}' is not valid. The error is: {1}", prop.Name, ex.Message);
349                                         throw new ConfigurationErrorsException (msg, reader);
350                                 }
351                                 readProps [prop] = prop.Name;
352                         
353                                 ConfigXmlTextReader _reader = reader as ConfigXmlTextReader;
354                                 if (_reader != null){
355                                         prop.Source = _reader.Filename;
356                                         prop.LineNumber = _reader.LineNumber;
357                                 }
358                         }
359                         
360                         reader.MoveToElement ();
361                         if (reader.IsEmptyElement) {
362                                 reader.Skip ();
363                         } else {
364                                 int depth = reader.Depth;
365
366                                 reader.ReadStartElement ();
367                                 reader.MoveToContent ();
368
369                                 do {
370                                         if (reader.NodeType != XmlNodeType.Element) {
371                                                 reader.Skip ();
372                                                 continue;
373                                         }
374                                         
375                                         PropertyInformation prop = ElementInformation.Properties [reader.LocalName];
376                                         if (prop == null || (serializeCollectionKey && !prop.IsKey)) {
377                                                 if (!OnDeserializeUnrecognizedElement (reader.LocalName, reader)) {
378                                                         if (prop == null) {
379                                                                 ConfigurationElementCollection c = GetDefaultCollection ();
380                                                                 if (c != null && c.OnDeserializeUnrecognizedElement (reader.LocalName, reader))
381                                                                         continue;
382                                                         }
383                                                         throw new ConfigurationErrorsException ("Unrecognized element '" + reader.LocalName + "'.", reader);
384                                                 }
385                                                 continue;
386                                         }
387                                         
388                                         if (!prop.IsElement)
389                                                 throw new ConfigurationErrorsException ("Property '" + prop.Name + "' is not a ConfigurationElement.");
390                                         
391                                         if (readProps.Contains (prop))
392                                                 throw new ConfigurationErrorsException ("The element <" + prop.Name + "> may only appear once in this section.", reader);
393                                         
394                                         ConfigurationElement val = (ConfigurationElement) prop.Value;
395                                         val.DeserializeElement (reader, serializeCollectionKey);
396                                         readProps [prop] = prop.Name;
397
398                                         if(depth == reader.Depth)
399                                                 reader.Read();
400
401                                 } while (depth < reader.Depth);                         
402                         }
403                         
404                         modified = false;
405                                 
406                         foreach (PropertyInformation prop in ElementInformation.Properties)
407                                 if (!String.IsNullOrEmpty(prop.Name) && prop.IsRequired && !readProps.ContainsKey (prop)) {
408                                         PropertyInformation p = ElementInformation.Properties [prop.Name];
409                                         if (p == null) {
410                                                 object val = OnRequiredPropertyNotFound (prop.Name);
411                                                 if (!object.Equals (val, prop.DefaultValue)) {
412                                                         prop.Value = val;
413                                                         prop.IsModified = false;
414                                                 }
415                                         }
416                                 }
417
418                         PostDeserialize ();
419                 }
420
421                 protected virtual bool OnDeserializeUnrecognizedAttribute (string name, string value)
422                 {
423                         return false;
424                 }
425
426                 protected virtual bool OnDeserializeUnrecognizedElement (string element, XmlReader reader)
427                 {
428                         return false;
429                 }
430                 
431                 protected virtual object OnRequiredPropertyNotFound (string name)
432                 {
433                         throw new ConfigurationErrorsException ("Required attribute '" + name + "' not found.");
434                 }
435                 
436                 protected virtual void PreSerialize (XmlWriter writer)
437                 {
438                 }
439
440                 protected virtual void PostDeserialize ()
441                 {
442                 }
443
444                 protected internal virtual void InitializeDefault ()
445                 {
446                 }
447
448                 protected internal virtual bool IsModified ()
449                 {
450                         return modified;
451                 }
452                 
453                 protected internal virtual void SetReadOnly ()
454                 {
455                         readOnly = true;
456                 }
457                 
458                 public virtual bool IsReadOnly ()
459                 {
460                         return readOnly;
461                 }
462
463                 protected internal virtual void Reset (ConfigurationElement parentElement)
464                 {
465                         elementPresent = false;
466                         
467                         if (parentElement != null)
468                                 ElementInformation.Reset (parentElement.ElementInformation);
469                         else
470                                 InitializeDefault ();
471                 }
472
473                 protected internal virtual void ResetModified ()
474                 {
475                         modified = false;
476                         foreach (PropertyInformation p in ElementInformation.Properties)
477                                 p.IsModified = false;
478                 }
479
480                 protected internal virtual bool SerializeElement (XmlWriter writer, bool serializeCollectionKey)
481                 {
482                         PreSerialize (writer);
483                         
484                         if (serializeCollectionKey) {
485                                 ConfigurationPropertyCollection props = GetKeyProperties ();
486                                 foreach (ConfigurationProperty prop in props)
487                                         writer.WriteAttributeString (prop.Name, prop.ConvertToString (this[prop.Name]));
488                                 return props.Count > 0;
489                         }
490                         
491                         bool wroteData = false;
492                         
493                         foreach (PropertyInformation prop in ElementInformation.Properties)
494                         {
495                                 if (prop.IsElement || prop.ValueOrigin == PropertyValueOrigin.Default)
496                                         continue;
497                                 
498                                 if (!object.Equals (prop.Value, prop.DefaultValue)) {
499                                         writer.WriteAttributeString (prop.Name, prop.GetStringValue ());
500                                         wroteData = true;
501                                 }
502                         }
503                         
504                         foreach (PropertyInformation prop in ElementInformation.Properties)
505                         {
506                                 if (!prop.IsElement)
507                                         continue;
508                                 
509                                 ConfigurationElement val = (ConfigurationElement) prop.Value;
510                                 if (val != null)
511                                         wroteData = val.SerializeToXmlElement (writer, prop.Name) || wroteData;
512                         }
513                         return wroteData;
514                 }
515                                 
516                 protected internal virtual bool SerializeToXmlElement (
517                                 XmlWriter writer, string elementName)
518                 {
519                         if (!HasValues ())
520                                 return false;
521
522                         if (elementName != null && elementName != "")
523                                 writer.WriteStartElement (elementName);
524                         bool res = SerializeElement (writer, false);
525                         if (elementName != null && elementName != "")
526                                 writer.WriteEndElement ();
527                         return res;
528                 }
529
530                 protected internal virtual void Unmerge (
531                                 ConfigurationElement source, ConfigurationElement parent,
532                                 ConfigurationSaveMode updateMode)
533                 {
534                         if (parent != null && source.GetType() != parent.GetType())
535                                 throw new ConfigurationErrorsException ("Can't unmerge two elements of different type");
536                         
537                         foreach (PropertyInformation prop in source.ElementInformation.Properties)
538                         {
539                                 if (prop.ValueOrigin == PropertyValueOrigin.Default)
540                                         continue;
541                                 
542                                 PropertyInformation unmergedProp = ElementInformation.Properties [prop.Name];
543                                 
544                                 object sourceValue = prop.Value;
545                                 if      (parent == null || !parent.HasValue (prop.Name)) {
546                                         unmergedProp.Value = sourceValue;
547                                         continue;
548                                 }
549                                 else if (sourceValue != null) {
550                                         object parentValue = parent [prop.Name];
551                                         if (prop.IsElement) {
552                                                 if (parentValue != null) {
553                                                         ConfigurationElement copy = (ConfigurationElement) unmergedProp.Value;
554                                                         copy.Unmerge ((ConfigurationElement) sourceValue, (ConfigurationElement) parentValue, updateMode);
555                                                 }
556                                                 else
557                                                         unmergedProp.Value = sourceValue;
558                                         }
559                                         else {
560                                                 if (!object.Equals (sourceValue, parentValue) || 
561                                                         (updateMode == ConfigurationSaveMode.Full) ||
562                                                         (updateMode == ConfigurationSaveMode.Modified && prop.ValueOrigin == PropertyValueOrigin.SetHere))
563                                                         unmergedProp.Value = sourceValue;
564                                         }
565                                 }
566                         }
567                 }
568                 
569                 internal bool HasValue (string propName)
570                 {
571                         PropertyInformation info = ElementInformation.Properties [propName];
572                         return info != null && info.ValueOrigin != PropertyValueOrigin.Default;
573                 }
574                 
575                 internal bool IsReadFromConfig (string propName)
576                 {
577                         PropertyInformation info = ElementInformation.Properties [propName];
578                         return info != null && info.ValueOrigin == PropertyValueOrigin.SetHere;
579                 }
580
581                 internal bool IsElementPresent
582                 {
583                         get {   return elementPresent;  }
584                 }
585
586                 void ValidateValue (ConfigurationProperty p, string value)
587                 {
588                         ConfigurationValidatorBase validator;
589                         if (p == null || (validator = p.Validator) == null)
590                                 return;
591                         
592                         if (!validator.CanValidate (p.Type))
593                                 throw new ConfigurationErrorsException (
594                                         String.Format ("Validator does not support type {0}", p.Type));
595                         validator.Validate (p.ConvertFromString (value));
596                 }
597         }
598         
599         internal class ElementMap
600         {
601 #if TARGET_JVM
602                 const string elementMapsKey = "ElementMap_elementMaps";
603                 static Hashtable elementMaps
604                 {
605                         get
606                         {
607                                 Hashtable tbl = (Hashtable) AppDomain.CurrentDomain.GetData (elementMapsKey);
608                                 if (tbl == null) {
609                                         lock (typeof (ElementMap)) {
610                                                 tbl = (Hashtable) AppDomain.CurrentDomain.GetData (elementMapsKey);
611                                                 if (tbl == null) {
612                                                         tbl = Hashtable.Synchronized (new Hashtable ());
613                                                         AppDomain.CurrentDomain.SetData (elementMapsKey, tbl);
614                                                 }
615                                         }
616                                 }
617                                 return tbl;
618                         }
619                 }
620 #else
621                 static readonly Hashtable elementMaps = Hashtable.Synchronized (new Hashtable ());
622 #endif
623
624                 readonly ConfigurationPropertyCollection properties;
625                 readonly ConfigurationCollectionAttribute collectionAttribute;
626
627                 public static ElementMap GetMap (Type t)
628                 {
629                         ElementMap map = elementMaps [t] as ElementMap;
630                         if (map != null) return map;
631                         map = new ElementMap (t);
632                         elementMaps [t] = map;
633                         return map;
634                 }
635                 
636                 public ElementMap (Type t)
637                 {
638                         properties = new ConfigurationPropertyCollection ();
639                 
640                         collectionAttribute = Attribute.GetCustomAttribute (t, typeof(ConfigurationCollectionAttribute)) as ConfigurationCollectionAttribute;
641                         
642                         PropertyInfo[] props = t.GetProperties (BindingFlags.Public|BindingFlags.NonPublic|BindingFlags.Static|BindingFlags.Instance);
643                         foreach (PropertyInfo prop in props)
644                         {
645                                 ConfigurationPropertyAttribute at = Attribute.GetCustomAttribute (prop, typeof(ConfigurationPropertyAttribute)) as ConfigurationPropertyAttribute;
646                                 if (at == null) continue;
647                                 string name = at.Name != null ? at.Name : prop.Name;
648
649                                 ConfigurationValidatorAttribute validatorAttr = Attribute.GetCustomAttribute (prop, typeof (ConfigurationValidatorAttribute)) as ConfigurationValidatorAttribute;
650                                 ConfigurationValidatorBase validator = validatorAttr != null ? validatorAttr.ValidatorInstance : null;
651
652                                 TypeConverterAttribute convertAttr = (TypeConverterAttribute) Attribute.GetCustomAttribute (prop, typeof (TypeConverterAttribute));
653                                 TypeConverter converter = convertAttr != null ? (TypeConverter) Activator.CreateInstance (Type.GetType (convertAttr.ConverterTypeName), true) : null;
654                                 ConfigurationProperty cp = new ConfigurationProperty (name, prop.PropertyType, at.DefaultValue, converter, validator, at.Options);
655
656                                 cp.CollectionAttribute = Attribute.GetCustomAttribute (prop, typeof(ConfigurationCollectionAttribute)) as ConfigurationCollectionAttribute;                             
657                                 properties.Add (cp);
658                         }
659                 }
660
661                 public ConfigurationCollectionAttribute CollectionAttribute
662                 {
663                         get { return collectionAttribute; }
664                 }
665                 
666                 public bool HasProperties
667                 {
668                         get { return properties.Count > 0; }
669                 }
670                 
671                 public ConfigurationPropertyCollection Properties
672                 {
673                         get {
674                                 return properties;
675                         }
676                 }
677         }
678 }
679
680 #endif