1 //------------------------------------------------------------------------------ 2 // <copyright file="ConfigurationElementCollection.cs" company="Microsoft"> 3 // Copyright (c) Microsoft Corporation. All rights reserved. 4 // </copyright> 5 //------------------------------------------------------------------------------ 6 7 using System; 8 using System.Collections; 9 using System.Collections.Specialized; 10 using System.Xml; 11 12 namespace System.Configuration { 13 14 [System.Diagnostics.DebuggerDisplay("Count = {Count}")] 15 public abstract class ConfigurationElementCollection : ConfigurationElement, ICollection { 16 internal const string DefaultAddItemName = "add"; 17 internal const string DefaultRemoveItemName = "remove"; 18 internal const string DefaultClearItemsName = "clear"; 19 20 private int _removedItemCount = 0; // Number of items removed for this collection (not including parent) 21 private int _inheritedCount = 0; // Total number of inherited items 22 private ArrayList _items = new ArrayList(); 23 private String _addElement = DefaultAddItemName; 24 private String _removeElement = DefaultRemoveItemName; 25 private String _clearElement = DefaultClearItemsName; 26 private bool bEmitClearTag = false; 27 private bool bCollectionCleared = false; 28 private bool bModified = false; 29 private bool bReadOnly = false; 30 private IComparer _comparer; 31 internal bool internalAddToEnd = false; 32 internal String internalElementTagName = string.Empty; 33 ConfigurationElementCollection()34 protected ConfigurationElementCollection() { 35 } 36 ConfigurationElementCollection(IComparer comparer)37 protected ConfigurationElementCollection(IComparer comparer) { 38 if (comparer == null) { 39 throw new ArgumentNullException("comparer"); 40 } 41 42 _comparer = comparer; 43 } 44 45 private ArrayList Items { 46 get { 47 return _items; 48 } 49 } 50 51 private enum InheritedType { 52 inNeither = 0, 53 inParent = 1, 54 inSelf = 2, 55 inBothSame = 3, 56 inBothDiff = 4, 57 inBothCopyNoRemove = 5, 58 } 59 60 protected internal string AddElementName { 61 get { 62 return _addElement; 63 } 64 set { 65 _addElement = value; 66 if (BaseConfigurationRecord.IsReservedAttributeName(value)) { 67 throw new ArgumentException(SR.GetString(SR.Item_name_reserved, DefaultAddItemName, value)); 68 } 69 } 70 } 71 72 protected internal string RemoveElementName { 73 get { 74 return _removeElement; 75 } 76 set { 77 if (BaseConfigurationRecord.IsReservedAttributeName(value)) { 78 throw new ArgumentException(SR.GetString(SR.Item_name_reserved, DefaultRemoveItemName, value)); 79 } 80 _removeElement = value; 81 } 82 } 83 84 protected internal string ClearElementName { 85 get { 86 return _clearElement; 87 } 88 set { 89 if (BaseConfigurationRecord.IsReservedAttributeName(value)) { 90 throw new ArgumentException(SR.GetString(SR.Item_name_reserved, DefaultClearItemsName, value)); 91 } 92 _clearElement = value; 93 } 94 } 95 96 // AssociateContext 97 // 98 // Associate a collection of values with a configRecord 99 // AssociateContext(BaseConfigurationRecord configRecord)100 internal override void AssociateContext(BaseConfigurationRecord configRecord) { 101 base.AssociateContext(configRecord); 102 103 foreach (Entry entry in _items) { 104 if (entry._value != null) { 105 entry._value.AssociateContext(configRecord); 106 } 107 } 108 } 109 IsModified()110 protected internal override bool IsModified() { 111 if (bModified == true) { 112 return true; 113 } 114 115 if (base.IsModified() == true) { 116 return true; 117 } 118 119 foreach (Entry entry in _items) { 120 if (entry._entryType != EntryType.Removed) { 121 ConfigurationElement elem = entry._value; 122 if (elem.IsModified()) { 123 return true; 124 } 125 } 126 } 127 return false; 128 } 129 ResetModified()130 protected internal override void ResetModified() { 131 bModified = false; 132 base.ResetModified(); 133 134 foreach (Entry entry in _items) { 135 if (entry._entryType != EntryType.Removed) { 136 ConfigurationElement elem = entry._value; 137 elem.ResetModified(); 138 } 139 } 140 } 141 IsReadOnly()142 public override bool IsReadOnly() { 143 return bReadOnly; 144 } 145 SetReadOnly()146 protected internal override void SetReadOnly() { 147 bReadOnly = true; 148 foreach (Entry entry in _items) { 149 if (entry._entryType != EntryType.Removed) { 150 ConfigurationElement elem = entry._value; 151 elem.SetReadOnly(); 152 } 153 } 154 } 155 GetEnumeratorImpl()156 internal virtual IEnumerator GetEnumeratorImpl() { 157 return new Enumerator(_items, this); 158 } 159 GetElementsEnumerator()160 internal IEnumerator GetElementsEnumerator() { 161 // Return an enumerator over the collection's config elements. 162 // This is different then the std GetEnumerator because the second one 163 // can return different set of items if overriden in a derived class 164 165 return new Enumerator(_items, this); 166 } 167 Equals(object compareTo)168 public override bool Equals(object compareTo) { 169 if (compareTo.GetType() == this.GetType()) { 170 ConfigurationElementCollection compareToElem = (ConfigurationElementCollection)compareTo; 171 if (this.Count != compareToElem.Count) { 172 return false; 173 } 174 175 foreach (Entry thisEntry in Items) { 176 bool found = false; 177 foreach (Entry compareEntry in compareToElem.Items) { 178 if (Object.Equals(thisEntry._value, compareEntry._value)) { 179 found = true; 180 break; 181 } 182 } 183 if (found == false) { 184 // not in the collection must be different 185 return false; 186 } 187 } 188 return true; 189 } 190 return false; 191 } 192 GetHashCode()193 public override int GetHashCode() { 194 int hHashCode = 0; 195 foreach (Entry thisEntry in Items) { 196 ConfigurationElement elem = thisEntry._value; 197 hHashCode ^= elem.GetHashCode(); 198 } 199 return hHashCode; 200 } 201 202 Unmerge(ConfigurationElement sourceElement, ConfigurationElement parentElement, ConfigurationSaveMode saveMode)203 protected internal override void Unmerge(ConfigurationElement sourceElement, 204 ConfigurationElement parentElement, 205 ConfigurationSaveMode saveMode) { 206 207 base.Unmerge(sourceElement, parentElement, saveMode); 208 if (sourceElement != null) { 209 ConfigurationElementCollection parentCollection = parentElement as ConfigurationElementCollection; 210 ConfigurationElementCollection sourceCollection = sourceElement as ConfigurationElementCollection; 211 Hashtable Inheritance = new Hashtable(); 212 _lockedAllExceptAttributesList = sourceElement._lockedAllExceptAttributesList; 213 _lockedAllExceptElementsList = sourceElement._lockedAllExceptElementsList; 214 _fItemLocked = sourceElement._fItemLocked; 215 _lockedAttributesList = sourceElement._lockedAttributesList; 216 _lockedElementsList = sourceElement._lockedElementsList; 217 218 AssociateContext(sourceElement._configRecord); 219 220 if (parentElement != null) { 221 if (parentElement._lockedAttributesList != null) 222 _lockedAttributesList = UnMergeLockList(sourceElement._lockedAttributesList, 223 parentElement._lockedAttributesList, saveMode); 224 if (parentElement._lockedElementsList != null) 225 _lockedElementsList = UnMergeLockList(sourceElement._lockedElementsList, 226 parentElement._lockedElementsList, saveMode); 227 if (parentElement._lockedAllExceptAttributesList != null) 228 _lockedAllExceptAttributesList = UnMergeLockList(sourceElement._lockedAllExceptAttributesList, 229 parentElement._lockedAllExceptAttributesList, saveMode); 230 if (parentElement._lockedAllExceptElementsList != null) 231 _lockedAllExceptElementsList = UnMergeLockList(sourceElement._lockedAllExceptElementsList, 232 parentElement._lockedAllExceptElementsList, saveMode); 233 } 234 235 if (CollectionType == ConfigurationElementCollectionType.AddRemoveClearMap || 236 CollectionType == ConfigurationElementCollectionType.AddRemoveClearMapAlternate) { 237 // When writing out portable configurations the <clear/> tag should be written 238 bCollectionCleared = sourceCollection.bCollectionCleared; 239 EmitClear = (saveMode == ConfigurationSaveMode.Full && (_clearElement.Length != 0)) || 240 (saveMode == ConfigurationSaveMode.Modified && bCollectionCleared) ? 241 true : sourceCollection.EmitClear; 242 243 if ((parentCollection != null) && (EmitClear != true)) { 244 foreach (Entry entry in parentCollection.Items) { 245 if (entry._entryType != EntryType.Removed) { 246 Inheritance[entry.GetKey(this)] = InheritedType.inParent; 247 } 248 } 249 } 250 251 foreach (Entry entry in sourceCollection.Items) { 252 if (entry._entryType != EntryType.Removed) { 253 if (Inheritance.Contains(entry.GetKey(this))) { 254 Entry parentEntry = (Entry)parentCollection.Items[parentCollection.RealIndexOf(entry._value)]; 255 256 ConfigurationElement elem = entry._value; 257 if (elem.Equals(parentEntry._value)) { 258 // in modified mode we consider any change to be different than the parent 259 Inheritance[entry.GetKey(this)] = InheritedType.inBothSame; 260 if (saveMode == ConfigurationSaveMode.Modified) { 261 if (elem.IsModified()) { 262 Inheritance[entry.GetKey(this)] = InheritedType.inBothDiff; 263 } else 264 if (elem.ElementPresent) { 265 // This is when the source file contained the entry but it was an 266 // exact copy. We don't want to emit a remove so we treat it as 267 // a special case. 268 Inheritance[entry.GetKey(this)] = InheritedType.inBothCopyNoRemove; 269 } 270 } 271 } 272 else { 273 Inheritance[entry.GetKey(this)] = InheritedType.inBothDiff; 274 if (CollectionType == ConfigurationElementCollectionType.AddRemoveClearMapAlternate 275 && entry._entryType == EntryType.Added) { 276 // this is a special case for deailing with defect number 529517 277 // this code allow the config to write out the same xml when no remove was 278 // present during deserialization. 279 Inheritance[entry.GetKey(this)] = InheritedType.inBothCopyNoRemove; 280 } 281 } 282 } 283 else { // not in parent 284 Inheritance[entry.GetKey(this)] = InheritedType.inSelf; 285 } 286 } 287 } 288 289 // 290 if ((parentCollection != null) && (EmitClear != true)) { 291 foreach (Entry entry in parentCollection.Items) { 292 if (entry._entryType != EntryType.Removed) { 293 294 InheritedType tp = (InheritedType)Inheritance[entry.GetKey(this)]; 295 if (tp == InheritedType.inParent || tp == InheritedType.inBothDiff) { 296 ConfigurationElement elem = CallCreateNewElement(entry.GetKey(this).ToString()); 297 298 elem.Reset(entry._value); // copy this entry 299 BaseAdd(elem,ThrowOnDuplicate,true); // Add it (so that is once existed in the temp 300 BaseRemove(entry.GetKey(this), false); // now remove it to for a remove instruction 301 } 302 } 303 } 304 } 305 306 foreach (Entry entry in sourceCollection.Items) { 307 if (entry._entryType != EntryType.Removed) { 308 InheritedType tp = (InheritedType)Inheritance[entry.GetKey(this)]; 309 310 if (tp == InheritedType.inSelf || tp == InheritedType.inBothDiff || 311 tp == InheritedType.inBothCopyNoRemove) { 312 ConfigurationElement elem = CallCreateNewElement(entry.GetKey(this).ToString()); 313 314 elem.Unmerge(entry._value, null, saveMode); 315 316 if (tp == InheritedType.inSelf) { 317 elem.RemoveAllInheritedLocks(); // If the key changed only local locks are kept 318 } 319 320 BaseAdd(elem,ThrowOnDuplicate,true); // Add it 321 } 322 } 323 } 324 } 325 else { 326 if (CollectionType == ConfigurationElementCollectionType.BasicMap || 327 CollectionType == ConfigurationElementCollectionType.BasicMapAlternate) { 328 foreach (Entry entry in sourceCollection.Items) { 329 bool FoundKeyInParent = false; 330 Entry parentEntrySaved = null; 331 332 if (entry._entryType == EntryType.Added || 333 entry._entryType == EntryType.Replaced) { 334 bool InParent = false; 335 336 if (parentCollection != null) { 337 foreach (Entry parentEntry in parentCollection.Items) { 338 if (Object.Equals(entry.GetKey(this), parentEntry.GetKey(this))) { 339 // for basic map collection where the key is actually an element 340 // we do not want the merging behavior or data will not get written 341 // out for the properties if they match the first element deamed to be a parent 342 // For example <allow verbs="NewVerb" users="*"/> will loose the users because 343 // an entry exists in the root element. 344 if (!IsElementName(entry.GetKey(this).ToString())) { 345 // For elements which are not keyed by the element name 346 // need to be unmerged 347 FoundKeyInParent = true; 348 parentEntrySaved = parentEntry; 349 } 350 } 351 352 if (Object.Equals(entry._value, parentEntry._value)) { 353 FoundKeyInParent = true; 354 InParent = true; // in parent and the same exact values 355 parentEntrySaved = parentEntry; 356 break; 357 } 358 } 359 } 360 361 ConfigurationElement elem = CallCreateNewElement(entry.GetKey(this).ToString()); 362 363 if (!FoundKeyInParent) { // Unmerge is similar to a reset when used like this 364 // except that it handles the different update modes 365 // which Reset does not understand 366 elem.Unmerge(entry._value, null, saveMode); // copy this entry 367 BaseAdd(-1, elem,true); // Add it 368 } 369 else { 370 ConfigurationElement sourceItem = entry._value; 371 if (!InParent || 372 (saveMode == ConfigurationSaveMode.Modified && sourceItem.IsModified()) || 373 (saveMode == ConfigurationSaveMode.Full)) { 374 elem.Unmerge(entry._value, parentEntrySaved._value, saveMode); 375 BaseAdd(-1, elem,true); // Add it 376 } 377 } 378 } 379 } 380 } 381 } 382 } 383 } 384 Reset(ConfigurationElement parentElement)385 protected internal override void Reset(ConfigurationElement parentElement) { 386 ConfigurationElementCollection parentCollection = parentElement as ConfigurationElementCollection; 387 ResetLockLists(parentElement); 388 389 if (parentCollection != null) { 390 foreach (Entry entry in parentCollection.Items) { 391 ConfigurationElement elem = CallCreateNewElement(entry.GetKey(this).ToString()); 392 elem.Reset(entry._value); 393 394 if ((CollectionType == ConfigurationElementCollectionType.AddRemoveClearMap || 395 CollectionType == ConfigurationElementCollectionType.AddRemoveClearMapAlternate) && 396 (entry._entryType == EntryType.Added || 397 entry._entryType == EntryType.Replaced)) { // do not add removed items from the parent 398 BaseAdd(elem, true, true); // This version combines dups and throws (unless overridden) 399 } 400 else { 401 if (CollectionType == ConfigurationElementCollectionType.BasicMap || 402 CollectionType == ConfigurationElementCollectionType.BasicMapAlternate) { 403 BaseAdd(-1, elem, true); // this version appends regardless of if it is a dup. 404 } 405 } 406 } 407 _inheritedCount = Count; // After reset the count is the number of items actually inherited. 408 } 409 } 410 411 public int Count { 412 get { 413 return _items.Count - _removedItemCount; 414 } 415 } 416 417 public bool EmitClear { 418 get { 419 return bEmitClearTag; 420 } 421 set { 422 if (IsReadOnly()) { 423 throw new ConfigurationErrorsException(SR.GetString(SR.Config_base_read_only)); 424 } 425 if (value == true) { 426 CheckLockedElement(_clearElement, null); // has clear been locked? 427 CheckLockedElement(_removeElement, null); // has remove been locked? Clear implies remove 428 } 429 bModified = true; 430 bEmitClearTag = value; 431 } 432 } 433 434 public bool IsSynchronized { 435 get { 436 return false; 437 } 438 } 439 440 public Object SyncRoot { 441 get { 442 return null; 443 } 444 } 445 CopyTo(ConfigurationElement[] array, int index)446 public void CopyTo(ConfigurationElement[] array, int index) { 447 ((ICollection)this).CopyTo(array, index); 448 } 449 ICollection.CopyTo(Array arr, int index)450 void ICollection.CopyTo(Array arr, int index) { 451 foreach (Entry entry in _items) { 452 if (entry._entryType != EntryType.Removed) { 453 arr.SetValue(entry._value, index++); 454 } 455 } 456 } 457 GetEnumerator()458 public IEnumerator GetEnumerator() { 459 return GetEnumeratorImpl(); 460 } 461 BaseAdd(ConfigurationElement element)462 protected virtual void BaseAdd(ConfigurationElement element) { 463 BaseAdd(element, ThrowOnDuplicate); 464 } 465 BaseAdd(ConfigurationElement element, bool throwIfExists)466 protected internal void BaseAdd(ConfigurationElement element, bool throwIfExists) { 467 BaseAdd(element, throwIfExists, false); 468 } 469 BaseAdd(ConfigurationElement element, bool throwIfExists, bool ignoreLocks)470 private void BaseAdd(ConfigurationElement element, bool throwIfExists, bool ignoreLocks) { 471 bool flagAsReplaced = false; 472 bool localAddToEnd = internalAddToEnd; 473 474 if (IsReadOnly()) { 475 throw new ConfigurationErrorsException(SR.GetString(SR.Config_base_read_only)); 476 } 477 478 if (LockItem == true && ignoreLocks == false) { 479 throw new ConfigurationErrorsException(SR.GetString(SR.Config_base_element_locked, _addElement)); 480 } 481 482 Object key = GetElementKeyInternal(element); 483 int iFoundItem = -1; 484 for (int index = 0; index < _items.Count; index++) { 485 Entry entry = (Entry)_items[index]; 486 if (CompareKeys(key, entry.GetKey(this))) { 487 if (entry._value != null && entry._value.LockItem == true && ignoreLocks == false) { 488 throw new ConfigurationErrorsException(SR.GetString(SR.Config_base_collection_item_locked)); 489 } 490 if (entry._entryType != EntryType.Removed && throwIfExists) { 491 if (!element.Equals(entry._value)) { 492 throw new ConfigurationErrorsException(SR.GetString(SR.Config_base_collection_entry_already_exists, key), 493 element.PropertyFileName(""), element.PropertyLineNumber("")); 494 } 495 else { 496 entry._value = element; 497 } 498 return; 499 } 500 if (entry._entryType != EntryType.Added) { 501 if ((CollectionType == ConfigurationElementCollectionType.AddRemoveClearMap || 502 CollectionType == ConfigurationElementCollectionType.AddRemoveClearMapAlternate) && 503 entry._entryType == EntryType.Removed && 504 _removedItemCount > 0) { 505 _removedItemCount--; // account for the value 506 } 507 entry._entryType = EntryType.Replaced; 508 flagAsReplaced = true; 509 } 510 if (localAddToEnd || 511 CollectionType == ConfigurationElementCollectionType.AddRemoveClearMapAlternate) { 512 iFoundItem = index; 513 if (entry._entryType == EntryType.Added) { 514 // this is a special case for defect number 529517 to emulate everett behavior 515 localAddToEnd = true; 516 } 517 break; 518 } 519 else { 520 // check to see if the element is trying to set a locked property. 521 if (ignoreLocks == false) { 522 element.HandleLockedAttributes(entry._value); 523 // copy the lock from the removed element before setting the new element 524 element.MergeLocks(entry._value); 525 } 526 entry._value = element; 527 bModified = true; 528 return; 529 } 530 } 531 } 532 533 // Brand new item. 534 if (iFoundItem >= 0) { 535 _items.RemoveAt(iFoundItem); 536 537 // if the item being removed was inherited adjust the cout 538 if (CollectionType == ConfigurationElementCollectionType.AddRemoveClearMapAlternate && 539 iFoundItem > Count + _removedItemCount - _inheritedCount) { 540 _inheritedCount--; 541 } 542 } 543 BaseAddInternal(localAddToEnd ? -1 : iFoundItem, element, flagAsReplaced, ignoreLocks); 544 bModified = true; 545 } 546 BaseIndexOf(ConfigurationElement element)547 protected int BaseIndexOf(ConfigurationElement element) { 548 int index = 0; 549 Object key = GetElementKeyInternal(element); 550 foreach (Entry entry in _items) { 551 if (entry._entryType != EntryType.Removed) { 552 if (CompareKeys(key, entry.GetKey(this))) { 553 return index; 554 } 555 index++; 556 } 557 } 558 return -1; 559 } 560 RealIndexOf(ConfigurationElement element)561 internal int RealIndexOf(ConfigurationElement element) { 562 int index = 0; 563 Object key = GetElementKeyInternal(element); 564 foreach (Entry entry in _items) { 565 if (CompareKeys(key, entry.GetKey(this))) { 566 return index; 567 } 568 index++; 569 } 570 return -1; 571 } 572 BaseAddInternal(int index, ConfigurationElement element, bool flagAsReplaced, bool ignoreLocks)573 private void BaseAddInternal(int index, ConfigurationElement element, bool flagAsReplaced, bool ignoreLocks) { 574 // Allow the element to initialize itself after its 575 // constructor has been run so that it may access 576 // virtual methods. 577 578 element.AssociateContext(_configRecord); 579 if (element != null) { 580 element.CallInit(); 581 } 582 583 if (IsReadOnly()) { 584 throw new ConfigurationErrorsException(SR.GetString(SR.Config_base_read_only)); 585 } 586 587 if (!ignoreLocks) { // during reset we ignore locks so we can copy the elements 588 if(CollectionType == ConfigurationElementCollectionType.BasicMap || 589 CollectionType == ConfigurationElementCollectionType.BasicMapAlternate) { 590 if (BaseConfigurationRecord.IsReservedAttributeName(ElementName)) { 591 throw new ArgumentException(SR.GetString(SR.Basicmap_item_name_reserved, ElementName)); 592 } 593 CheckLockedElement(ElementName, null); 594 } 595 if(CollectionType == ConfigurationElementCollectionType.AddRemoveClearMap || 596 CollectionType == ConfigurationElementCollectionType.AddRemoveClearMapAlternate) { 597 598 CheckLockedElement(_addElement, null); 599 } 600 } 601 602 if (CollectionType == ConfigurationElementCollectionType.BasicMapAlternate || 603 CollectionType == ConfigurationElementCollectionType.AddRemoveClearMapAlternate) { 604 if (index == -1) { 605 index = Count + _removedItemCount - _inheritedCount; // insert before inherited, but after any removed 606 } 607 else { 608 if (index > Count + _removedItemCount - _inheritedCount && flagAsReplaced == false) { 609 throw (new ConfigurationErrorsException(SR.GetString(SR.Config_base_cannot_add_items_below_inherited_items))); 610 } 611 } 612 } 613 614 if (CollectionType == ConfigurationElementCollectionType.BasicMap && 615 index >= 0 && 616 index < _inheritedCount) { 617 throw (new ConfigurationErrorsException(SR.GetString(SR.Config_base_cannot_add_items_above_inherited_items))); 618 } 619 620 EntryType entryType = (flagAsReplaced == false) ? EntryType.Added : EntryType.Replaced; 621 622 Object key = GetElementKeyInternal(element); 623 624 if (index >= 0) { 625 if (index > _items.Count) { 626 throw new ConfigurationErrorsException(SR.GetString(SR.IndexOutOfRange, index)); 627 } 628 _items.Insert(index, new Entry(entryType, key, element)); 629 } 630 else { 631 _items.Add(new Entry(entryType, key, element)); 632 } 633 634 bModified = true; 635 } 636 637 BaseAdd(int index, ConfigurationElement element)638 protected virtual void BaseAdd(int index, ConfigurationElement element) { 639 BaseAdd(index, element, false); 640 } 641 BaseAdd(int index, ConfigurationElement element, bool ignoreLocks)642 private void BaseAdd(int index, ConfigurationElement element, bool ignoreLocks) { 643 if (IsReadOnly()) { 644 throw new ConfigurationErrorsException(SR.GetString(SR.Config_base_read_only)); 645 } 646 if (index < -1) { 647 throw new ConfigurationErrorsException(SR.GetString(SR.IndexOutOfRange, index)); 648 } 649 650 if ((index != -1) && 651 (CollectionType == ConfigurationElementCollectionType.AddRemoveClearMap || 652 CollectionType == ConfigurationElementCollectionType.AddRemoveClearMapAlternate)) { 653 654 // If it's an AddRemoveClearMap*** collection, turn the index passed into into a real internal index 655 int realIndex = 0; 656 657 if (index > 0) { 658 foreach (Entry entryfound in _items) { 659 if (entryfound._entryType != EntryType.Removed) { 660 index--; 661 } 662 if (index == 0) { 663 break; 664 } 665 realIndex++; 666 } 667 index = ++realIndex; 668 } 669 // check for duplicates 670 Object key = GetElementKeyInternal(element); 671 foreach (Entry entry in _items) { 672 if (CompareKeys(key, entry.GetKey(this))) { 673 if (entry._entryType != EntryType.Removed) { 674 if (!element.Equals(entry._value)) { 675 throw new ConfigurationErrorsException(SR.GetString(SR.Config_base_collection_entry_already_exists, key), 676 element.PropertyFileName(""), element.PropertyLineNumber("")); 677 } 678 return; 679 } 680 } 681 } 682 683 } 684 685 BaseAddInternal(index, element, false, ignoreLocks); 686 } 687 BaseRemove(Object key)688 protected internal void BaseRemove(Object key) { 689 BaseRemove(key, false); 690 } 691 BaseRemove(Object key, bool throwIfMissing)692 private void BaseRemove(Object key, bool throwIfMissing) { 693 if (IsReadOnly()) 694 throw new ConfigurationErrorsException(SR.GetString(SR.Config_base_read_only)); 695 696 int index = 0; 697 bool foundEntry = false; 698 foreach (Entry entry in _items) { 699 if (CompareKeys(key, entry.GetKey(this))) { 700 foundEntry = true; 701 702 if (entry._value == null) // A phoney delete is already present 703 { 704 if (throwIfMissing) { 705 throw new ConfigurationErrorsException(SR.GetString(SR.Config_base_collection_entry_not_found, key)); 706 } 707 else { 708 return; 709 } 710 } 711 712 if (entry._value.LockItem == true) { 713 throw new ConfigurationErrorsException(SR.GetString(SR.Config_base_attribute_locked, key)); 714 } 715 716 if (entry._value.ElementPresent == false) { 717 CheckLockedElement(_removeElement, null); // has remove been locked? 718 } 719 720 switch (entry._entryType) { 721 case EntryType.Added: 722 if (CollectionType != ConfigurationElementCollectionType.AddRemoveClearMap && 723 CollectionType != ConfigurationElementCollectionType.AddRemoveClearMapAlternate) { 724 if (CollectionType == ConfigurationElementCollectionType.BasicMapAlternate) { 725 if (index >= Count - _inheritedCount) { 726 throw (new ConfigurationErrorsException(SR.GetString(SR.Config_base_cannot_remove_inherited_items))); 727 } 728 } 729 if (CollectionType == ConfigurationElementCollectionType.BasicMap) { 730 if (index < _inheritedCount) { 731 throw (new ConfigurationErrorsException(SR.GetString(SR.Config_base_cannot_remove_inherited_items))); 732 } 733 } 734 735 _items.RemoveAt(index); 736 } 737 else { 738 // don't really remove it from the collection just mark it removed 739 entry._entryType = EntryType.Removed; 740 _removedItemCount++; 741 } 742 break; 743 case EntryType.Removed: 744 if (throwIfMissing) { 745 throw new ConfigurationErrorsException(SR.GetString(SR.Config_base_collection_entry_already_removed)); 746 } 747 break; 748 default: 749 if (CollectionType != ConfigurationElementCollectionType.AddRemoveClearMap && 750 CollectionType != ConfigurationElementCollectionType.AddRemoveClearMapAlternate) { 751 throw new ConfigurationErrorsException(SR.GetString(SR.Config_base_collection_elements_may_not_be_removed)); 752 } 753 entry._entryType = EntryType.Removed; 754 _removedItemCount++; 755 break; 756 } 757 bModified = true; 758 return; 759 } 760 index++; 761 } 762 // Note because it is possible for removes to get orphaned by the API they will 763 // not cause a throw from the base classes. The scenerio is: 764 // Add an item in a parent level 765 // remove the item in a child level 766 // remove the item at the parent level. 767 // 768 // throw new ConfigurationErrorsException(SR.GetString(SR.Config_base_collection_entry_not_found)); 769 if (foundEntry == false) { 770 if (throwIfMissing) { 771 throw new ConfigurationErrorsException(SR.GetString(SR.Config_base_collection_entry_not_found, key)); 772 } 773 774 if (CollectionType == ConfigurationElementCollectionType.AddRemoveClearMap || 775 CollectionType == ConfigurationElementCollectionType.AddRemoveClearMapAlternate) { 776 if (CollectionType == ConfigurationElementCollectionType.AddRemoveClearMapAlternate) { 777 _items.Insert(Count + _removedItemCount - _inheritedCount, new Entry(EntryType.Removed, key, null)); 778 } 779 else { 780 _items.Add(new Entry(EntryType.Removed, key, null)); 781 } 782 _removedItemCount++; 783 } 784 785 } 786 } 787 BaseGet(Object key)788 protected internal ConfigurationElement BaseGet(Object key) { 789 foreach (Entry entry in _items) { 790 if (entry._entryType != EntryType.Removed) { 791 if (CompareKeys(key, entry.GetKey(this))) { 792 return entry._value; 793 } 794 } 795 } 796 return null; 797 } 798 BaseIsRemoved(Object key)799 protected internal bool BaseIsRemoved(Object key) { 800 foreach (Entry entry in _items) { 801 if (CompareKeys(key, entry.GetKey(this))) { 802 if (entry._entryType == EntryType.Removed) { 803 return true; 804 } 805 else { 806 return false; 807 } 808 } 809 } 810 return false; 811 } 812 BaseGet(int index)813 protected internal ConfigurationElement BaseGet(int index) { 814 if (index < 0) { 815 throw new ConfigurationErrorsException(SR.GetString(SR.IndexOutOfRange, index)); 816 } 817 818 int VirtualIndex = 0; 819 Entry entry = (Entry)null; 820 821 foreach (Entry entryfound in _items) { 822 if (VirtualIndex == index && (entryfound._entryType != EntryType.Removed)) { 823 entry = entryfound; 824 break; 825 } 826 if (entryfound._entryType != EntryType.Removed) { 827 VirtualIndex++; 828 } 829 } 830 831 if (entry != null) { 832 return entry._value; 833 } 834 else { 835 throw new ConfigurationErrorsException(SR.GetString(SR.IndexOutOfRange, index)); 836 } 837 } 838 BaseGetAllKeys()839 protected internal object[] BaseGetAllKeys() { 840 object[] keys = new object[Count]; 841 int index = 0; 842 foreach (Entry entry in _items) { 843 if (entry._entryType != EntryType.Removed) { 844 keys[index] = entry.GetKey(this); 845 index++; 846 } 847 } 848 return keys; 849 } 850 BaseGetKey(int index)851 protected internal object BaseGetKey(int index) { 852 int VirtualIndex = 0; 853 Entry entry = (Entry)null; 854 if (index < 0) { 855 throw new ConfigurationErrorsException(SR.GetString(SR.IndexOutOfRange, index)); 856 } 857 858 foreach (Entry entryfound in _items) { 859 if (VirtualIndex == index && (entryfound._entryType != EntryType.Removed)) { 860 entry = entryfound; 861 break; 862 } 863 864 if (entryfound._entryType != EntryType.Removed) { 865 VirtualIndex++; 866 } 867 } 868 869 // Entry entry = (Entry)_items[index]; 870 if (entry != null) { 871 object key = entry.GetKey(this); 872 873 return key; 874 } 875 else { 876 throw new ConfigurationErrorsException(SR.GetString(SR.IndexOutOfRange, index)); 877 } 878 } 879 BaseClear()880 protected internal void BaseClear() { 881 if (IsReadOnly()) { 882 throw new ConfigurationErrorsException(SR.GetString(SR.Config_base_read_only)); 883 } 884 885 CheckLockedElement(_clearElement, null); // has clear been locked? 886 CheckLockedElement(_removeElement, null); // has remove been locked? Clear implies remove 887 888 bModified = true; 889 bCollectionCleared = true; 890 if ((CollectionType == ConfigurationElementCollectionType.BasicMap || 891 CollectionType == ConfigurationElementCollectionType.BasicMapAlternate) 892 && _inheritedCount > 0) { 893 int RemoveIndex = 0; 894 if (CollectionType == ConfigurationElementCollectionType.BasicMapAlternate) { 895 RemoveIndex = 0; // Inherited items are at the bottom and cannot be removed 896 } 897 if (CollectionType == ConfigurationElementCollectionType.BasicMap) { 898 RemoveIndex = _inheritedCount; // inherited items are at the top and cannot be removed 899 } 900 while (Count - _inheritedCount > 0) { 901 _items.RemoveAt(RemoveIndex); 902 } 903 } 904 else { 905 // do not clear any locked items 906 // _items.Clear(); 907 int inheritedRemoved = 0; 908 int removedRemoved = 0; 909 int initialCount = Count; 910 911 // check for locks before removing any items from the collection 912 for (int CheckIndex = 0; CheckIndex < _items.Count; CheckIndex++) { 913 Entry entry = (Entry)_items[CheckIndex]; 914 if (entry._value != null && entry._value.LockItem == true) { 915 throw new ConfigurationErrorsException(SR.GetString(SR.Config_base_collection_item_locked_cannot_clear)); 916 } 917 } 918 919 for (int RemoveIndex = _items.Count - 1; RemoveIndex >= 0; RemoveIndex--) { 920 Entry entry = (Entry)_items[RemoveIndex]; 921 if ((CollectionType == ConfigurationElementCollectionType.AddRemoveClearMap && 922 RemoveIndex < _inheritedCount) || 923 (CollectionType == ConfigurationElementCollectionType.AddRemoveClearMapAlternate && 924 (RemoveIndex >= initialCount - _inheritedCount))) { 925 inheritedRemoved++; 926 } 927 if (entry._entryType == EntryType.Removed) { 928 removedRemoved++; 929 } 930 931 _items.RemoveAt(RemoveIndex); 932 } 933 _inheritedCount -= inheritedRemoved; 934 _removedItemCount -= removedRemoved; 935 } 936 } 937 BaseRemoveAt(int index)938 protected internal void BaseRemoveAt(int index) { 939 if (IsReadOnly()) { 940 throw new ConfigurationErrorsException(SR.GetString(SR.Config_base_read_only)); 941 } 942 int VirtualIndex = 0; 943 Entry entry = (Entry)null; 944 945 foreach (Entry entryfound in _items) { 946 if (VirtualIndex == index && (entryfound._entryType != EntryType.Removed)) { 947 entry = entryfound; 948 break; 949 } 950 951 if (entryfound._entryType != EntryType.Removed) { 952 VirtualIndex++; 953 } 954 } 955 956 if (entry == null) { 957 throw new ConfigurationErrorsException(SR.GetString(SR.IndexOutOfRange, index)); 958 } 959 else { 960 if (entry._value.LockItem == true) { 961 throw new ConfigurationErrorsException(SR.GetString(SR.Config_base_attribute_locked, entry.GetKey(this))); 962 } 963 964 if (entry._value.ElementPresent == false) { 965 CheckLockedElement(_removeElement, null); // has remove been locked? 966 } 967 968 switch (entry._entryType) { 969 case EntryType.Added: 970 if (CollectionType != ConfigurationElementCollectionType.AddRemoveClearMap && 971 CollectionType != ConfigurationElementCollectionType.AddRemoveClearMapAlternate) { 972 if (CollectionType == ConfigurationElementCollectionType.BasicMapAlternate) { 973 if (index >= Count - _inheritedCount) { 974 throw (new ConfigurationErrorsException(SR.GetString(SR.Config_base_cannot_remove_inherited_items))); 975 } 976 } 977 978 if (CollectionType == ConfigurationElementCollectionType.BasicMap) { 979 if (index < _inheritedCount) { 980 throw (new ConfigurationErrorsException(SR.GetString(SR.Config_base_cannot_remove_inherited_items))); 981 } 982 } 983 984 _items.RemoveAt(index); 985 986 } 987 else { 988 // don't really remove it from the collection just mark it removed 989 if (entry._value.ElementPresent == false) { 990 CheckLockedElement(_removeElement, null); // has remove been locked? 991 } 992 993 entry._entryType = EntryType.Removed; 994 _removedItemCount++; 995 } 996 997 break; 998 999 case EntryType.Removed: 1000 throw new ConfigurationErrorsException(SR.GetString(SR.Config_base_collection_entry_already_removed)); 1001 1002 default: 1003 if (CollectionType != ConfigurationElementCollectionType.AddRemoveClearMap && 1004 CollectionType != ConfigurationElementCollectionType.AddRemoveClearMapAlternate) { 1005 throw new ConfigurationErrorsException(SR.GetString(SR.Config_base_collection_elements_may_not_be_removed)); 1006 } 1007 1008 entry._entryType = EntryType.Removed; 1009 _removedItemCount++; 1010 break; 1011 } 1012 bModified = true; 1013 } 1014 } 1015 1016 1017 SerializeElement(XmlWriter writer, bool serializeCollectionKey)1018 protected internal override bool SerializeElement(XmlWriter writer, bool serializeCollectionKey) { 1019 ConfigurationElementCollectionType type = CollectionType; 1020 bool DataToWrite = false; 1021 1022 DataToWrite |= base.SerializeElement(writer, serializeCollectionKey); 1023 1024 if (type == ConfigurationElementCollectionType.AddRemoveClearMap || 1025 type == ConfigurationElementCollectionType.AddRemoveClearMapAlternate) { 1026 // it is possible that the collection only has to be cleared and contains 1027 // no real elements 1028 if (bEmitClearTag == true && (_clearElement.Length != 0)) { 1029 if (writer != null) { 1030 writer.WriteStartElement(_clearElement); 1031 writer.WriteEndElement(); 1032 } 1033 DataToWrite = true; 1034 } 1035 } 1036 1037 foreach (Entry entry in _items) { 1038 if (type == ConfigurationElementCollectionType.BasicMap || 1039 type == ConfigurationElementCollectionType.BasicMapAlternate) { 1040 if (entry._entryType == EntryType.Added || entry._entryType == EntryType.Replaced) { 1041 if (ElementName != null && ElementName.Length != 0) { 1042 if (BaseConfigurationRecord.IsReservedAttributeName(ElementName)) { 1043 throw new ArgumentException(SR.GetString(SR.Basicmap_item_name_reserved, ElementName)); 1044 } 1045 DataToWrite |= entry._value.SerializeToXmlElement(writer, ElementName); 1046 } 1047 else { 1048 DataToWrite |= entry._value.SerializeElement(writer, false); 1049 } 1050 } 1051 } 1052 else if (type == ConfigurationElementCollectionType.AddRemoveClearMap || 1053 type == ConfigurationElementCollectionType.AddRemoveClearMapAlternate) { 1054 if ((entry._entryType == EntryType.Removed || 1055 entry._entryType == EntryType.Replaced) && 1056 entry._value != null) { 1057 1058 if (writer != null) { 1059 writer.WriteStartElement(_removeElement); 1060 } 1061 1062 DataToWrite |= entry._value.SerializeElement(writer, true); 1063 1064 if (writer != null) { 1065 writer.WriteEndElement(); 1066 } 1067 1068 DataToWrite = true; 1069 } 1070 if (entry._entryType == EntryType.Added || entry._entryType == EntryType.Replaced) { 1071 DataToWrite |= entry._value.SerializeToXmlElement(writer, _addElement); 1072 } 1073 } 1074 } 1075 return DataToWrite; 1076 } 1077 OnDeserializeUnrecognizedElement(String elementName, XmlReader reader)1078 protected override bool OnDeserializeUnrecognizedElement(String elementName, XmlReader reader) { 1079 bool handled = false; // 1080 if (CollectionType == ConfigurationElementCollectionType.AddRemoveClearMap || 1081 CollectionType == ConfigurationElementCollectionType.AddRemoveClearMapAlternate) { 1082 if (elementName == _addElement) { 1083 ConfigurationElement elem = CallCreateNewElement(); 1084 elem.ResetLockLists(this); 1085 elem.DeserializeElement(reader, false); 1086 BaseAdd(elem); 1087 handled = true; 1088 } 1089 else if (elementName == _removeElement) { 1090 ConfigurationElement elem = CallCreateNewElement(); 1091 elem.ResetLockLists(this); 1092 elem.DeserializeElement(reader, true); 1093 if (IsElementRemovable(elem) == true) { 1094 BaseRemove(GetElementKeyInternal(elem), false); 1095 } 1096 1097 handled = true; 1098 } 1099 else if (elementName == _clearElement) { 1100 if (reader.AttributeCount > 0) { 1101 while (reader.MoveToNextAttribute()) { 1102 String propertyName = reader.Name; 1103 throw new ConfigurationErrorsException(SR.GetString(SR.Config_base_unrecognized_attribute, propertyName), reader); 1104 } 1105 } 1106 CheckLockedElement(elementName, reader); 1107 reader.MoveToElement(); 1108 BaseClear(); // 1109 bEmitClearTag = true; 1110 handled = true; 1111 } 1112 } 1113 else if (elementName == ElementName) { 1114 if (BaseConfigurationRecord.IsReservedAttributeName(elementName)) { 1115 throw new ArgumentException(SR.GetString(SR.Basicmap_item_name_reserved, elementName)); 1116 } 1117 ConfigurationElement elem = CallCreateNewElement(); 1118 elem.ResetLockLists(this); 1119 elem.DeserializeElement(reader, false); 1120 BaseAdd(elem); 1121 1122 handled = true; 1123 } 1124 else if (IsElementName(elementName)) { // this section handle the collection like the allow deny senario which 1125 if (BaseConfigurationRecord.IsReservedAttributeName(elementName)) { 1126 throw new ArgumentException(SR.GetString(SR.Basicmap_item_name_reserved, elementName)); 1127 } 1128 // have multiple tags for the collection 1129 ConfigurationElement elem = CallCreateNewElement(elementName); 1130 elem.ResetLockLists(this); 1131 elem.DeserializeElement(reader, false); 1132 BaseAdd(-1, elem); 1133 handled = true; 1134 } 1135 return handled; 1136 } 1137 CallCreateNewElement(string elementName)1138 private ConfigurationElement CallCreateNewElement(string elementName) { 1139 ConfigurationElement elem = CreateNewElement(elementName); 1140 elem.AssociateContext(_configRecord); 1141 elem.CallInit(); 1142 return elem; 1143 } 1144 CallCreateNewElement()1145 private ConfigurationElement CallCreateNewElement() { 1146 ConfigurationElement elem = CreateNewElement(); 1147 elem.AssociateContext(_configRecord); 1148 elem.CallInit(); 1149 return elem; 1150 } 1151 CreateNewElement(string elementName)1152 protected virtual ConfigurationElement CreateNewElement(string elementName) { 1153 return CreateNewElement(); 1154 } CreateNewElement()1155 protected abstract ConfigurationElement CreateNewElement(); GetElementKey(ConfigurationElement element)1156 protected abstract Object GetElementKey(ConfigurationElement element); GetElementKeyInternal(ConfigurationElement element)1157 internal Object GetElementKeyInternal(ConfigurationElement element) { 1158 Object key = GetElementKey(element); 1159 if (key == null) 1160 throw new ConfigurationErrorsException(SR.GetString(SR.Config_base_invalid_element_key)); 1161 return key; 1162 } 1163 IsElementRemovable(ConfigurationElement element)1164 protected virtual bool IsElementRemovable(ConfigurationElement element) { 1165 return true; 1166 } 1167 CompareKeys(Object key1, Object key2)1168 private bool CompareKeys(Object key1, Object key2) { 1169 if (_comparer != null) { 1170 return (_comparer.Compare(key1, key2) == 0); 1171 } 1172 else { 1173 return key1.Equals(key2); 1174 } 1175 } 1176 1177 protected virtual String ElementName { 1178 get { 1179 return ""; 1180 } 1181 } 1182 IsElementName(string elementName)1183 protected virtual bool IsElementName(string elementName) { 1184 return false; 1185 } 1186 IsLockableElement(string elementName)1187 internal bool IsLockableElement(string elementName) { 1188 if (CollectionType == ConfigurationElementCollectionType.AddRemoveClearMap || 1189 CollectionType == ConfigurationElementCollectionType.AddRemoveClearMapAlternate) { 1190 return (elementName == AddElementName || 1191 elementName == RemoveElementName || 1192 elementName == ClearElementName); 1193 } 1194 else { 1195 return (elementName == ElementName) || IsElementName(elementName); 1196 } 1197 } 1198 1199 internal string LockableElements { 1200 get { 1201 if (CollectionType == ConfigurationElementCollectionType.AddRemoveClearMap || 1202 CollectionType == ConfigurationElementCollectionType.AddRemoveClearMapAlternate) { 1203 1204 string ElementNames = "'" + AddElementName + "'"; // Must have an add 1205 if (RemoveElementName.Length != 0) 1206 ElementNames += ", '" + RemoveElementName + "'"; 1207 if (ClearElementName.Length != 0) 1208 ElementNames += ", '" + ClearElementName + "'"; 1209 return ElementNames; 1210 } 1211 else { 1212 if (!String.IsNullOrEmpty(ElementName)) { 1213 return "'" + ElementName + "'"; 1214 } 1215 return String.Empty; 1216 } 1217 } 1218 } 1219 1220 protected virtual bool ThrowOnDuplicate { 1221 get { 1222 if (CollectionType == ConfigurationElementCollectionType.AddRemoveClearMap || 1223 CollectionType == ConfigurationElementCollectionType.AddRemoveClearMapAlternate) { 1224 return true; 1225 } 1226 return false; 1227 } 1228 } 1229 1230 public virtual ConfigurationElementCollectionType CollectionType { 1231 get { 1232 return ConfigurationElementCollectionType.AddRemoveClearMap; 1233 } 1234 } 1235 1236 private enum EntryType { 1237 Inherited, 1238 Replaced, 1239 Removed, 1240 Added, 1241 } 1242 1243 private class Entry { 1244 1245 internal EntryType _entryType; 1246 internal Object _key; 1247 internal ConfigurationElement _value; 1248 GetKey(ConfigurationElementCollection ThisCollection)1249 internal Object GetKey(ConfigurationElementCollection ThisCollection) { 1250 // For items that have been really inserted... 1251 if (_value != null) { 1252 return ThisCollection.GetElementKeyInternal(_value); 1253 } 1254 else { 1255 return _key; // These are items that only have been removed 1256 } 1257 1258 } 1259 Entry(EntryType type, Object key, ConfigurationElement value)1260 internal Entry(EntryType type, Object key, ConfigurationElement value) { 1261 _entryType = type; 1262 _key = key; 1263 _value = value; 1264 } 1265 } 1266 1267 private class Enumerator : IDictionaryEnumerator { 1268 1269 private IEnumerator _itemsEnumerator; 1270 private DictionaryEntry _current = new DictionaryEntry(); 1271 private ConfigurationElementCollection ThisCollection; 1272 1273 Enumerator(ArrayList items, ConfigurationElementCollection collection)1274 internal Enumerator(ArrayList items, ConfigurationElementCollection collection) { 1275 _itemsEnumerator = items.GetEnumerator(); 1276 ThisCollection = collection; 1277 } 1278 IEnumerator.MoveNext()1279 bool IEnumerator.MoveNext() { 1280 while (_itemsEnumerator.MoveNext()) { 1281 Entry entry = (Entry)_itemsEnumerator.Current; 1282 if (entry._entryType != EntryType.Removed) { 1283 _current.Key = (entry.GetKey(ThisCollection) != null) ? entry.GetKey(ThisCollection) : "key"; 1284 _current.Value = entry._value; 1285 return true; 1286 } 1287 } 1288 return false; 1289 } 1290 IEnumerator.Reset()1291 void IEnumerator.Reset() { 1292 _itemsEnumerator.Reset(); 1293 } 1294 1295 Object IEnumerator.Current { 1296 get { 1297 return _current.Value; 1298 } 1299 } 1300 1301 DictionaryEntry IDictionaryEnumerator.Entry { 1302 get { 1303 return _current; 1304 } 1305 } 1306 1307 Object IDictionaryEnumerator.Key { 1308 get { 1309 return _current.Key; 1310 } 1311 } 1312 1313 Object IDictionaryEnumerator.Value { 1314 get { 1315 return _current.Value; 1316 } 1317 } 1318 } 1319 } 1320 } 1321