| File: FrameworkFork\Microsoft.Xml\Xml\Dom\XmlAttributeCollection.cs | Web Access |
| Project: src\src\dotnet-svcutil\lib\src\dotnet-svcutil-lib.csproj (dotnet-svcutil-lib) |
// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. namespace Microsoft.Xml { using System; using System.Collections; using System.Diagnostics; // Represents a collection of attributes that can be accessed by name or index. public sealed class XmlAttributeCollection : XmlNamedNodeMap, ICollection { internal XmlAttributeCollection(XmlNode parent) : base(parent) { } // Gets the attribute with the specified index. [System.Runtime.CompilerServices.IndexerName("ItemOf")] public XmlAttribute this[int i] { get { try { return (XmlAttribute)nodes[i]; } catch (ArgumentOutOfRangeException) { throw new IndexOutOfRangeException(ResXml.Xdom_IndexOutOfRange); } } } // Gets the attribute with the specified name. [System.Runtime.CompilerServices.IndexerName("ItemOf")] public XmlAttribute this[string name] { get { int hash = XmlName.GetHashCode(name); for (int i = 0; i < nodes.Count; i++) { XmlAttribute node = (XmlAttribute)nodes[i]; if (hash == node.LocalNameHash && name == node.Name) { return node; } } return null; } } // Gets the attribute with the specified LocalName and NamespaceUri. [System.Runtime.CompilerServices.IndexerName("ItemOf")] public XmlAttribute this[string localName, string namespaceURI] { get { int hash = XmlName.GetHashCode(localName); for (int i = 0; i < nodes.Count; i++) { XmlAttribute node = (XmlAttribute)nodes[i]; if (hash == node.LocalNameHash && localName == node.LocalName && namespaceURI == node.NamespaceURI) { return node; } } return null; } } internal int FindNodeOffset(XmlAttribute node) { for (int i = 0; i < nodes.Count; i++) { XmlAttribute tmp = (XmlAttribute)nodes[i]; if (tmp.LocalNameHash == node.LocalNameHash && tmp.Name == node.Name && tmp.NamespaceURI == node.NamespaceURI) { return i; } } return -1; } internal int FindNodeOffsetNS(XmlAttribute node) { for (int i = 0; i < nodes.Count; i++) { XmlAttribute tmp = (XmlAttribute)nodes[i]; if (tmp.LocalNameHash == node.LocalNameHash && tmp.LocalName == node.LocalName && tmp.NamespaceURI == node.NamespaceURI) { return i; } } return -1; } // Adds a XmlNode using its Name property public override XmlNode SetNamedItem(XmlNode node) { if (node != null && !(node is XmlAttribute)) throw new ArgumentException(ResXml.Xdom_AttrCol_Object); int offset = FindNodeOffset(node.LocalName, node.NamespaceURI); if (offset == -1) { return InternalAppendAttribute((XmlAttribute)node); } else { XmlNode oldNode = base.RemoveNodeAt(offset); InsertNodeAt(offset, node); return oldNode; } } // Inserts the specified node as the first node in the collection. public XmlAttribute Prepend(XmlAttribute node) { if (node.OwnerDocument != null && node.OwnerDocument != parent.OwnerDocument) throw new ArgumentException(ResXml.Xdom_NamedNode_Context); if (node.OwnerElement != null) Detach(node); RemoveDuplicateAttribute(node); InsertNodeAt(0, node); return node; } // Inserts the specified node as the last node in the collection. public XmlAttribute Append(XmlAttribute node) { XmlDocument doc = node.OwnerDocument; if (doc == null || doc.IsLoading == false) { if (doc != null && doc != parent.OwnerDocument) { throw new ArgumentException(ResXml.Xdom_NamedNode_Context); } if (node.OwnerElement != null) { Detach(node); } AddNode(node); } else { base.AddNodeForLoad(node, doc); InsertParentIntoElementIdAttrMap(node); } return node; } // Inserts the specified attribute immediately before the specified reference attribute. public XmlAttribute InsertBefore(XmlAttribute newNode, XmlAttribute refNode) { if (newNode == refNode) return newNode; if (refNode == null) return Append(newNode); if (refNode.OwnerElement != parent) throw new ArgumentException(ResXml.Xdom_AttrCol_Insert); if (newNode.OwnerDocument != null && newNode.OwnerDocument != parent.OwnerDocument) throw new ArgumentException(ResXml.Xdom_NamedNode_Context); if (newNode.OwnerElement != null) Detach(newNode); int offset = FindNodeOffset(refNode.LocalName, refNode.NamespaceURI); Debug.Assert(offset != -1); // the if statement above guarantees that the ref node is in the collection int dupoff = RemoveDuplicateAttribute(newNode); if (dupoff >= 0 && dupoff < offset) offset--; InsertNodeAt(offset, newNode); return newNode; } // Inserts the specified attribute immediately after the specified reference attribute. public XmlAttribute InsertAfter(XmlAttribute newNode, XmlAttribute refNode) { if (newNode == refNode) return newNode; if (refNode == null) return Prepend(newNode); if (refNode.OwnerElement != parent) throw new ArgumentException(ResXml.Xdom_AttrCol_Insert); if (newNode.OwnerDocument != null && newNode.OwnerDocument != parent.OwnerDocument) throw new ArgumentException(ResXml.Xdom_NamedNode_Context); if (newNode.OwnerElement != null) Detach(newNode); int offset = FindNodeOffset(refNode.LocalName, refNode.NamespaceURI); Debug.Assert(offset != -1); // the if statement above guarantees that the ref node is in the collection int dupoff = RemoveDuplicateAttribute(newNode); if (dupoff >= 0 && dupoff < offset) offset--; InsertNodeAt(offset + 1, newNode); return newNode; } // Removes the specified attribute node from the map. public XmlAttribute Remove(XmlAttribute node) { int cNodes = nodes.Count; for (int offset = 0; offset < cNodes; offset++) { if (nodes[offset] == node) { RemoveNodeAt(offset); return node; } } return null; } // Removes the attribute node with the specified index from the map. public XmlAttribute RemoveAt(int i) { if (i < 0 || i >= Count) return null; return (XmlAttribute)RemoveNodeAt(i); } // Removes all attributes from the map. public void RemoveAll() { int n = Count; while (n > 0) { n--; RemoveAt(n); } } void ICollection.CopyTo(Array array, int index) { for (int i = 0, max = Count; i < max; i++, index++) array.SetValue(nodes[i], index); } bool ICollection.IsSynchronized { get { return false; } } object ICollection.SyncRoot { get { return this; } } int ICollection.Count { get { return base.Count; } } public void CopyTo(XmlAttribute[] array, int index) { for (int i = 0, max = Count; i < max; i++, index++) array[index] = (XmlAttribute)(((XmlNode)nodes[i]).CloneNode(true)); } internal override XmlNode AddNode(XmlNode node) { //should be sure by now that the node doesn't have the same name with an existing node in the collection RemoveDuplicateAttribute((XmlAttribute)node); XmlNode retNode = base.AddNode(node); Debug.Assert(retNode is XmlAttribute); InsertParentIntoElementIdAttrMap((XmlAttribute)node); return retNode; } internal override XmlNode InsertNodeAt(int i, XmlNode node) { XmlNode retNode = base.InsertNodeAt(i, node); InsertParentIntoElementIdAttrMap((XmlAttribute)node); return retNode; } internal override XmlNode RemoveNodeAt(int i) { //remove the node without checking replacement XmlNode retNode = base.RemoveNodeAt(i); Debug.Assert(retNode is XmlAttribute); RemoveParentFromElementIdAttrMap((XmlAttribute)retNode); // after remove the attribute, we need to check if a default attribute node should be created and inserted into the tree XmlAttribute defattr = parent.OwnerDocument.GetDefaultAttribute((XmlElement)parent, retNode.Prefix, retNode.LocalName, retNode.NamespaceURI); if (defattr != null) InsertNodeAt(i, defattr); return retNode; } internal void Detach(XmlAttribute attr) { attr.OwnerElement.Attributes.Remove(attr); } //insert the parent element node into the map internal void InsertParentIntoElementIdAttrMap(XmlAttribute attr) { XmlElement parentElem = parent as XmlElement; if (parentElem != null) { if (parent.OwnerDocument == null) return; XmlName attrname = parent.OwnerDocument.GetIDInfoByElement(parentElem.XmlName); if (attrname != null && attrname.Prefix == attr.XmlName.Prefix && attrname.LocalName == attr.XmlName.LocalName) { parent.OwnerDocument.AddElementWithId(attr.Value, parentElem); //add the element into the hashtable } } } //remove the parent element node from the map when the ID attribute is removed internal void RemoveParentFromElementIdAttrMap(XmlAttribute attr) { XmlElement parentElem = parent as XmlElement; if (parentElem != null) { if (parent.OwnerDocument == null) return; XmlName attrname = parent.OwnerDocument.GetIDInfoByElement(parentElem.XmlName); if (attrname != null && attrname.Prefix == attr.XmlName.Prefix && attrname.LocalName == attr.XmlName.LocalName) { parent.OwnerDocument.RemoveElementWithId(attr.Value, parentElem); //remove the element from the hashtable } } } //the function checks if there is already node with the same name existing in the collection // if so, remove it because the new one will be inserted to replace this one (could be in different position though ) // by the calling function later internal int RemoveDuplicateAttribute(XmlAttribute attr) { int ind = FindNodeOffset(attr.LocalName, attr.NamespaceURI); if (ind != -1) { XmlAttribute at = (XmlAttribute)nodes[ind]; base.RemoveNodeAt(ind); RemoveParentFromElementIdAttrMap(at); } return ind; } internal bool PrepareParentInElementIdAttrMap(string attrPrefix, string attrLocalName) { XmlElement parentElem = parent as XmlElement; Debug.Assert(parentElem != null); XmlDocument doc = parent.OwnerDocument; Debug.Assert(doc != null); //The returned attrname if not null is the name with namespaceURI being set to string.Empty //Because DTD doesn't support namespaceURI so all comparisons are based on no namespaceURI (string.Empty); XmlName attrname = doc.GetIDInfoByElement(parentElem.XmlName); if (attrname != null && attrname.Prefix == attrPrefix && attrname.LocalName == attrLocalName) { return true; } return false; } internal void ResetParentInElementIdAttrMap(string oldVal, string newVal) { XmlElement parentElem = parent as XmlElement; Debug.Assert(parentElem != null); XmlDocument doc = parent.OwnerDocument; Debug.Assert(doc != null); doc.RemoveElementWithId(oldVal, parentElem); //add the element into the hashtable doc.AddElementWithId(newVal, parentElem); } // WARNING: // For performance reasons, this function does not check // for xml attributes within the collection with the same full name. // This means that any caller of this function must be sure that // a duplicate attribute does not exist. internal XmlAttribute InternalAppendAttribute(XmlAttribute node) { // a duplicate node better not exist Debug.Assert(-1 == FindNodeOffset(node)); XmlNode retNode = base.AddNode(node); Debug.Assert(retNode is XmlAttribute); InsertParentIntoElementIdAttrMap((XmlAttribute)node); return (XmlAttribute)retNode; } } }