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