2005-09-27 Chris Toshok <toshok@ximian.com>
[mono.git] / mcs / class / System.Configuration / System.Configuration / ConfigurationElementCollection.cs
1 //
2 // System.Configuration.ConfigurationElementCollection.cs
3 //
4 // Authors:
5 //      Tim Coleman (tim@timcoleman.com)
6 //
7 // Copyright (C) Tim Coleman, 2004
8
9 //
10 // Permission is hereby granted, free of charge, to any person obtaining
11 // a copy of this software and associated documentation files (the
12 // "Software"), to deal in the Software without restriction, including
13 // without limitation the rights to use, copy, modify, merge, publish,
14 // distribute, sublicense, and/or sell copies of the Software, and to
15 // permit persons to whom the Software is furnished to do so, subject to
16 // the following conditions:
17 // 
18 // The above copyright notice and this permission notice shall be
19 // included in all copies or substantial portions of the Software.
20 // 
21 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
22 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
23 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
24 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
25 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
26 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
27 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
28 //
29 // Copyright (C) 2004 Novell, Inc (http://www.novell.com)
30 //
31
32 #if NET_2_0
33
34 using System.Collections;
35 using System.Diagnostics;
36 using System.Xml;
37
38 namespace System.Configuration 
39 {
40         [DebuggerDisplayAttribute ("Count = {Count}")]
41         public abstract class ConfigurationElementCollection : ConfigurationElement, ICollection, IEnumerable
42         {
43                 ArrayList list = new ArrayList ();
44                 ArrayList removed;
45                 ArrayList inherited;
46                 bool emitClear;
47                 bool modified;
48                 IComparer comparer;
49                 int inheritedLimitIndex;
50                 
51                 string addElementName = "add";
52                 string clearElementName = "clear";
53                 string removeElementName = "remove";
54                 
55                 #region Constructors
56
57                 protected ConfigurationElementCollection ()
58                 {
59                 }
60
61                 protected ConfigurationElementCollection (IComparer comparer)
62                 {
63                         this.comparer = comparer;
64                 }
65
66                 internal override void InitFromProperty (PropertyInformation propertyInfo)
67                 {
68                         ConfigurationCollectionAttribute colat = propertyInfo.Property.CollectionAttribute;
69                         if (colat == null)
70                                 colat = ElementMap.GetMap (GetType ()).CollectionAttribute;
71                         if (colat != null) {
72                                 addElementName = colat.AddItemName;
73                                 clearElementName = colat.ClearItemsName;
74                                 removeElementName = colat.RemoveItemName;
75                         }
76                         base.InitFromProperty (propertyInfo);
77                 }
78                 
79                 #endregion // Constructors
80
81                 #region Properties
82                 
83                 protected virtual ConfigurationElementCollectionType CollectionType {
84                         get { return ConfigurationElementCollectionType.AddRemoveClearMap; }
85                 }
86                 
87                 bool IsBasic {
88                         get {
89                                 return CollectionType == ConfigurationElementCollectionType.BasicMap ||
90                                                 CollectionType == ConfigurationElementCollectionType.BasicMapAlternate;
91                         }
92                 }
93                 
94                 bool IsAlternate {
95                         get {
96                                 return CollectionType == ConfigurationElementCollectionType.AddRemoveClearMapAlternate ||
97                                                 CollectionType == ConfigurationElementCollectionType.BasicMapAlternate;
98                         }
99                 }
100
101                 public virtual int Count {
102                         get { return list.Count; }
103                 }
104
105                 protected virtual string ElementName {
106                         get { return string.Empty; }
107                 }
108
109                 public bool EmitClear {
110                         get { return emitClear; }
111                         set { emitClear = value; }
112                 }
113
114                 public bool IsSynchronized {
115                         get { return false; }
116                 }
117
118                 public object SyncRoot {
119                         get { return this; }
120                 }
121
122                 protected virtual bool ThrowOnDuplicate {
123                         get { return true; }
124                 }
125                 
126                 protected internal string AddElementName {
127                         get { return addElementName; }
128                         set { addElementName = value; }
129                 }
130
131                 protected internal string ClearElementName {
132                         get { return clearElementName; }
133                         set { clearElementName = value; }
134                 }
135
136                 protected internal string RemoveElementName {
137                         get { return removeElementName; }
138                         set { removeElementName = value; }
139                 }
140
141                 #endregion // Properties
142
143                 #region Methods
144
145                 protected virtual void BaseAdd (ConfigurationElement element)
146                 {
147                         BaseAdd (element, ThrowOnDuplicate);
148                 }
149
150                 protected virtual void BaseAdd (ConfigurationElement element, bool throwIfExists)
151                 {
152 //                      if (throwIfExists && BaseIndexOf (element) != -1)
153 //                              throw new ConfigurationException ("Duplicate element in collection");
154                         if (IsReadOnly ())
155                                 throw new ConfigurationErrorsException ("Collection is read only.");
156                         
157                         if (IsAlternate) {
158                                 list.Insert (inheritedLimitIndex, element);
159                                 inheritedLimitIndex++;
160                         }
161                         else
162                                 list.Add (element);
163                         modified = true;
164                 }
165
166                 protected virtual void BaseAdd (int index, ConfigurationElement element)
167                 {
168 //                      if (ThrowOnDuplicate && BaseIndexOf (element) != -1)
169 //                              throw new ConfigurationException ("Duplicate element in collection");
170                         if (IsReadOnly ())
171                                 throw new ConfigurationErrorsException ("Collection is read only.");
172                         
173                         if (IsAlternate && (index > inheritedLimitIndex))
174                                 throw new ConfigurationErrorsException ("Can't insert new elements below the inherited elements.");
175                         if (!IsAlternate && (index <= inheritedLimitIndex))
176                                 throw new ConfigurationErrorsException ("Can't insert new elements above the inherited elements.");
177                                 
178                         list.Insert (index, element);
179                         modified = true;
180                 }
181
182                 protected internal void BaseClear ()
183                 {
184                         if (IsReadOnly ())
185                                 throw new ConfigurationErrorsException ("Collection is read only.");
186                                 
187                         list.Clear ();
188                         modified = true;
189                 }
190
191                 protected internal ConfigurationElement BaseGet (int index)
192                 {
193                         return (ConfigurationElement) list [index];
194                 }
195
196                 protected internal ConfigurationElement BaseGet (object key)
197                 {
198                         int index = IndexOfKey (key);
199                         if (index != -1) return (ConfigurationElement) list [index];
200                         else return null;
201                 }
202
203                 protected internal object[] BaseGetAllKeys ()
204                 {
205                         object[] keys = new object [list.Count];
206                         for (int n=0; n<list.Count; n++)
207                                 keys [n] = BaseGetKey (n);
208                         return keys;
209                 }
210
211                 protected internal object BaseGetKey (int index)
212                 {
213                         return GetElementKey ((ConfigurationElement) list[index]).ToString ();
214                 }
215
216                 protected int BaseIndexOf (ConfigurationElement element)
217                 {
218                         return list.IndexOf (element);
219                 }
220                 
221                 int IndexOfKey (object key)
222                 {
223                         for (int n=0; n<list.Count; n++) {
224                                 if (CompareKeys (GetElementKey ((ConfigurationElement) list[n]), key))
225                                         return n;
226                         }
227                         return -1;
228                 }
229
230                 protected internal bool BaseIsRemoved (object key)
231                 {
232                         if (removed == null)
233                                 return false;
234                         foreach (ConfigurationElement elem in removed) {
235                                 if (CompareKeys (GetElementKey (elem), key))
236                                         return true;
237                         }
238                         return false;
239                 }
240
241                 protected internal void BaseRemove (object key)
242                 {
243                         if (IsReadOnly ())
244                                 throw new ConfigurationErrorsException ("Collection is read only.");
245                                 
246                         int index = IndexOfKey (key);
247                         if (index != -1) {
248                                 BaseRemoveAt (index);
249                                 modified = true;
250                         }
251                 }
252
253                 protected internal void BaseRemoveAt (int index)
254                 {
255                         if (IsReadOnly ())
256                                 throw new ConfigurationErrorsException ("Collection is read only.");
257                                 
258                         ConfigurationElement elem = (ConfigurationElement) list [index];
259                         if (!IsElementRemovable (elem))
260                                 throw new ConfigurationErrorsException ("Element can't be removed from element collection.");
261                         
262                         if (inherited != null && inherited.Contains (elem))
263                                 throw new ConfigurationErrorsException ("Inherited items can't be removed.");
264                         
265                         list.RemoveAt (index);
266                         modified = true;
267                 }
268
269                 bool CompareKeys (object key1, object key2)
270                 {
271                         if (comparer != null)
272                                 return comparer.Compare (key1, key2) == 0;
273                         else
274                                 return object.Equals (key1, key2);
275                 }
276
277                 public void CopyTo (ConfigurationElement[] array, int index)
278                 {
279                         list.CopyTo (array, index);
280                 }
281                 
282                 protected abstract ConfigurationElement CreateNewElement ();
283
284                 protected virtual ConfigurationElement CreateNewElement (string elementName)
285                 {
286                         return CreateNewElement ();
287                 }
288                 
289                 ConfigurationElement CreateNewElementInternal (string elementName)
290                 {
291                         ConfigurationElement elem;
292                         if (elementName == null)
293                                 elem = CreateNewElement ();
294                         else
295                                 elem = CreateNewElement (elementName);
296                         elem.Init ();
297                         return elem;
298                 }
299                 
300                 public override bool Equals (object compareTo)
301                 {
302                         ConfigurationElementCollection other = compareTo as ConfigurationElementCollection;
303                         if (other == null) return false;
304                         if (GetType() != other.GetType()) return false;
305                         if (Count != other.Count) return false;
306                         
307                         for (int n=0; n<Count; n++) {
308                                 if (!BaseGet (n).Equals (other.BaseGet (n)))
309                                         return false;
310                         }
311                         return true;
312                 }
313
314                 protected abstract object GetElementKey (ConfigurationElement element);
315
316                 public override int GetHashCode ()
317                 {
318                         int code = 0;
319                         for (int n=0; n<Count; n++)
320                                 code += BaseGet (n).GetHashCode ();
321                         return code;
322                 }
323                 
324                 void ICollection.CopyTo (Array arr, int index)
325                 {
326                         list.CopyTo (arr, index);
327                 }
328                 
329                 public IEnumerator GetEnumerator ()
330                 {
331                         return list.GetEnumerator ();
332                 }
333
334                 protected virtual bool IsElementName (string elementName)
335                 {
336                         return false;
337                 }
338
339                 protected virtual bool IsElementRemovable (ConfigurationElement element)
340                 {
341                         return !IsReadOnly ();
342                 }
343
344                 protected internal override bool IsModified ()
345                 {
346                         return modified;
347                 }
348
349                 internal override bool HasValues ()
350                 {
351                         return list.Count > 0;
352                 }
353
354                 protected internal override void Reset (ConfigurationElement parentElement)
355                 {
356                         bool basic = IsBasic;
357                                 
358                         ConfigurationElementCollection parent = (ConfigurationElementCollection) parentElement;
359                         for (int n=0; n<parent.Count; n++)
360                         {
361                                 ConfigurationElement parentItem = parent.BaseGet (n);
362                                 ConfigurationElement item = CreateNewElementInternal (parentItem.GetType().FullName);
363                                 item.Reset (parentItem);
364                                 BaseAdd (item);
365                                 
366                                 if (basic) {
367                                         if (inherited == null)
368                                                 inherited = new ArrayList ();
369                                         inherited.Add (item);
370                                 }
371                         }
372                         if (IsAlternate)
373                                 inheritedLimitIndex = 0;
374                         else
375                                 inheritedLimitIndex = Count - 1;
376                         modified = false;
377                 }
378
379                 protected internal override void ResetModified ()
380                 {
381                         modified = false;
382                 }
383
384                 protected internal override bool SerializeElement (XmlWriter writer, bool serializeCollectionKey)
385                 {
386                         if (serializeCollectionKey) {
387                                 return base.SerializeElement (writer, serializeCollectionKey);
388                         }
389                         
390                         bool wroteData = false;
391                         
392                         if (IsBasic)
393                         {
394                                 for (int n=0; n<list.Count; n++) {
395                                         ConfigurationElement elem = (ConfigurationElement) list [n];
396                                         if (ElementName != string.Empty)
397                                                 wroteData = elem.SerializeToXmlElement (writer, ElementName) || wroteData;
398                                         else
399                                                 wroteData = elem.SerializeElement (writer, false) || wroteData;
400                                 }
401                         }
402                         else
403                         {
404                                 if (emitClear) {
405                                         writer.WriteElementString (clearElementName, "");
406                                         wroteData = true;
407                                 }
408                                 
409                                 if (removed != null) {
410                                         for (int n=0; n<removed.Count; n++) {
411                                                 writer.WriteStartElement (removeElementName);
412                                                 ((ConfigurationElement)removed[n]).SerializeElement (writer, true);
413                                                 writer.WriteEndElement ();
414                                         }
415                                         wroteData = wroteData || removed.Count > 0;
416                                 }
417                                 
418                                 for (int n=0; n<list.Count; n++) {
419                                         ConfigurationElement elem = (ConfigurationElement) list [n];
420                                         elem.SerializeToXmlElement (writer, addElementName);
421                                 }
422                                 
423                                 wroteData = wroteData || list.Count > 0;
424                         }
425                         return wroteData;
426                 }
427
428                 protected override bool OnDeserializeUnrecognizedElement (string elementName, XmlReader reader)
429                 {
430                         if (IsBasic)
431                         {
432                                 ConfigurationElement elem = null;
433                                 
434                                 if (elementName == ElementName)
435                                         elem = CreateNewElementInternal (null);
436                                 if (IsElementName (elementName))
437                                         elem = CreateNewElementInternal (elementName);
438
439                                 if (elem != null) {
440                                         elem.DeserializeElement (reader, false);
441                                         BaseAdd (elem);
442                                         modified = false;
443                                         return true;
444                                 }
445                         }
446                         else {
447                                 if (elementName == clearElementName) {
448                                         ConfigurationElement elem = CreateNewElementInternal (null);
449                                         elem.DeserializeElement (reader, true);
450                                         BaseClear ();
451                                         emitClear = true;
452                                         modified = false;
453                                         return true;
454                                 }
455                                 else if (elementName == removeElementName) {
456                                         ConfigurationElement elem = CreateNewElementInternal (null);
457                                         elem.DeserializeElement (reader, true);
458                                         BaseRemove (GetElementKey (elem));
459                                         modified = false;
460                                         return true;
461                                 }
462                                 else if (elementName == addElementName) {
463                                         ConfigurationElement elem = CreateNewElementInternal (null);
464                                         elem.DeserializeElement (reader, false);
465                                         BaseAdd (elem);
466                                         modified = false;
467                                         return true;
468                                 }
469                         }
470                         
471                         return false;
472                 }
473                 
474                 protected internal override void Unmerge (ConfigurationElement sourceElement, ConfigurationElement parentElement, ConfigurationSaveMode updateMode)
475                 {
476                         ConfigurationElementCollection source = (ConfigurationElementCollection) sourceElement;
477                         ConfigurationElementCollection parent = (ConfigurationElementCollection) parentElement;
478                         
479                         for (int n=0; n<source.Count; n++) {
480                                 ConfigurationElement sitem = source.BaseGet (n);
481                                 object key = source.GetElementKey (sitem);
482                                 ConfigurationElement pitem = parent != null ? parent.BaseGet (key) as ConfigurationElement : null;
483                                 if (pitem != null && updateMode != ConfigurationSaveMode.Full) {
484                                         ConfigurationElement nitem = CreateNewElementInternal (pitem.GetType().FullName);
485                                         nitem.Unmerge (sitem, pitem, ConfigurationSaveMode.Minimal);
486                                         if (nitem.HasValues ())
487                                                 BaseAdd (nitem);
488                                 }
489                                 else {
490                                         ConfigurationElement nitem = CreateNewElementInternal (sitem.GetType().FullName);
491                                         nitem.Unmerge (sitem, null, ConfigurationSaveMode.Full);
492                                         BaseAdd (nitem);
493                                 }
494                         }
495                         
496                         if (updateMode == ConfigurationSaveMode.Full)
497                                 EmitClear = true;
498                         else if (parent != null) {
499                                 for (int n=0; n<parent.Count; n++) {
500                                         ConfigurationElement pitem = parent.BaseGet (n);
501                                         object key = parent.GetElementKey (pitem);
502                                         if (source.IndexOfKey (key) == -1) {
503                                                 if (removed == null) removed = new ArrayList ();
504                                                 removed.Add (pitem);
505                                         }
506                                 }
507                         }
508                 }
509
510                 #endregion // Methods
511         }
512 }
513
514 #endif