2010-07-06 Gonzalo Paniagua Javier <gonzalo@novell.com>
[mono.git] / mcs / class / System.Configuration / System.Configuration / ConfigurationElementCollection.cs
1 //
2 // System.Configuration.ConfigurationElementCollection.cs
3 //
4 // Authors:
5 //      Tim Coleman (tim@timcoleman.com)
6 //
7 // Copyright (C) Tim Coleman, 2004
8
9 //
10 // Permission is hereby granted, free of charge, to any person obtaining
11 // a copy of this software and associated documentation files (the
12 // "Software"), to deal in the Software without restriction, including
13 // without limitation the rights to use, copy, modify, merge, publish,
14 // distribute, sublicense, and/or sell copies of the Software, and to
15 // permit persons to whom the Software is furnished to do so, subject to
16 // the following conditions:
17 // 
18 // The above copyright notice and this permission notice shall be
19 // included in all copies or substantial portions of the Software.
20 // 
21 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
22 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
23 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
24 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
25 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
26 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
27 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
28 //
29 // Copyright (C) 2004 Novell, Inc (http://www.novell.com)
30 //
31
32 #if NET_2_0
33
34 using System.Collections;
35 using System.Diagnostics;
36 using System.Xml;
37
38 namespace System.Configuration 
39 {
40         [DebuggerDisplayAttribute ("Count = {Count}")]
41         public abstract 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                         modified = true;
286                 }
287
288                 bool CompareKeys (object key1, object key2)
289                 {
290                         if (comparer != null)
291                                 return comparer.Compare (key1, key2) == 0;
292                         else
293                                 return object.Equals (key1, key2);
294                 }
295
296                 public void CopyTo (ConfigurationElement[] array, int index)
297                 {
298                         list.CopyTo (array, index);
299                 }
300                 
301                 protected abstract ConfigurationElement CreateNewElement ();
302
303                 protected virtual ConfigurationElement CreateNewElement (string elementName)
304                 {
305                         return CreateNewElement ();
306                 }
307                 
308                 ConfigurationElement CreateNewElementInternal (string elementName)
309                 {
310                         ConfigurationElement elem;
311                         if (elementName == null)
312                                 elem = CreateNewElement ();
313                         else
314                                 elem = CreateNewElement (elementName);
315                         elem.Init ();
316                         return elem;
317                 }
318                 
319                 public override bool Equals (object compareTo)
320                 {
321                         ConfigurationElementCollection other = compareTo as ConfigurationElementCollection;
322                         if (other == null) return false;
323                         if (GetType() != other.GetType()) return false;
324                         if (Count != other.Count) return false;
325                         
326                         for (int n=0; n<Count; n++) {
327                                 if (!BaseGet (n).Equals (other.BaseGet (n)))
328                                         return false;
329                         }
330                         return true;
331                 }
332
333                 protected abstract object GetElementKey (ConfigurationElement element);
334
335                 public override int GetHashCode ()
336                 {
337                         int code = 0;
338                         for (int n=0; n<Count; n++)
339                                 code += BaseGet (n).GetHashCode ();
340                         return code;
341                 }
342                 
343                 void ICollection.CopyTo (Array arr, int index)
344                 {
345                         list.CopyTo (arr, index);
346                 }
347                 
348                 public IEnumerator GetEnumerator ()
349                 {
350                         return list.GetEnumerator ();
351                 }
352
353                 protected virtual bool IsElementName (string elementName)
354                 {
355                         return false;
356                 }
357
358                 protected virtual bool IsElementRemovable (ConfigurationElement element)
359                 {
360                         return !IsReadOnly ();
361                 }
362
363                 protected internal override bool IsModified ()
364                 {
365                         return modified;
366                 }
367
368                 [MonoTODO]
369                 public override bool IsReadOnly ()
370                 {
371                         return base.IsReadOnly ();
372                 }
373
374                 internal override bool HasValues ()
375                 {
376                         return list.Count > 0;
377                 }
378
379                 protected internal override void Reset (ConfigurationElement parentElement)
380                 {
381                         bool basic = IsBasic;
382                                 
383                         ConfigurationElementCollection parent = (ConfigurationElementCollection) parentElement;
384                         for (int n=0; n<parent.Count; n++)
385                         {
386                                 ConfigurationElement parentItem = parent.BaseGet (n);
387                                 ConfigurationElement item = CreateNewElementInternal (null);
388                                 item.Reset (parentItem);
389                                 BaseAdd (item);
390                                 
391                                 if (basic) {
392                                         if (inherited == null)
393                                                 inherited = new ArrayList ();
394                                         inherited.Add (item);
395                                 }
396                         }
397                         if (IsAlternate)
398                                 inheritedLimitIndex = 0;
399                         else
400                                 inheritedLimitIndex = Count - 1;
401                         modified = false;
402                 }
403
404                 protected internal override void ResetModified ()
405                 {
406                         modified = false;
407                 }
408
409                 [MonoTODO]
410                 protected internal override void SetReadOnly ()
411                 {
412                         base.SetReadOnly ();
413                 }
414
415                 protected internal override bool SerializeElement (XmlWriter writer, bool serializeCollectionKey)
416                 {
417                         if (serializeCollectionKey) {
418                                 return base.SerializeElement (writer, serializeCollectionKey);
419                         }
420                         
421                         bool wroteData = false;
422                         
423                         if (IsBasic)
424                         {
425                                 for (int n=0; n<list.Count; n++) {
426                                         ConfigurationElement elem = (ConfigurationElement) list [n];
427                                         if (ElementName != string.Empty)
428                                                 wroteData = elem.SerializeToXmlElement (writer, ElementName) || wroteData;
429                                         else
430                                                 wroteData = elem.SerializeElement (writer, false) || wroteData;
431                                 }
432                         }
433                         else
434                         {
435                                 if (emitClear) {
436                                         writer.WriteElementString (clearElementName, "");
437                                         wroteData = true;
438                                 }
439                                 
440                                 if (removed != null) {
441                                         for (int n=0; n<removed.Count; n++) {
442                                                 writer.WriteStartElement (removeElementName);
443                                                 ((ConfigurationElement)removed[n]).SerializeElement (writer, true);
444                                                 writer.WriteEndElement ();
445                                         }
446                                         wroteData = wroteData || removed.Count > 0;
447                                 }
448                                 
449                                 for (int n=0; n<list.Count; n++) {
450                                         ConfigurationElement elem = (ConfigurationElement) list [n];
451                                         elem.SerializeToXmlElement (writer, addElementName);
452                                 }
453                                 
454                                 wroteData = wroteData || list.Count > 0;
455                         }
456                         return wroteData;
457                 }
458
459                 protected override bool OnDeserializeUnrecognizedElement (string elementName, XmlReader reader)
460                 {
461                         if (IsBasic)
462                         {
463                                 ConfigurationElement elem = null;
464                                 
465                                 if (elementName == ElementName)
466                                         elem = CreateNewElementInternal (null);
467                                 if (IsElementName (elementName))
468                                         elem = CreateNewElementInternal (elementName);
469
470                                 if (elem != null) {
471                                         elem.DeserializeElement (reader, false);
472                                         BaseAdd (elem);
473                                         modified = false;
474                                         return true;
475                                 }
476                         }
477                         else {
478                                 if (elementName == clearElementName) {
479                                         reader.MoveToContent ();
480                                         if (reader.MoveToNextAttribute ())
481                                                 throw new ConfigurationErrorsException ("Unrecognized attribute '" + reader.LocalName + "'.");
482                                         reader.MoveToElement ();
483                                         reader.Skip ();
484                                         BaseClear ();
485                                         emitClear = true;
486                                         modified = false;
487                                         return true;
488                                 }
489                                 else if (elementName == removeElementName) {
490                                         ConfigurationElement elem = CreateNewElementInternal (null);
491                                         ConfigurationRemoveElement removeElem = new ConfigurationRemoveElement (elem, this);
492                                         removeElem.DeserializeElement (reader, true);
493                                         BaseRemove (removeElem.KeyValue);
494                                         modified = false;
495                                         return true;
496                                 }
497                                 else if (elementName == addElementName) {
498                                         ConfigurationElement elem = CreateNewElementInternal (null);
499                                         elem.DeserializeElement (reader, false);
500                                         BaseAdd (elem);
501                                         modified = false;
502                                         return true;
503                                 }
504                         }
505                         
506                         return false;
507                 }
508                 
509                 protected internal override void Unmerge (ConfigurationElement sourceElement, ConfigurationElement parentElement, ConfigurationSaveMode updateMode)
510                 {
511                         ConfigurationElementCollection source = (ConfigurationElementCollection) sourceElement;
512                         ConfigurationElementCollection parent = (ConfigurationElementCollection) parentElement;
513                         
514                         for (int n=0; n<source.Count; n++) {
515                                 ConfigurationElement sitem = source.BaseGet (n);
516                                 object key = source.GetElementKey (sitem);
517                                 ConfigurationElement pitem = parent != null ? parent.BaseGet (key) as ConfigurationElement : null;
518                                 if (pitem != null && updateMode != ConfigurationSaveMode.Full) {
519                                         ConfigurationElement nitem = CreateNewElementInternal (null);
520                                         nitem.Unmerge (sitem, pitem, ConfigurationSaveMode.Minimal);
521                                         if (nitem.HasValues ())
522                                                 BaseAdd (nitem);
523                                 }
524                                 else {
525                                         ConfigurationElement nitem = CreateNewElementInternal (null);
526                                         nitem.Unmerge (sitem, null, ConfigurationSaveMode.Full);
527                                         BaseAdd (nitem);
528                                 }
529                         }
530                         
531                         if (updateMode == ConfigurationSaveMode.Full)
532                                 EmitClear = true;
533                         else if (parent != null) {
534                                 for (int n=0; n<parent.Count; n++) {
535                                         ConfigurationElement pitem = parent.BaseGet (n);
536                                         object key = parent.GetElementKey (pitem);
537                                         if (source.IndexOfKey (key) == -1) {
538                                                 if (removed == null) removed = new ArrayList ();
539                                                 removed.Add (pitem);
540                                         }
541                                 }
542                         }
543                 }
544
545                 #endregion // Methods
546         }
547 }
548
549 #endif