// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt) // This code is distributed under the GNU LGPL (for details please see \doc\license.txt) using System; using System.Collections.Generic; namespace ICSharpCode.AvalonEdit.Xml { /// /// Specailized attribute collection with attribute name caching /// public class AXmlAttributeCollection: FilteredCollection> { /// Empty unbound collection [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Security", "CA2104:DoNotDeclareReadOnlyMutableReferenceTypes", Justification = "InsertItem prevents modifying the Empty collection")] public static readonly AXmlAttributeCollection Empty = new AXmlAttributeCollection(); /// Create unbound collection protected AXmlAttributeCollection() {} /// Wrap the given collection. Non-attributes are filtered public AXmlAttributeCollection(AXmlObjectCollection source): base(source) {} /// Wrap the given collection. Non-attributes are filtered. Items not matching the condition are filtered. public AXmlAttributeCollection(AXmlObjectCollection source, Predicate condition): base(source, condition) {} Dictionary> hashtable = new Dictionary>(); void AddToHashtable(AXmlAttribute attr) { string localName = attr.LocalName; if (!hashtable.ContainsKey(localName)) { hashtable[localName] = new List(1); } hashtable[localName].Add(attr); } void RemoveFromHashtable(AXmlAttribute attr) { string localName = attr.LocalName; hashtable[localName].Remove(attr); } static List NoAttributes = new List(); /// /// Get all attributes with given local name. /// Hash table is used for lookup so this is cheap. /// public IEnumerable GetByLocalName(string localName) { if (hashtable.ContainsKey(localName)) { return hashtable[localName]; } else { return NoAttributes; } } /// protected override void ClearItems() { foreach(AXmlAttribute item in this) { RemoveFromHashtable(item); item.Changing -= item_Changing; item.Changed -= item_Changed; } base.ClearItems(); } /// protected override void InsertItem(int index, AXmlAttribute item) { // prevent insertions into the static 'Empty' instance if (this == Empty) throw new NotSupportedException("Cannot insert into AXmlAttributeCollection.Empty"); AddToHashtable(item); item.Changing += item_Changing; item.Changed += item_Changed; base.InsertItem(index, item); } /// protected override void RemoveItem(int index) { RemoveFromHashtable(this[index]); this[index].Changing -= item_Changing; this[index].Changed -= item_Changed; base.RemoveItem(index); } /// protected override void SetItem(int index, AXmlAttribute item) { RemoveFromHashtable(this[index]); this[index].Changing -= item_Changing; this[index].Changed -= item_Changed; AddToHashtable(item); item.Changing += item_Changing; item.Changed += item_Changed; base.SetItem(index, item); } // Every item in the collection should be registered to these handlers // so that we can handle renames void item_Changing(object sender, AXmlObjectEventArgs e) { RemoveFromHashtable((AXmlAttribute)e.Object); } void item_Changed(object sender, AXmlObjectEventArgs e) { AddToHashtable((AXmlAttribute)e.Object); } } }