New test.
[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                 public 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 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 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                         int old_index = IndexOfKey (GetElementKey (element));
158                         if (IsAlternate) {
159                                 list.Insert (inheritedLimitIndex, element);
160                                 inheritedLimitIndex++;
161                         }
162                         else {
163                                 list.Add (element);
164                         }
165                         if (!IsAlternate && old_index != -1)
166                                 list.RemoveAt (old_index);
167                         modified = true;
168                 }
169
170                 protected virtual void BaseAdd (int index, ConfigurationElement element)
171                 {
172                         if (ThrowOnDuplicate && BaseIndexOf (element) != -1)
173                                 throw new ConfigurationException ("Duplicate element in collection");
174                         if (IsReadOnly ())
175                                 throw new ConfigurationErrorsException ("Collection is read only.");
176                         
177                         if (IsAlternate && (index > inheritedLimitIndex))
178                                 throw new ConfigurationErrorsException ("Can't insert new elements below the inherited elements.");
179                         if (!IsAlternate && (index <= inheritedLimitIndex))
180                                 throw new ConfigurationErrorsException ("Can't insert new elements above the inherited elements.");
181                         
182                         list.Insert (index, element);
183                         modified = true;
184                 }
185
186                 protected internal void BaseClear ()
187                 {
188                         if (IsReadOnly ())
189                                 throw new ConfigurationErrorsException ("Collection is read only.");
190                                 
191                         list.Clear ();
192                         modified = true;
193                 }
194
195                 protected internal ConfigurationElement BaseGet (int index)
196                 {
197                         return (ConfigurationElement) list [index];
198                 }
199
200                 protected internal ConfigurationElement BaseGet (object key)
201                 {
202                         int index = IndexOfKey (key);
203                         if (index != -1) return (ConfigurationElement) list [index];
204                         else return null;
205                 }
206
207                 protected internal object[] BaseGetAllKeys ()
208                 {
209                         object[] keys = new object [list.Count];
210                         for (int n=0; n<list.Count; n++)
211                                 keys [n] = BaseGetKey (n);
212                         return keys;
213                 }
214
215                 protected internal object BaseGetKey (int index)
216                 {
217                         if (index < 0 || index >= list.Count)
218                                 throw new ConfigurationErrorsException (String.Format ("Index {0} is out of range", index));
219
220                         return GetElementKey ((ConfigurationElement) list[index]).ToString ();
221                 }
222
223                 protected int BaseIndexOf (ConfigurationElement element)
224                 {
225                         return list.IndexOf (element);
226                 }
227                 
228                 int IndexOfKey (object key)
229                 {
230                         for (int n=0; n<list.Count; n++) {
231                                 if (CompareKeys (GetElementKey ((ConfigurationElement) list[n]), key))
232                                         return n;
233                         }
234                         return -1;
235                 }
236
237                 protected internal bool BaseIsRemoved (object key)
238                 {
239                         if (removed == null)
240                                 return false;
241                         foreach (ConfigurationElement elem in removed) {
242                                 if (CompareKeys (GetElementKey (elem), key))
243                                         return true;
244                         }
245                         return false;
246                 }
247
248                 protected internal void BaseRemove (object key)
249                 {
250                         if (IsReadOnly ())
251                                 throw new ConfigurationErrorsException ("Collection is read only.");
252                                 
253                         int index = IndexOfKey (key);
254                         if (index != -1) {
255                                 BaseRemoveAt (index);
256                                 modified = true;
257                         }
258                 }
259
260                 protected internal void BaseRemoveAt (int index)
261                 {
262                         if (IsReadOnly ())
263                                 throw new ConfigurationErrorsException ("Collection is read only.");
264                                 
265                         ConfigurationElement elem = (ConfigurationElement) list [index];
266                         if (!IsElementRemovable (elem))
267                                 throw new ConfigurationErrorsException ("Element can't be removed from element collection.");
268                         
269                         if (inherited != null && inherited.Contains (elem))
270                                 throw new ConfigurationErrorsException ("Inherited items can't be removed.");
271                         
272                         list.RemoveAt (index);
273                         modified = true;
274                 }
275
276                 bool CompareKeys (object key1, object key2)
277                 {
278                         if (comparer != null)
279                                 return comparer.Compare (key1, key2) == 0;
280                         else
281                                 return object.Equals (key1, key2);
282                 }
283
284                 public void CopyTo (ConfigurationElement[] array, int index)
285                 {
286                         list.CopyTo (array, index);
287                 }
288                 
289                 protected abstract ConfigurationElement CreateNewElement ();
290
291                 protected virtual ConfigurationElement CreateNewElement (string elementName)
292                 {
293                         return CreateNewElement ();
294                 }
295                 
296                 ConfigurationElement CreateNewElementInternal (string elementName)
297                 {
298                         ConfigurationElement elem;
299                         if (elementName == null)
300                                 elem = CreateNewElement ();
301                         else
302                                 elem = CreateNewElement (elementName);
303                         elem.Init ();
304                         return elem;
305                 }
306                 
307                 public override bool Equals (object compareTo)
308                 {
309                         ConfigurationElementCollection other = compareTo as ConfigurationElementCollection;
310                         if (other == null) return false;
311                         if (GetType() != other.GetType()) return false;
312                         if (Count != other.Count) return false;
313                         
314                         for (int n=0; n<Count; n++) {
315                                 if (!BaseGet (n).Equals (other.BaseGet (n)))
316                                         return false;
317                         }
318                         return true;
319                 }
320
321                 protected abstract object GetElementKey (ConfigurationElement element);
322
323                 public override int GetHashCode ()
324                 {
325                         int code = 0;
326                         for (int n=0; n<Count; n++)
327                                 code += BaseGet (n).GetHashCode ();
328                         return code;
329                 }
330                 
331                 void ICollection.CopyTo (Array arr, int index)
332                 {
333                         list.CopyTo (arr, index);
334                 }
335                 
336                 public IEnumerator GetEnumerator ()
337                 {
338                         return list.GetEnumerator ();
339                 }
340
341                 protected virtual bool IsElementName (string elementName)
342                 {
343                         return false;
344                 }
345
346                 protected virtual bool IsElementRemovable (ConfigurationElement element)
347                 {
348                         return !IsReadOnly ();
349                 }
350
351                 protected internal override bool IsModified ()
352                 {
353                         return modified;
354                 }
355
356                 [MonoTODO]
357                 public override bool IsReadOnly ()
358                 {
359                         return base.IsReadOnly ();
360                 }
361
362                 internal override bool HasValues ()
363                 {
364                         return list.Count > 0;
365                 }
366
367                 protected internal override void Reset (ConfigurationElement parentElement)
368                 {
369                         bool basic = IsBasic;
370                                 
371                         ConfigurationElementCollection parent = (ConfigurationElementCollection) parentElement;
372                         for (int n=0; n<parent.Count; n++)
373                         {
374                                 ConfigurationElement parentItem = parent.BaseGet (n);
375                                 ConfigurationElement item = CreateNewElementInternal (null);
376                                 item.Reset (parentItem);
377                                 BaseAdd (item);
378                                 
379                                 if (basic) {
380                                         if (inherited == null)
381                                                 inherited = new ArrayList ();
382                                         inherited.Add (item);
383                                 }
384                         }
385                         if (IsAlternate)
386                                 inheritedLimitIndex = 0;
387                         else
388                                 inheritedLimitIndex = Count - 1;
389                         modified = false;
390                 }
391
392                 protected internal override void ResetModified ()
393                 {
394                         modified = false;
395                 }
396
397                 [MonoTODO]
398                 protected internal override void SetReadOnly ()
399                 {
400                         base.SetReadOnly ();
401                 }
402
403                 protected internal override bool SerializeElement (XmlWriter writer, bool serializeCollectionKey)
404                 {
405                         if (serializeCollectionKey) {
406                                 return base.SerializeElement (writer, serializeCollectionKey);
407                         }
408                         
409                         bool wroteData = false;
410                         
411                         if (IsBasic)
412                         {
413                                 for (int n=0; n<list.Count; n++) {
414                                         ConfigurationElement elem = (ConfigurationElement) list [n];
415                                         if (ElementName != string.Empty)
416                                                 wroteData = elem.SerializeToXmlElement (writer, ElementName) || wroteData;
417                                         else
418                                                 wroteData = elem.SerializeElement (writer, false) || wroteData;
419                                 }
420                         }
421                         else
422                         {
423                                 if (emitClear) {
424                                         writer.WriteElementString (clearElementName, "");
425                                         wroteData = true;
426                                 }
427                                 
428                                 if (removed != null) {
429                                         for (int n=0; n<removed.Count; n++) {
430                                                 writer.WriteStartElement (removeElementName);
431                                                 ((ConfigurationElement)removed[n]).SerializeElement (writer, true);
432                                                 writer.WriteEndElement ();
433                                         }
434                                         wroteData = wroteData || removed.Count > 0;
435                                 }
436                                 
437                                 for (int n=0; n<list.Count; n++) {
438                                         ConfigurationElement elem = (ConfigurationElement) list [n];
439                                         elem.SerializeToXmlElement (writer, addElementName);
440                                 }
441                                 
442                                 wroteData = wroteData || list.Count > 0;
443                         }
444                         return wroteData;
445                 }
446
447                 protected override bool OnDeserializeUnrecognizedElement (string elementName, XmlReader reader)
448                 {
449                         if (IsBasic)
450                         {
451                                 ConfigurationElement elem = null;
452                                 
453                                 if (elementName == ElementName)
454                                         elem = CreateNewElementInternal (null);
455                                 if (IsElementName (elementName))
456                                         elem = CreateNewElementInternal (elementName);
457
458                                 if (elem != null) {
459                                         elem.DeserializeElement (reader, false);
460                                         BaseAdd (elem);
461                                         modified = false;
462                                         return true;
463                                 }
464                         }
465                         else {
466                                 if (elementName == clearElementName) {
467                                         reader.MoveToContent ();
468                                         if (reader.MoveToNextAttribute ())
469                                                 throw new ConfigurationErrorsException ("Unrecognized attribute '" + reader.LocalName + "'.");
470                                         reader.MoveToElement ();
471                                         reader.Skip ();
472                                         BaseClear ();
473                                         emitClear = true;
474                                         modified = false;
475                                         return true;
476                                 }
477                                 else if (elementName == removeElementName) {
478                                         ConfigurationElement elem = CreateNewElementInternal (removeElementName);
479                                         elem.DeserializeElement (reader, true);
480                                         BaseRemove (GetElementKey (elem));
481                                         modified = false;
482                                         return true;
483                                 }
484                                 else if (elementName == addElementName) {
485                                         ConfigurationElement elem = CreateNewElementInternal (null);
486                                         elem.DeserializeElement (reader, false);
487                                         BaseAdd (elem);
488                                         modified = false;
489                                         return true;
490                                 }
491                         }
492                         
493                         return false;
494                 }
495                 
496                 protected internal override void Unmerge (ConfigurationElement sourceElement, ConfigurationElement parentElement, ConfigurationSaveMode updateMode)
497                 {
498                         ConfigurationElementCollection source = (ConfigurationElementCollection) sourceElement;
499                         ConfigurationElementCollection parent = (ConfigurationElementCollection) parentElement;
500                         
501                         for (int n=0; n<source.Count; n++) {
502                                 ConfigurationElement sitem = source.BaseGet (n);
503                                 object key = source.GetElementKey (sitem);
504                                 ConfigurationElement pitem = parent != null ? parent.BaseGet (key) as ConfigurationElement : null;
505                                 if (pitem != null && updateMode != ConfigurationSaveMode.Full) {
506                                         ConfigurationElement nitem = CreateNewElementInternal (null);
507                                         nitem.Unmerge (sitem, pitem, ConfigurationSaveMode.Minimal);
508                                         if (nitem.HasValues ())
509                                                 BaseAdd (nitem);
510                                 }
511                                 else {
512                                         ConfigurationElement nitem = CreateNewElementInternal (null);
513                                         nitem.Unmerge (sitem, null, ConfigurationSaveMode.Full);
514                                         BaseAdd (nitem);
515                                 }
516                         }
517                         
518                         if (updateMode == ConfigurationSaveMode.Full)
519                                 EmitClear = true;
520                         else if (parent != null) {
521                                 for (int n=0; n<parent.Count; n++) {
522                                         ConfigurationElement pitem = parent.BaseGet (n);
523                                         object key = parent.GetElementKey (pitem);
524                                         if (source.IndexOfKey (key) == -1) {
525                                                 if (removed == null) removed = new ArrayList ();
526                                                 removed.Add (pitem);
527                                         }
528                                 }
529                         }
530                 }
531
532                 #endregion // Methods
533         }
534 }
535
536 #endif