Extension to commit 17bd0c42c5646b4ab08342fbf8137e7c5bfc0738. This ensures that inher...
[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 partial 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         
70                         if (colat == null)
71                                 colat = Attribute.GetCustomAttribute (propertyInfo.Type, typeof (ConfigurationCollectionAttribute)) as ConfigurationCollectionAttribute;
72
73                         if (colat != null) {
74                                 addElementName = colat.AddItemName;
75                                 clearElementName = colat.ClearItemsName;
76                                 removeElementName = colat.RemoveItemName;
77                         }
78                         base.InitFromProperty (propertyInfo);
79                 }
80                 
81                 #endregion // Constructors
82
83                 #region Properties
84                 
85                 public virtual ConfigurationElementCollectionType CollectionType {
86                         get { return ConfigurationElementCollectionType.AddRemoveClearMap; }
87                 }
88                 
89                 bool IsBasic {
90                         get {
91                                 return CollectionType == ConfigurationElementCollectionType.BasicMap ||
92                                                 CollectionType == ConfigurationElementCollectionType.BasicMapAlternate;
93                         }
94                 }
95                 
96                 bool IsAlternate {
97                         get {
98                                 return CollectionType == ConfigurationElementCollectionType.AddRemoveClearMapAlternate ||
99                                                 CollectionType == ConfigurationElementCollectionType.BasicMapAlternate;
100                         }
101                 }
102
103                 public int Count {
104                         get { return list.Count; }
105                 }
106
107                 protected virtual string ElementName {
108                         get { return string.Empty; }
109                 }
110
111                 public bool EmitClear {
112                         get { return emitClear; }
113                         set { emitClear = value; }
114                 }
115
116                 public bool IsSynchronized {
117                         get { return false; }
118                 }
119
120                 public object SyncRoot {
121                         get { return this; }
122                 }
123
124                 protected virtual bool ThrowOnDuplicate {
125                         get {
126                                 if (CollectionType != ConfigurationElementCollectionType.AddRemoveClearMap &&
127                                     CollectionType != ConfigurationElementCollectionType.AddRemoveClearMapAlternate)
128                                         return false;
129                                 
130                                 return true;
131                         }
132                 }
133                 
134                 protected internal string AddElementName {
135                         get { return addElementName; }
136                         set { addElementName = value; }
137                 }
138
139                 protected internal string ClearElementName {
140                         get { return clearElementName; }
141                         set { clearElementName = value; }
142                 }
143
144                 protected internal string RemoveElementName {
145                         get { return removeElementName; }
146                         set { removeElementName = value; }
147                 }
148
149                 #endregion // Properties
150
151                 #region Methods
152
153                 protected virtual void BaseAdd (ConfigurationElement element)
154                 {
155                         BaseAdd (element, ThrowOnDuplicate);
156                 }
157
158                 protected void BaseAdd (ConfigurationElement element, bool throwIfExists)
159                 {
160                         if (IsReadOnly ())
161                                 throw new ConfigurationErrorsException ("Collection is read only.");
162                         
163                         if (IsAlternate) {
164                                 list.Insert (inheritedLimitIndex, element);
165                                 inheritedLimitIndex++;
166                         }
167                         else {
168                                 int old_index = IndexOfKey (GetElementKey (element));
169                                 if (old_index >= 0) {
170                                         if (element.Equals (list [old_index]))
171                                                 return;
172                                         if (throwIfExists)
173                                                 throw new ConfigurationException ("Duplicate element in collection");
174                                         list.RemoveAt (old_index);
175                                 }
176                                 list.Add (element);
177                         }
178
179                         modified = true;
180                 }
181
182                 protected virtual void BaseAdd (int index, ConfigurationElement element)
183                 {
184                         if (ThrowOnDuplicate && BaseIndexOf (element) != -1)
185                                 throw new ConfigurationException ("Duplicate element in collection");
186                         if (IsReadOnly ())
187                                 throw new ConfigurationErrorsException ("Collection is read only.");
188                         
189                         if (IsAlternate && (index > inheritedLimitIndex))
190                                 throw new ConfigurationErrorsException ("Can't insert new elements below the inherited elements.");
191                         if (!IsAlternate && (index <= inheritedLimitIndex))
192                                 throw new ConfigurationErrorsException ("Can't insert new elements above the inherited elements.");
193                         
194                         list.Insert (index, element);
195                         modified = true;
196                 }
197
198                 protected internal void BaseClear ()
199                 {
200                         if (IsReadOnly ())
201                                 throw new ConfigurationErrorsException ("Collection is read only.");
202                                 
203                         list.Clear ();
204                         modified = true;
205                 }
206
207                 protected internal ConfigurationElement BaseGet (int index)
208                 {
209                         return (ConfigurationElement) list [index];
210                 }
211
212                 protected internal ConfigurationElement BaseGet (object key)
213                 {
214                         int index = IndexOfKey (key);
215                         if (index != -1) return (ConfigurationElement) list [index];
216                         else return null;
217                 }
218
219                 protected internal object[] BaseGetAllKeys ()
220                 {
221                         object[] keys = new object [list.Count];
222                         for (int n=0; n<list.Count; n++)
223                                 keys [n] = BaseGetKey (n);
224                         return keys;
225                 }
226
227                 protected internal object BaseGetKey (int index)
228                 {
229                         if (index < 0 || index >= list.Count)
230                                 throw new ConfigurationErrorsException (String.Format ("Index {0} is out of range", index));
231
232                         return GetElementKey ((ConfigurationElement) list[index]).ToString ();
233                 }
234
235                 protected int BaseIndexOf (ConfigurationElement element)
236                 {
237                         return list.IndexOf (element);
238                 }
239                 
240                 int IndexOfKey (object key)
241                 {
242                         for (int n=0; n<list.Count; n++) {
243                                 if (CompareKeys (GetElementKey ((ConfigurationElement) list[n]), key))
244                                         return n;
245                         }
246                         return -1;
247                 }
248
249                 protected internal bool BaseIsRemoved (object key)
250                 {
251                         if (removed == null)
252                                 return false;
253                         foreach (ConfigurationElement elem in removed) {
254                                 if (CompareKeys (GetElementKey (elem), key))
255                                         return true;
256                         }
257                         return false;
258                 }
259
260                 protected internal void BaseRemove (object key)
261                 {
262                         if (IsReadOnly ())
263                                 throw new ConfigurationErrorsException ("Collection is read only.");
264                                 
265                         int index = IndexOfKey (key);
266                         if (index != -1) {
267                                 BaseRemoveAt (index);
268                                 modified = true;
269                         }
270                 }
271
272                 protected internal void BaseRemoveAt (int index)
273                 {
274                         if (IsReadOnly ())
275                                 throw new ConfigurationErrorsException ("Collection is read only.");
276                                 
277                         ConfigurationElement elem = (ConfigurationElement) list [index];
278                         if (!IsElementRemovable (elem))
279                                 throw new ConfigurationErrorsException ("Element can't be removed from element collection.");
280                         
281                         if (inherited != null && inherited.Contains (elem))
282                                 throw new ConfigurationErrorsException ("Inherited items can't be removed.");
283                         
284                         list.RemoveAt (index);
285                         
286                         if (IsAlternate)
287                                 if (inheritedLimitIndex > 0)
288                                         inheritedLimitIndex--;
289                         
290                         modified = true;
291                 }
292
293                 bool CompareKeys (object key1, object key2)
294                 {
295                         if (comparer != null)
296                                 return comparer.Compare (key1, key2) == 0;
297                         else
298                                 return object.Equals (key1, key2);
299                 }
300
301                 public void CopyTo (ConfigurationElement[] array, int index)
302                 {
303                         list.CopyTo (array, index);
304                 }
305                 
306                 protected abstract ConfigurationElement CreateNewElement ();
307
308                 protected virtual ConfigurationElement CreateNewElement (string elementName)
309                 {
310                         return CreateNewElement ();
311                 }
312                 
313                 ConfigurationElement CreateNewElementInternal (string elementName)
314                 {
315                         ConfigurationElement elem;
316                         if (elementName == null)
317                                 elem = CreateNewElement ();
318                         else
319                                 elem = CreateNewElement (elementName);
320                         elem.Init ();
321                         return elem;
322                 }
323                 
324                 public override bool Equals (object compareTo)
325                 {
326                         ConfigurationElementCollection other = compareTo as ConfigurationElementCollection;
327                         if (other == null) return false;
328                         if (GetType() != other.GetType()) return false;
329                         if (Count != other.Count) return false;
330                         
331                         for (int n=0; n<Count; n++) {
332                                 if (!BaseGet (n).Equals (other.BaseGet (n)))
333                                         return false;
334                         }
335                         return true;
336                 }
337
338                 protected abstract object GetElementKey (ConfigurationElement element);
339
340                 public override int GetHashCode ()
341                 {
342                         int code = 0;
343                         for (int n=0; n<Count; n++)
344                                 code += BaseGet (n).GetHashCode ();
345                         return code;
346                 }
347                 
348                 void ICollection.CopyTo (Array arr, int index)
349                 {
350                         list.CopyTo (arr, index);
351                 }
352                 
353                 public IEnumerator GetEnumerator ()
354                 {
355                         return list.GetEnumerator ();
356                 }
357
358                 protected virtual bool IsElementName (string elementName)
359                 {
360                         return false;
361                 }
362
363                 protected virtual bool IsElementRemovable (ConfigurationElement element)
364                 {
365                         return !IsReadOnly ();
366                 }
367
368                 protected internal override bool IsModified ()
369                 {
370                         return modified;
371                 }
372
373                 [MonoTODO]
374                 public override bool IsReadOnly ()
375                 {
376                         return base.IsReadOnly ();
377                 }
378
379                 internal override bool HasValues ()
380                 {
381                         return list.Count > 0;
382                 }
383
384                 protected internal override void Reset (ConfigurationElement parentElement)
385                 {
386                         bool basic = IsBasic;
387                                 
388                         ConfigurationElementCollection parent = (ConfigurationElementCollection) parentElement;
389                         for (int n=0; n<parent.Count; n++)
390                         {
391                                 ConfigurationElement parentItem = parent.BaseGet (n);
392                                 ConfigurationElement item = CreateNewElementInternal (null);
393                                 item.Reset (parentItem);
394                                 BaseAdd (item);
395                                 
396                                 if (basic) {
397                                         if (inherited == null)
398                                                 inherited = new ArrayList ();
399                                         inherited.Add (item);
400                                 }
401                         }
402                         if (IsAlternate)
403                                 inheritedLimitIndex = 0;
404                         else
405                                 inheritedLimitIndex = Count - 1;
406                         modified = false;
407                 }
408
409                 protected internal override void ResetModified ()
410                 {
411                         modified = false;
412                 }
413
414                 [MonoTODO]
415                 protected internal override void SetReadOnly ()
416                 {
417                         base.SetReadOnly ();
418                 }
419
420                 protected internal override bool SerializeElement (XmlWriter writer, bool serializeCollectionKey)
421                 {
422                         if (serializeCollectionKey) {
423                                 return base.SerializeElement (writer, serializeCollectionKey);
424                         }
425                         
426                         bool wroteData = false;
427                         
428                         if (IsBasic)
429                         {
430                                 for (int n=0; n<list.Count; n++) {
431                                         ConfigurationElement elem = (ConfigurationElement) list [n];
432                                         if (ElementName != string.Empty)
433                                                 wroteData = elem.SerializeToXmlElement (writer, ElementName) || wroteData;
434                                         else
435                                                 wroteData = elem.SerializeElement (writer, false) || wroteData;
436                                 }
437                         }
438                         else
439                         {
440                                 if (emitClear) {
441                                         writer.WriteElementString (clearElementName, "");
442                                         wroteData = true;
443                                 }
444                                 
445                                 if (removed != null) {
446                                         for (int n=0; n<removed.Count; n++) {
447                                                 writer.WriteStartElement (removeElementName);
448                                                 ((ConfigurationElement)removed[n]).SerializeElement (writer, true);
449                                                 writer.WriteEndElement ();
450                                         }
451                                         wroteData = wroteData || removed.Count > 0;
452                                 }
453                                 
454                                 for (int n=0; n<list.Count; n++) {
455                                         ConfigurationElement elem = (ConfigurationElement) list [n];
456                                         elem.SerializeToXmlElement (writer, addElementName);
457                                 }
458                                 
459                                 wroteData = wroteData || list.Count > 0;
460                         }
461                         return wroteData;
462                 }
463
464                 protected override bool OnDeserializeUnrecognizedElement (string elementName, XmlReader reader)
465                 {
466                         if (IsBasic)
467                         {
468                                 ConfigurationElement elem = null;
469                                 
470                                 if (elementName == ElementName)
471                                         elem = CreateNewElementInternal (null);
472                                 if (IsElementName (elementName))
473                                         elem = CreateNewElementInternal (elementName);
474
475                                 if (elem != null) {
476                                         elem.DeserializeElement (reader, false);
477                                         BaseAdd (elem);
478                                         modified = false;
479                                         return true;
480                                 }
481                         }
482                         else {
483                                 if (elementName == clearElementName) {
484                                         reader.MoveToContent ();
485                                         if (reader.MoveToNextAttribute ())
486                                                 throw new ConfigurationErrorsException ("Unrecognized attribute '" + reader.LocalName + "'.");
487                                         reader.MoveToElement ();
488                                         reader.Skip ();
489                                         BaseClear ();
490                                         emitClear = true;
491                                         modified = false;
492                                         return true;
493                                 }
494                                 else if (elementName == removeElementName) {
495                                         ConfigurationElement elem = CreateNewElementInternal (null);
496                                         ConfigurationRemoveElement removeElem = new ConfigurationRemoveElement (elem, this);
497                                         removeElem.DeserializeElement (reader, true);
498                                         BaseRemove (removeElem.KeyValue);
499                                         modified = false;
500                                         return true;
501                                 }
502                                 else if (elementName == addElementName) {
503                                         ConfigurationElement elem = CreateNewElementInternal (null);
504                                         elem.DeserializeElement (reader, false);
505                                         BaseAdd (elem);
506                                         modified = false;
507                                         return true;
508                                 }
509                         }
510                         
511                         return false;
512                 }
513                 
514                 protected internal override void Unmerge (ConfigurationElement sourceElement, ConfigurationElement parentElement, ConfigurationSaveMode updateMode)
515                 {
516                         ConfigurationElementCollection source = (ConfigurationElementCollection) sourceElement;
517                         ConfigurationElementCollection parent = (ConfigurationElementCollection) parentElement;
518                         
519                         for (int n=0; n<source.Count; n++) {
520                                 ConfigurationElement sitem = source.BaseGet (n);
521                                 object key = source.GetElementKey (sitem);
522                                 ConfigurationElement pitem = parent != null ? parent.BaseGet (key) as ConfigurationElement : null;
523                                 if (pitem != null && updateMode != ConfigurationSaveMode.Full) {
524                                         ConfigurationElement nitem = CreateNewElementInternal (null);
525                                         nitem.Unmerge (sitem, pitem, ConfigurationSaveMode.Minimal);
526                                         if (nitem.HasValues ())
527                                                 BaseAdd (nitem);
528                                 }
529                                 else {
530                                         ConfigurationElement nitem = CreateNewElementInternal (null);
531                                         nitem.Unmerge (sitem, null, ConfigurationSaveMode.Full);
532                                         BaseAdd (nitem);
533                                 }
534                         }
535                         
536                         if (updateMode == ConfigurationSaveMode.Full)
537                                 EmitClear = true;
538                         else if (parent != null) {
539                                 for (int n=0; n<parent.Count; n++) {
540                                         ConfigurationElement pitem = parent.BaseGet (n);
541                                         object key = parent.GetElementKey (pitem);
542                                         if (source.IndexOfKey (key) == -1) {
543                                                 if (removed == null) removed = new ArrayList ();
544                                                 removed.Add (pitem);
545                                         }
546                                 }
547                         }
548                 }
549
550                 #endregion // Methods
551         }
552 }
553
554 #endif