2005-10-07 Chris Toshok <toshok@ximian.com>
[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                 bool readOnly;
46                 ElementInformation elementInfo;
47                 ConfigurationElementProperty elementProperty;
48
49                 protected ConfigurationElement ()
50                 {
51                 }
52                 
53                 internal virtual void InitFromProperty (PropertyInformation propertyInfo)
54                 {
55                         elementInfo = new ElementInformation (this, propertyInfo);
56                         Init ();
57                 }
58                 
59                 public ElementInformation ElementInformation {
60                         get {
61                                 if (elementInfo == null)
62                                         elementInfo = new ElementInformation (this, null);
63                                 return elementInfo;
64                         }
65                 }
66
67                 internal string RawXml {
68                         get { return rawXml; }
69                         set { rawXml = value; }
70                 }
71
72                 protected internal virtual void Init ()
73                 {
74                 }
75
76                 public ConfigurationElementProperty ElementProperty {
77                         get {
78                                 if (elementProperty == null)
79                                         elementProperty = new ConfigurationElementProperty (ElementInformation.Validator);
80                                 return elementProperty;
81                         }
82                 }
83
84                 [MonoTODO]
85                 protected ContextInformation EvaluationContext {
86                         get {
87                                 throw new NotImplementedException ();
88                         }
89                 }
90
91                 [MonoTODO]
92                 public ConfigurationLockCollection LockAllAttributesExcept {
93                         get {
94                                 throw new NotImplementedException ();
95                         }
96                 }
97
98                 [MonoTODO]
99                 public ConfigurationLockCollection LockAllElementsExcept {
100                         get {
101                                 throw new NotImplementedException ();
102                         }
103                 }
104
105                 [MonoTODO]
106                 ConfigurationLockCollection lockAttributes;
107                 public ConfigurationLockCollection LockAttributes {
108                         get {
109                                 if (lockAttributes == null) {
110                                         lockAttributes = new ConfigurationLockCollection (this, ConfigurationLockType.Attribute);
111                                 }
112
113                                 return lockAttributes;
114                         }
115                 }
116
117                 [MonoTODO]
118                 public ConfigurationLockCollection LockElements {
119                         get {
120                                 throw new NotImplementedException ();
121                         }
122                 }
123
124                 [MonoTODO]
125                 public bool LockItem {
126                         get {
127                                 throw new NotImplementedException ();
128                         }
129                         set {
130                                 throw new NotImplementedException ();
131                         }
132                 }
133
134                 [MonoTODO]
135                 public void ListErrors (IList list)
136                 {
137                         throw new NotImplementedException ();
138                 }
139
140                 [MonoTODO]
141                 public void SetPropertyValue (ConfigurationProperty prop, object value, bool ignoreLocks)
142                 {
143                         throw new NotImplementedException ();
144                 }
145
146                 internal ConfigurationPropertyCollection GetKeyProperties ()
147                 {
148                         if (keyProps != null) return keyProps;
149                         
150                         if (map.Properties == Properties)
151                                 keyProps = map.KeyProperties;
152                         else {
153                                 keyProps = new ConfigurationPropertyCollection ();
154                                 foreach (ConfigurationProperty prop in Properties) {
155                                         if (prop.IsKey)
156                                                 keyProps.Add (prop);
157                                 }
158                         }
159                         return keyProps;
160                 }
161
162                 protected internal object this [ConfigurationProperty property] {
163                         get { return this [property.Name]; }
164                         set { this [property.Name] = value; }
165                 }
166
167                 protected internal object this [string property_name] {
168                         get {
169                                 PropertyInformation pi = ElementInformation.Properties [property_name];
170                                 if (pi == null)
171                                         throw new InvalidOperationException ("Property '" + property_name + "' not found in configuration element");
172                                 
173                                 return pi.Value;
174                         }
175
176                         set {
177                                 PropertyInformation pi = ElementInformation.Properties [property_name];
178                                 if (pi == null)
179                                         throw new InvalidOperationException ("Property '" + property_name + "' not found in configuration element");
180                                 
181                                 pi.Value = value;
182                                 modified = true;
183                         }
184                 }
185
186                 protected internal virtual ConfigurationPropertyCollection Properties {
187                         get {
188                                 if (map == null)
189                                         map = ElementMap.GetMap (GetType());
190                                 return map.Properties;
191                         }
192                 }
193
194                 public override bool Equals (object compareTo)
195                 {
196                         ConfigurationElement other = compareTo as ConfigurationElement;
197                         if (other == null) return false;
198                         if (GetType() != other.GetType()) return false;
199                         
200                         foreach (ConfigurationProperty prop in Properties) {
201                                 if (!object.Equals (this [prop], other [prop]))
202                                         return false;
203                         }
204                         return true;
205                 }
206
207                 public override int GetHashCode ()
208                 {
209                         int code = 0;
210                         foreach (ConfigurationProperty prop in Properties)
211                                 code += this [prop].GetHashCode ();
212                         return code;
213                 }
214
215                 internal virtual bool HasValues ()
216                 {
217                         foreach (PropertyInformation pi in ElementInformation.Properties)
218                                 if (pi.ValueOrigin != PropertyValueOrigin.Default)
219                                         return true;
220                         return false;
221                 }
222                 
223                 protected internal virtual void DeserializeElement (XmlReader reader, bool serializeCollectionKey)
224                 {
225                         Hashtable readProps = new Hashtable ();
226                         
227                         reader.MoveToContent ();
228                         while (reader.MoveToNextAttribute ())
229                         {
230                                 PropertyInformation prop = ElementInformation.Properties [reader.LocalName];
231                                 if (prop == null || (serializeCollectionKey && !prop.IsKey)) {
232                                         if (!OnDeserializeUnrecognizedAttribute (reader.LocalName, reader.Value))
233                                                 throw new ConfigurationException ("Unrecognized attribute '" + reader.LocalName + "'.");
234                                         continue;
235                                 }
236                                 
237                                 if (readProps.ContainsKey (prop))
238                                         throw new ConfigurationException ("The attribute '" + prop.Name + "' may only appear once in this element.");
239                                 
240                                 prop.SetStringValue (reader.Value);
241                                 readProps [prop] = prop.Name;
242                         }
243                         
244                         reader.MoveToElement ();
245                         if (reader.IsEmptyElement) {
246                                 reader.Skip ();
247                         }
248                         else {
249                                 reader.ReadStartElement ();
250                                 reader.MoveToContent ();
251                                 
252                                 while (reader.NodeType != XmlNodeType.EndElement)
253                                 {
254                                         if (reader.NodeType != XmlNodeType.Element) {
255                                                 reader.Skip ();
256                                                 continue;
257                                         }
258                                         
259                                         PropertyInformation prop = ElementInformation.Properties [reader.LocalName];
260                                         if (prop == null || (serializeCollectionKey && !prop.IsKey)) {
261                                                 if (!OnDeserializeUnrecognizedElement (reader.LocalName, reader))
262                                                         throw new ConfigurationException ("Unrecognized element '" + reader.LocalName + "'.");
263                                                 continue;
264                                         }
265                                         
266                                         if (!prop.IsElement)
267                                                 throw new ConfigurationException ("Property '" + prop.Name + "' is not a ConfigurationElement.");
268                                         
269                                         if (readProps.Contains (prop))
270                                                 throw new ConfigurationException ("The element <" + prop.Name + "> may only appear once in this section.");
271                                         
272                                         ConfigurationElement val = (ConfigurationElement) prop.Value;
273                                         val.DeserializeElement (reader, serializeCollectionKey);
274                                         readProps [prop] = prop.Name;
275                                 }
276                         }
277                         
278                         modified = false;
279                                 
280                         foreach (PropertyInformation prop in ElementInformation.Properties)
281                                 if (prop.IsRequired && !readProps.ContainsKey (prop)) {
282                                         object val = OnRequiredPropertyNotFound (prop.Name);
283                                         if (!object.Equals (val, prop.DefaultValue)) {
284                                                 prop.Value = val;
285                                                 prop.IsModified = false;
286                                         }
287                                 }
288
289                         PostDeserialize ();
290                 }
291
292                 protected virtual bool OnDeserializeUnrecognizedAttribute (string name, string value)
293                 {
294                         return false;
295                 }
296
297                 protected virtual bool OnDeserializeUnrecognizedElement (string element, XmlReader reader)
298                 {
299                         return false;
300                 }
301                 
302                 protected virtual object OnRequiredPropertyNotFound (string name)
303                 {
304                         throw new ConfigurationErrorsException ("Required attribute '" + name + "' not found.");
305                 }
306                 
307                 protected virtual void PreSerialize (XmlWriter writer)
308                 {
309                 }
310
311                 protected virtual void PostDeserialize ()
312                 {
313                 }
314
315                 protected internal virtual void InitializeDefault ()
316                 {
317                 }
318
319                 protected internal virtual bool IsModified ()
320                 {
321                         return modified;
322                 }
323                 
324                 protected internal virtual void SetReadOnly ()
325                 {
326                         readOnly = true;
327                 }
328                 
329                 public virtual bool IsReadOnly ()
330                 {
331                         return readOnly;
332                 }
333
334                 protected internal virtual void Reset (ConfigurationElement parentElement)
335                 {
336                         if (parentElement != null)
337                                 ElementInformation.Reset (parentElement.ElementInformation);
338                         else
339                                 InitializeDefault ();
340                 }
341
342                 protected internal virtual void ResetModified ()
343                 {
344                         modified = false;
345                         foreach (PropertyInformation p in ElementInformation.Properties)
346                                 p.IsModified = false;
347                 }
348
349                 protected internal virtual bool SerializeElement (XmlWriter writer, bool serializeCollectionKey)
350                 {
351                         PreSerialize (writer);
352                         
353                         if (serializeCollectionKey) {
354                                 ConfigurationPropertyCollection props = GetKeyProperties ();
355                                 foreach (ConfigurationProperty prop in props)
356                                         writer.WriteAttributeString (prop.Name, prop.ConvertToString (this[prop.Name]));
357                                 return props.Count > 0;
358                         }
359                         
360                         bool wroteData = false;
361                         
362                         foreach (PropertyInformation prop in ElementInformation.Properties)
363                         {
364                                 if (prop.IsElement || prop.ValueOrigin == PropertyValueOrigin.Default)
365                                         continue;
366                                 
367                                 if (!object.Equals (prop.Value, prop.DefaultValue)) {
368                                         writer.WriteAttributeString (prop.Name, prop.GetStringValue ());
369                                         wroteData = true;
370                                 }
371                         }
372                         
373                         foreach (PropertyInformation prop in ElementInformation.Properties)
374                         {
375                                 if (!prop.IsElement)
376                                         continue;
377                                 
378                                 ConfigurationElement val = (ConfigurationElement) prop.Value;
379                                 if (val != null && val.HasValues ()) {
380                                         wroteData = val.SerializeToXmlElement (writer, prop.Name) || wroteData;
381                                 }
382                         }
383                         return wroteData;
384                 }
385                                 
386                 protected internal virtual bool SerializeToXmlElement (
387                                 XmlWriter writer, string elementName)
388                 {
389                         writer.WriteStartElement (elementName);
390                         bool res = SerializeElement (writer, false);
391                         writer.WriteEndElement ();
392                         return res;
393                 }
394
395                 protected internal virtual void Unmerge (
396                                 ConfigurationElement source, ConfigurationElement parent,
397                                 ConfigurationSaveMode updateMode)
398                 {
399                         if (parent != null && source.GetType() != parent.GetType())
400                                 throw new ConfigurationException ("Can't unmerge two elements of different type");
401                         
402                         foreach (PropertyInformation prop in source.ElementInformation.Properties)
403                         {
404                                 if (prop.ValueOrigin == PropertyValueOrigin.Default)
405                                         continue;
406                                 
407                                 PropertyInformation unmergedProp = ElementInformation.Properties [prop.Name];
408                                 
409                                 object sourceValue = prop.Value;
410                                 if      (parent == null || !parent.HasValue (prop.Name)) {
411                                         unmergedProp.Value = sourceValue;
412                                         continue;
413                                 }
414                                 else if (sourceValue != null) {
415                                         object parentValue = parent [prop.Name];
416                                         if (prop.IsElement) {
417                                                 if (parentValue != null) {
418                                                         ConfigurationElement copy = (ConfigurationElement) unmergedProp.Value;
419                                                         copy.Unmerge ((ConfigurationElement) sourceValue, (ConfigurationElement) parentValue, updateMode);
420                                                 }
421                                                 else
422                                                         unmergedProp.Value = sourceValue;
423                                         }
424                                         else {
425                                                 if (!object.Equals (sourceValue, parentValue) || 
426                                                         (updateMode == ConfigurationSaveMode.Full) ||
427                                                         (updateMode == ConfigurationSaveMode.Modified && prop.ValueOrigin == PropertyValueOrigin.SetHere))
428                                                         unmergedProp.Value = sourceValue;
429                                         }
430                                 }
431                         }
432                 }
433                 
434                 internal bool HasValue (string propName)
435                 {
436                         PropertyInformation info = ElementInformation.Properties [propName];
437                         return info != null && info.ValueOrigin != PropertyValueOrigin.Default;
438                 }
439                 
440                 internal bool IsReadFromConfig (string propName)
441                 {
442                         PropertyInformation info = ElementInformation.Properties [propName];
443                         return info != null && info.ValueOrigin == PropertyValueOrigin.SetHere;
444                 }
445         }
446         
447         internal class ElementMap
448         {
449                 static Hashtable elementMaps = new Hashtable ();
450                 
451                 ConfigurationPropertyCollection properties;
452                 ConfigurationPropertyCollection keyProperties;
453                 
454                 ConfigurationCollectionAttribute collectionAttribute;
455                 
456                 public static ElementMap GetMap (Type t)
457                 {
458                         lock (elementMaps) {
459                                 ElementMap map = elementMaps [t] as ElementMap;
460                                 if (map != null) return map;
461                                 map = new ElementMap (t);
462                                 elementMaps [t] = map;
463                                 return map;
464                         }
465                 }
466                 
467                 public ElementMap (Type t)
468                 {
469                         ReflectProperties (t);
470                 }
471                 
472                 protected void ReflectProperties (Type t)
473                 {
474                         collectionAttribute = Attribute.GetCustomAttribute (t, typeof(ConfigurationCollectionAttribute)) as ConfigurationCollectionAttribute;
475                         
476                         PropertyInfo[] props = t.GetProperties ();
477                         foreach (PropertyInfo prop in props)
478                         {
479                                 ConfigurationPropertyAttribute at = Attribute.GetCustomAttribute (prop, typeof(ConfigurationPropertyAttribute)) as ConfigurationPropertyAttribute;
480                                 if (at == null) continue;
481                                 string name = at.Name != null ? at.Name : prop.Name;
482
483                                 if (at.DefaultValue != null && !prop.PropertyType.IsAssignableFrom (at.DefaultValue.GetType()))
484                                         throw new ConfigurationErrorsException (String.Format ("The default value for property '{0}' has a different type than the one of the property itself",
485                                                                                                name));
486
487                                 ConfigurationValidatorAttribute validatorAttr = Attribute.GetCustomAttribute (t, typeof(ConfigurationValidatorAttribute)) as ConfigurationValidatorAttribute;
488                                 ConfigurationValidatorBase validator = validatorAttr != null ? validatorAttr.ValidatorInstance : new DefaultValidator();
489                                 
490                                 TypeConverter converter = TypeDescriptor.GetConverter (prop.PropertyType);
491                                 ConfigurationProperty cp = new ConfigurationProperty (name, prop.PropertyType, at.DefaultValue, converter, validator, at.Options);
492                                 
493                                 cp.CollectionAttribute = Attribute.GetCustomAttribute (prop, typeof(ConfigurationCollectionAttribute)) as ConfigurationCollectionAttribute;
494                                 
495                                 if (properties == null) properties = new ConfigurationPropertyCollection ();
496                                 properties.Add (cp);
497                         }
498                 }
499                 
500                 public bool HasProperties
501                 {
502                         get { return properties != null && properties.Count > 0; }
503                 }
504                 
505                 public ConfigurationPropertyCollection Properties
506                 {
507                         get {
508                                 if (properties == null) properties = new ConfigurationPropertyCollection ();
509                                 return properties;
510                         }
511                 }
512                 
513                 public ConfigurationPropertyCollection KeyProperties {
514                         get {
515                                 if (keyProperties == null) {
516                                         keyProperties = new ConfigurationPropertyCollection ();
517                                         
518                                         if (properties != null)
519                                                 foreach (ConfigurationProperty p in properties)
520                                                         if (p.IsKey) keyProperties.Add (p);
521                                 }
522                                 return keyProperties;
523                         }
524                 }
525                 
526                 public ConfigurationCollectionAttribute CollectionAttribute {
527                         get { return collectionAttribute; }
528                 }
529         }
530 }
531
532 #endif