Merge pull request #725 from knocte/threadpool_init
[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 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 ConfigurationErrorsException ("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 ConfigurationErrorsException ("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
291                         modified = true;
292                 }
293
294                 bool CompareKeys (object key1, object key2)
295                 {
296                         if (comparer != null)
297                                 return comparer.Compare (key1, key2) == 0;
298                         else
299                                 return object.Equals (key1, key2);
300                 }
301
302                 public void CopyTo (ConfigurationElement[] array, int index)
303                 {
304                         list.CopyTo (array, index);
305                 }
306                 
307                 protected abstract ConfigurationElement CreateNewElement ();
308
309                 protected virtual ConfigurationElement CreateNewElement (string elementName)
310                 {
311                         return CreateNewElement ();
312                 }
313                 
314                 ConfigurationElement CreateNewElementInternal (string elementName)
315                 {
316                         ConfigurationElement elem;
317                         if (elementName == null)
318                                 elem = CreateNewElement ();
319                         else
320                                 elem = CreateNewElement (elementName);
321                         elem.Init ();
322                         return elem;
323                 }
324                 
325                 public override bool Equals (object compareTo)
326                 {
327                         ConfigurationElementCollection other = compareTo as ConfigurationElementCollection;
328                         if (other == null) return false;
329                         if (GetType() != other.GetType()) return false;
330                         if (Count != other.Count) return false;
331                         
332                         for (int n=0; n<Count; n++) {
333                                 if (!BaseGet (n).Equals (other.BaseGet (n)))
334                                         return false;
335                         }
336                         return true;
337                 }
338
339                 protected abstract object GetElementKey (ConfigurationElement element);
340
341                 public override int GetHashCode ()
342                 {
343                         int code = 0;
344                         for (int n=0; n<Count; n++)
345                                 code += BaseGet (n).GetHashCode ();
346                         return code;
347                 }
348                 
349                 void ICollection.CopyTo (Array arr, int index)
350                 {
351                         list.CopyTo (arr, index);
352                 }
353                 
354                 public IEnumerator GetEnumerator ()
355                 {
356                         return list.GetEnumerator ();
357                 }
358
359                 protected virtual bool IsElementName (string elementName)
360                 {
361                         return false;
362                 }
363
364                 protected virtual bool IsElementRemovable (ConfigurationElement element)
365                 {
366                         return !IsReadOnly ();
367                 }
368
369                 protected internal override bool IsModified ()
370                 {
371                         if (modified)
372                                 return true;
373
374                         for (int n=0; n<list.Count; n++) {
375                                 ConfigurationElement elem = (ConfigurationElement) list [n];
376                                 if (!elem.IsModified ())
377                                         continue;
378                                 modified = true;
379                                 break;
380                         }
381
382                         return modified;
383                 }
384
385                 [MonoTODO]
386                 public override bool IsReadOnly ()
387                 {
388                         return base.IsReadOnly ();
389                 }
390
391                 internal override void PrepareSave (ConfigurationElement parentElement, ConfigurationSaveMode mode)
392                 {
393                         var parent = (ConfigurationElementCollection)parentElement;
394                         base.PrepareSave (parentElement, mode);
395
396                         for (int n=0; n<list.Count; n++) {
397                                 ConfigurationElement elem = (ConfigurationElement) list [n];
398                                 object key = GetElementKey (elem);
399                                 ConfigurationElement pitem = parent != null ? parent.BaseGet (key) as ConfigurationElement : null;
400
401                                 elem.PrepareSave (pitem, mode);
402                         }
403                 }
404
405                 internal override bool HasValues (ConfigurationElement parentElement, ConfigurationSaveMode mode)
406                 {
407                         var parent = (ConfigurationElementCollection)parentElement;
408
409                         if (mode == ConfigurationSaveMode.Full)
410                                 return list.Count > 0;
411
412                         for (int n=0; n<list.Count; n++) {
413                                 ConfigurationElement elem = (ConfigurationElement) list [n];
414                                 object key = GetElementKey (elem);
415                                 ConfigurationElement pitem = parent != null ? parent.BaseGet (key) as ConfigurationElement : null;
416
417                                 if (elem.HasValues (pitem, mode))
418                                         return true;
419                         }
420
421                         return false;
422                 }
423
424                 protected internal override void Reset (ConfigurationElement parentElement)
425                 {
426                         bool basic = IsBasic;
427                                 
428                         ConfigurationElementCollection parent = (ConfigurationElementCollection) parentElement;
429                         for (int n=0; n<parent.Count; n++)
430                         {
431                                 ConfigurationElement parentItem = parent.BaseGet (n);
432                                 ConfigurationElement item = CreateNewElementInternal (null);
433                                 item.Reset (parentItem);
434                                 BaseAdd (item);
435                                 
436                                 if (basic) {
437                                         if (inherited == null)
438                                                 inherited = new ArrayList ();
439                                         inherited.Add (item);
440                                 }
441                         }
442                         if (IsAlternate)
443                                 inheritedLimitIndex = 0;
444                         else
445                                 inheritedLimitIndex = Count - 1;
446                         modified = false;
447                 }
448
449                 protected internal override void ResetModified ()
450                 {
451                         modified = false;
452                         for (int n=0; n<list.Count; n++) {
453                                 ConfigurationElement elem = (ConfigurationElement) list [n];
454                                 elem.ResetModified ();
455                         }
456                 }
457
458                 [MonoTODO]
459                 protected internal override void SetReadOnly ()
460                 {
461                         base.SetReadOnly ();
462                 }
463
464                 protected internal override bool SerializeElement (XmlWriter writer, bool serializeCollectionKey)
465                 {
466                         if (serializeCollectionKey) {
467                                 return base.SerializeElement (writer, serializeCollectionKey);
468                         }
469                         
470                         bool wroteData = false;
471                         
472                         if (IsBasic)
473                         {
474                                 for (int n=0; n<list.Count; n++) {
475                                         ConfigurationElement elem = (ConfigurationElement) list [n];
476                                         if (ElementName != string.Empty)
477                                                 wroteData = elem.SerializeToXmlElement (writer, ElementName) || wroteData;
478                                         else
479                                                 wroteData = elem.SerializeElement (writer, false) || wroteData;
480                                 }
481                         }
482                         else
483                         {
484                                 if (emitClear) {
485                                         writer.WriteElementString (clearElementName, "");
486                                         wroteData = true;
487                                 }
488                                 
489                                 if (removed != null) {
490                                         for (int n=0; n<removed.Count; n++) {
491                                                 writer.WriteStartElement (removeElementName);
492                                                 ((ConfigurationElement)removed[n]).SerializeElement (writer, true);
493                                                 writer.WriteEndElement ();
494                                         }
495                                         wroteData = wroteData || removed.Count > 0;
496                                 }
497                                 
498                                 for (int n=0; n<list.Count; n++) {
499                                         ConfigurationElement elem = (ConfigurationElement) list [n];
500                                         elem.SerializeToXmlElement (writer, addElementName);
501                                 }
502                                 
503                                 wroteData = wroteData || list.Count > 0;
504                         }
505                         return wroteData;
506                 }
507
508                 protected override bool OnDeserializeUnrecognizedElement (string elementName, XmlReader reader)
509                 {
510                         if (IsBasic)
511                         {
512                                 ConfigurationElement elem = null;
513                                 
514                                 if (elementName == ElementName)
515                                         elem = CreateNewElementInternal (null);
516                                 if (IsElementName (elementName))
517                                         elem = CreateNewElementInternal (elementName);
518
519                                 if (elem != null) {
520                                         elem.DeserializeElement (reader, false);
521                                         BaseAdd (elem);
522                                         modified = false;
523                                         return true;
524                                 }
525                         }
526                         else {
527                                 if (elementName == clearElementName) {
528                                         reader.MoveToContent ();
529                                         if (reader.MoveToNextAttribute ())
530                                                 throw new ConfigurationErrorsException ("Unrecognized attribute '" + reader.LocalName + "'.");
531                                         reader.MoveToElement ();
532                                         reader.Skip ();
533                                         BaseClear ();
534                                         emitClear = true;
535                                         modified = false;
536                                         return true;
537                                 }
538                                 else if (elementName == removeElementName) {
539                                         ConfigurationElement elem = CreateNewElementInternal (null);
540                                         ConfigurationRemoveElement removeElem = new ConfigurationRemoveElement (elem, this);
541                                         removeElem.DeserializeElement (reader, true);
542                                         BaseRemove (removeElem.KeyValue);
543                                         modified = false;
544                                         return true;
545                                 }
546                                 else if (elementName == addElementName) {
547                                         ConfigurationElement elem = CreateNewElementInternal (null);
548                                         elem.DeserializeElement (reader, false);
549                                         BaseAdd (elem);
550                                         modified = false;
551                                         return true;
552                                 }
553                         }
554                         
555                         return false;
556                 }
557                 
558                 protected internal override void Unmerge (ConfigurationElement sourceElement, ConfigurationElement parentElement, ConfigurationSaveMode updateMode)
559                 {
560                         ConfigurationElementCollection source = (ConfigurationElementCollection) sourceElement;
561                         ConfigurationElementCollection parent = (ConfigurationElementCollection) parentElement;
562                         
563                         for (int n=0; n<source.Count; n++) {
564                                 ConfigurationElement sitem = source.BaseGet (n);
565                                 object key = source.GetElementKey (sitem);
566                                 ConfigurationElement pitem = parent != null ? parent.BaseGet (key) as ConfigurationElement : null;
567                                 ConfigurationElement nitem = CreateNewElementInternal (null);
568                                 if (pitem != null && updateMode != ConfigurationSaveMode.Full) {
569                                         nitem.Unmerge (sitem, pitem, updateMode);
570                                         if (nitem.HasValues (pitem, updateMode))
571                                                 BaseAdd (nitem);
572                                 } else {
573                                         nitem.Unmerge (sitem, null, ConfigurationSaveMode.Full);
574                                         BaseAdd (nitem);
575                                 }
576                         }
577                         
578                         if (updateMode == ConfigurationSaveMode.Full)
579                                 EmitClear = true;
580                         else if (parent != null) {
581                                 for (int n=0; n<parent.Count; n++) {
582                                         ConfigurationElement pitem = parent.BaseGet (n);
583                                         object key = parent.GetElementKey (pitem);
584                                         if (source.IndexOfKey (key) == -1) {
585                                                 if (removed == null) removed = new ArrayList ();
586                                                 removed.Add (pitem);
587                                         }
588                                 }
589                         }
590                 }
591
592                 #endregion // Methods
593         }
594 }
595