**** Merged r40732-r40872 from MCS ****
[mono.git] / mcs / class / System / 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 && XML_DEP
31 #if XML_DEP
32 using System.Collections;
33 using System.Xml;
34 using System.Reflection;
35 using System.IO;
36 using System.ComponentModel;
37
38 namespace System.Configuration
39 {
40         public abstract class ConfigurationElement
41         {
42                 static Hashtable elementMaps = new Hashtable ();
43                 Hashtable values;
44                 string[] readProperties;
45                 string rawXml;
46                 bool modified;
47                 ElementMap map;
48                 ConfigurationPropertyCollection keyProps;
49                 
50                 protected ConfigurationElement ()
51                 {
52                 }
53                 
54                 internal string RawXml {
55                         get { return rawXml; }
56                         set { rawXml = value; }
57                 }
58
59                 protected internal virtual ConfigurationPropertyCollection CollectionKeyProperties {
60                         get {
61                                 return null;
62                         }
63                 }
64                 
65                 internal ConfigurationPropertyCollection GetKeyProperties ()
66                 {
67                         if (keyProps != null) return keyProps;
68                         keyProps = CollectionKeyProperties;
69                         if (keyProps != null) return keyProps;
70                         
71                         if (map.Properties == Properties)
72                                 keyProps = map.KeyProperties;
73                         else {
74                                 keyProps = new ConfigurationPropertyCollection ();
75                                 foreach (ConfigurationProperty prop in Properties) {
76                                         if (prop.IsKey)
77                                                 keyProps.Add (prop);
78                                 }
79                         }
80                         return keyProps;
81                 }
82
83                 protected internal object this [ConfigurationProperty property] {
84                         get {
85                                 if (values == null || !values.ContainsKey (property)) {
86                                         if (property.IsElement) {
87                                                 object elem = Activator.CreateInstance (property.Type);
88                                                 this [property] = elem;
89                                                 return elem;
90                                         }
91                                         else
92                                                 return property.DefaultValue;
93                                 }
94                                 else
95                                         return values [property];
96                         }
97
98                         set {
99                                 if (object.Equals (value, property.DefaultValue)) {
100                                         if (values == null) return;
101                                         values.Remove (property);
102                                 }
103                                 else {
104                                         if (values == null) values = new Hashtable ();
105                                         values [property] = value;
106                                 }
107                                 modified = true;
108                         }
109                 }
110
111                 protected internal object this [string property_name] {
112                         get {
113                                 ConfigurationProperty prop = Properties [property_name];
114                                 if (prop == null) throw new InvalidOperationException ("Property '" + property_name + "' not found in configuration section");
115                                 return this [prop];
116                         }
117
118                         set {
119                                 ConfigurationProperty prop = Properties [property_name];
120                                 if (prop == null) throw new InvalidOperationException ("Property '" + property_name + "' not found in configuration section");
121                                 this [prop] = value;
122                         }
123                 }
124
125                 protected internal virtual ConfigurationPropertyCollection Properties {
126                         get {
127                                 if (map == null)
128                                         map = GetMap (GetType());
129                                 return map.Properties;
130                         }
131                 }
132
133                 public override bool Equals (object compareTo)
134                 {
135                         ConfigurationElement other = compareTo as ConfigurationElement;
136                         if (other == null) return false;
137                         if (GetType() != other.GetType()) return false;
138                         
139                         foreach (ConfigurationProperty prop in Properties) {
140                                 if (!object.Equals (this [prop], other [prop]))
141                                         return false;
142                         }
143                         return true;
144                 }
145
146                 public override int GetHashCode ()
147                 {
148                         int code = 0;
149                         foreach (ConfigurationProperty prop in Properties)
150                                 code += this [prop].GetHashCode ();
151                         return code;
152                 }
153
154                 public bool HasValue (string key)
155                 {
156                         if (values == null) return false;
157                         ConfigurationProperty prop = Properties [key];
158                         if (prop == null) return false;
159                         return values.ContainsKey (prop);
160                 }
161                 
162                 internal virtual bool HasValues ()
163                 {
164                         return values != null && values.Count > 0;
165                 }
166
167                 [MonoTODO]
168                 public string PropertyFileName ()
169                 {
170                         throw new NotImplementedException ();
171                 }
172
173                 [MonoTODO]
174                 public int PropertyLineNumber ()
175                 {
176                         throw new NotImplementedException ();
177                 }
178
179                 [MonoTODO]
180                 protected internal virtual void Deserialize (XmlReader reader, bool serializeCollectionKey)
181                 {
182                         Hashtable readProps = new Hashtable ();
183                         
184                         reader.MoveToContent ();
185                         while (reader.MoveToNextAttribute ())
186                         {
187                                 ConfigurationProperty prop = Properties [reader.LocalName];
188                                 if (prop == null || (serializeCollectionKey && !prop.IsKey)) {
189                                         if (!HandleUnrecognizedAttribute (reader.LocalName, reader.Value))
190                                                 throw new ConfigurationException ("Unrecognized attribute '" + reader.LocalName + "'.");
191                                         continue;
192                                 }
193                                 
194                                 if (readProps.ContainsKey (prop))
195                                         throw new ConfigurationException ("The attribute '" + prop.Name + "' may only appear once in this element.");
196                                 
197                                 object val = prop.ConvertFromString (reader.Value);
198                                 if (!object.Equals (val, prop.DefaultValue))
199                                         this [prop] = val;
200                                 readProps [prop] = prop.Name;
201                         }
202                         
203                         reader.MoveToElement ();
204                         if (reader.IsEmptyElement) {
205                                 reader.Skip ();
206                         }
207                         else {
208                                 reader.ReadStartElement ();
209                                 reader.MoveToContent ();
210                                 
211                                 while (reader.NodeType != XmlNodeType.EndElement)
212                                 {
213                                         if (reader.NodeType != XmlNodeType.Element) {
214                                                 reader.Skip ();
215                                                 continue;
216                                         }
217                                         
218                                         ConfigurationProperty prop = Properties [reader.LocalName];
219                                         if (prop == null || (serializeCollectionKey && !prop.IsKey)) {
220                                                 if (!HandleUnrecognizedElement (reader.LocalName, reader))
221                                                         throw new ConfigurationException ("Unrecognized element '" + reader.LocalName + "'.");
222                                                 continue;
223                                         }
224                                         
225                                         if (!prop.IsElement)
226                                                 throw new ConfigurationException ("Property '" + prop.Name + "' is not a ConfigurationElement.");
227                                         
228                                         if (readProps.Contains (prop))
229                                                 throw new ConfigurationException ("The element <" + prop.Name + "> may only appear once in this section.");
230                                         
231                                         ConfigurationElement val = this [prop] as ConfigurationElement;
232                                         val.Deserialize (reader, serializeCollectionKey);
233                                         readProps [prop] = prop.Name;
234                                 }
235                         }
236                         
237                         modified = false;
238                                 
239                         if (readProps.Count > 0) {
240                                 readProperties = new string [readProps.Count];
241                                 readProps.Values.CopyTo ((object[])readProperties, 0);
242                         }
243                 }
244
245                 protected virtual bool HandleUnrecognizedAttribute (string name, string value)
246                 {
247                         return false;
248                 }
249
250                 protected virtual bool HandleUnrecognizedElement (string element, XmlReader reader)
251                 {
252                         return false;
253                 }
254
255                 protected internal virtual void InitializeDefault ()
256                 {
257                         values = null;
258                 }
259
260                 protected internal virtual bool IsModified ()
261                 {
262                         return modified;
263                 }
264
265                 protected internal virtual void ReadXml (XmlReader reader, object context)
266                 {
267                         Deserialize (reader, false);
268                 }
269
270                 protected internal virtual void Reset (ConfigurationElement parentElement, object context)
271                 {
272                         if (parentElement != null) {
273                                 values = null;
274                                 foreach (ConfigurationProperty prop in Properties) {
275                                         if (parentElement.HasValue (prop.Name)) {
276                                                 if (prop.IsElement) {
277                                                         ConfigurationElement parentValue = parentElement [prop.Name] as ConfigurationElement;
278                                                         ConfigurationElement value = Activator.CreateInstance (parentValue.GetType()) as ConfigurationElement;
279                                                         value.Reset (parentValue, context);
280                                                         this [prop] = value;
281                                                 }
282                                                 else
283                                                         this [prop] = parentElement [prop.Name];
284                                         }
285                                 }
286                         }
287                         else
288                                 InitializeDefault ();
289                 }
290
291                 protected internal virtual void ResetModified ()
292                 {
293                         modified = false;
294                 }
295
296                 [MonoTODO ("Return value?")]
297                 protected internal virtual bool Serialize (XmlWriter writer, bool serializeCollectionKey)
298                 {
299                         if (values == null) return true;
300                         
301                         if (serializeCollectionKey) {
302                                 foreach (ConfigurationProperty prop in GetKeyProperties ())
303                                         writer.WriteAttributeString (prop.Name, prop.ConvertToString (this[prop]));
304                                 return true;
305                         }
306                         
307                         ArrayList elems = new ArrayList ();
308                         foreach (DictionaryEntry entry in values)
309                         {
310                                 ConfigurationProperty prop = (ConfigurationProperty) entry.Key;
311                                 if (prop.IsElement) continue;
312                                 
313                                 if (!object.Equals (entry.Value, prop.DefaultValue))
314                                         writer.WriteAttributeString (prop.Name, prop.ConvertToString (entry.Value));
315                         }
316                         
317                         foreach (DictionaryEntry entry in values)
318                         {
319                                 ConfigurationProperty prop = (ConfigurationProperty) entry.Key;
320                                 if (!prop.IsElement) continue;
321                                 
322                                 ConfigurationElement val = entry.Value as ConfigurationElement;
323                                 if (val != null && val.HasValues ())
324                                         val.SerializeToXmlElement (writer, prop.Name);
325                         }
326                         return true;
327                 }
328                                 
329                 [MonoTODO]
330                 protected internal virtual bool SerializeAttributeOnRemove (
331                                 ConfigurationProperty property)
332                 {
333                         throw new NotImplementedException ();
334                 }
335
336                 protected internal virtual bool SerializeToXmlElement (
337                                 XmlWriter writer, string elementName)
338                 {
339                         writer.WriteStartElement (elementName);
340                         Serialize (writer, false);
341                         writer.WriteEndElement ();
342                         return true;
343                 }
344
345                 protected internal virtual void UnMerge (
346                                 ConfigurationElement source, ConfigurationElement parent,
347                                 bool serializeCollectionKey, object context,
348                                 ConfigurationUpdateMode updateMode)
349                 {
350                         if (source.GetType() != parent.GetType())
351                                 throw new ConfigurationException ("Can't unmerge two elements of different type");
352                         
353                         foreach (ConfigurationProperty prop in source.Properties)
354                         {
355                                 if (!source.HasValue (prop.Name)) continue;
356                                 
357                                 object sourceValue = source [prop];
358                                 if      (!parent.HasValue (prop.Name)) {
359                                         this [prop] = sourceValue;
360                                         continue;
361                                 }
362                                 else if (sourceValue != null) {
363                                         object parentValue = parent [prop];
364                                         if (prop.IsElement) {
365                                                 if (parentValue != null) {
366                                                         ConfigurationElement copy = (ConfigurationElement) Activator.CreateInstance (prop.Type);
367                                                         copy.UnMerge ((ConfigurationElement) sourceValue, (ConfigurationElement) parentValue, serializeCollectionKey, context, updateMode);
368                                                         this [prop] = copy;
369                                                 }
370                                                 else
371                                                         this [prop] = sourceValue;
372                                         }
373                                         else {
374                                                 if (!object.Equals (sourceValue, parentValue) || 
375                                                         (updateMode == ConfigurationUpdateMode.Full) ||
376                                                         (updateMode == ConfigurationUpdateMode.Modified && source.IsReadFromConfig (prop.Name)))
377                                                         this [prop] = sourceValue;
378                                         }
379                                 }
380                         }
381                 }
382                 
383                 bool IsReadFromConfig (string propName)
384                 {
385                         return readProperties != null && Array.IndexOf (readProperties, propName) != -1;
386                 }
387
388                 [MonoTODO]
389                 protected virtual void ValidateRequiredProperties (
390                                 ConfigurationPropertyCollection properties,
391                                 bool serialize_collection_key)
392                 {
393                         throw new NotImplementedException ();
394                 }
395
396                 protected internal virtual string WriteXml (
397                                 ConfigurationElement parent,
398                                 object context, string name,
399                                 ConfigurationUpdateMode updateMode)
400                 {
401                         ConfigurationElement elem;
402                         if (parent != null) {
403                                 elem = (ConfigurationElement) Activator.CreateInstance (GetType());
404                                 elem.UnMerge (this, parent, false, context, updateMode);
405                         }
406                         else
407                                 elem = this;
408                         
409                         StringWriter sw = new StringWriter ();
410                         XmlTextWriter tw = new XmlTextWriter (sw);
411                         tw.Formatting = Formatting.Indented;
412                         elem.SerializeToXmlElement (tw, name);
413                         tw.Close ();
414                         return sw.ToString ();
415                 }
416                 
417                 internal static ElementMap GetMap (Type t)
418                 {
419                         lock (elementMaps) {
420                                 ElementMap map = elementMaps [t] as ElementMap;
421                                 if (map != null) return map;
422                                 map = new ElementMap (t);
423                                 elementMaps [t] = map;
424                                 return map;
425                         }
426                 }
427         }
428         
429         internal class ElementMap
430         {
431                 ConfigurationPropertyCollection properties;
432                 ConfigurationPropertyCollection keyProperties;
433                 
434                 public ElementMap (Type t)
435                 {
436                         ReflectProperties (t);
437                 }
438                 
439                 protected void ReflectProperties (Type t)
440                 {
441                         PropertyInfo[] props = t.GetProperties ();
442                         foreach (PropertyInfo prop in props)
443                         {
444                                 ConfigurationPropertyAttribute at = (ConfigurationPropertyAttribute) Attribute.GetCustomAttribute (prop, typeof(ConfigurationPropertyAttribute)) as ConfigurationPropertyAttribute;
445                                 if (at == null) continue;
446                                 string name = at.Name != null ? at.Name : prop.Name;
447                                 
448                                 ConfigurationValidationAttribute validator = (ConfigurationValidationAttribute) Attribute.GetCustomAttribute (t, typeof(ConfigurationValidationAttribute)) as ConfigurationValidationAttribute;
449                                 TypeConverter converter = TypeDescriptor.GetConverter (prop.PropertyType);
450                                 ConfigurationProperty cp = new ConfigurationProperty (name, prop.PropertyType, at.DefaultValue, converter, validator, at.Flags);
451                                 
452                                 if (properties == null) properties = new ConfigurationPropertyCollection ();
453                                 properties.Add (cp);
454                         }
455                 }
456                 
457                 public bool HasProperties
458                 {
459                         get { return properties != null && properties.Count > 0; }
460                 }
461                 
462                 public ConfigurationPropertyCollection Properties
463                 {
464                         get {
465                                 if (properties == null) properties = new ConfigurationPropertyCollection ();
466                                 return properties;
467                         }
468                 }
469                 
470                 public ConfigurationPropertyCollection KeyProperties {
471                         get {
472                                 if (keyProperties == null) {
473                                         keyProperties = new ConfigurationPropertyCollection ();
474                                         
475                                         if (properties != null)
476                                                 foreach (ConfigurationProperty p in properties)
477                                                         if (p.IsKey) keyProperties.Add (p);
478                                 }
479                                 return keyProperties;
480                         }
481                 }
482         }
483 }
484
485 #endif
486 #endif