The Labs \ Source Viewer \ SSCLI \ System.Xml \ XmlAttributeCollection

  1. //------------------------------------------------------------------------------
  2. // <copyright file="XmlAttributeCollection.cs" company="Microsoft">
  3. //
  4. // Copyright (c) 2006 Microsoft Corporation. All rights reserved.
  5. //
  6. // The use and distribution terms for this software are contained in the file
  7. // named license.txt, which can be found in the root of this distribution.
  8. // By using this software in any fashion, you are agreeing to be bound by the
  9. // terms of this license.
  10. //
  11. // You must not remove this notice, or any other, from this software.
  12. //
  13. // </copyright>
  14. //------------------------------------------------------------------------------
  15. namespace System.Xml
  16. {
  17.     using System;
  18.     using System.Collections;
  19.     using System.Diagnostics;
  20.    
  21.     // Represents a collection of attributes that can be accessed by name or index.
  22.     public sealed class XmlAttributeCollection : XmlNamedNodeMap, ICollection
  23.     {
  24.         internal XmlAttributeCollection(XmlNode parent) : base(parent)
  25.         {
  26.         }
  27.        
  28.         // Gets the attribute with the specified index.
  29.         [System.Runtime.CompilerServices.IndexerName("ItemOf")]
  30.         public XmlAttribute this[int i]
  31.         {
  32.             get {
  33.                 try {
  34.                     return (XmlAttribute)Nodes[i];
  35.                 }
  36.                 catch (ArgumentOutOfRangeException) {
  37.                     throw new IndexOutOfRangeException(Res.GetString(Res.Xdom_IndexOutOfRange));
  38.                 }
  39.             }
  40.         }
  41.        
  42.         // Gets the attribute with the specified name.
  43.         [System.Runtime.CompilerServices.IndexerName("ItemOf")]
  44.         public XmlAttribute this[string name]
  45.         {
  46.             get {
  47.                 ArrayList nodes = this.Nodes;
  48.                 int hash = XmlName.GetHashCode(name);
  49.                
  50.                 for (int i = 0; i < nodes.Count; i++) {
  51.                     XmlAttribute node = (XmlAttribute)nodes[i];
  52.                    
  53.                     if (hash == node.LocalNameHash && name == node.Name) {
  54.                         return node;
  55.                     }
  56.                 }
  57.                
  58.                 return null;
  59.             }
  60.         }
  61.        
  62.         // Gets the attribute with the specified LocalName and NamespaceUri.
  63.         [System.Runtime.CompilerServices.IndexerName("ItemOf")]
  64.         public XmlAttribute this[string localName, string namespaceURI]
  65.         {
  66.             get {
  67.                 ArrayList nodes = this.Nodes;
  68.                 int hash = XmlName.GetHashCode(localName);
  69.                
  70.                 for (int i = 0; i < nodes.Count; i++) {
  71.                     XmlAttribute node = (XmlAttribute)nodes[i];
  72.                    
  73.                     if (hash == node.LocalNameHash && localName == node.LocalName && namespaceURI == node.NamespaceURI) {
  74.                         return node;
  75.                     }
  76.                 }
  77.                
  78.                 return null;
  79.             }
  80.         }
  81.        
  82.         internal int FindNodeOffset(XmlAttribute node)
  83.         {
  84.             ArrayList nodes = this.Nodes;
  85.             for (int i = 0; i < nodes.Count; i++) {
  86.                 XmlAttribute tmp = (XmlAttribute)nodes[i];
  87.                
  88.                 if (tmp.LocalNameHash == node.LocalNameHash && tmp.Name == node.Name && tmp.NamespaceURI == node.NamespaceURI) {
  89.                     return i;
  90.                 }
  91.             }
  92.             return -1;
  93.         }
  94.        
  95.         internal int FindNodeOffsetNS(XmlAttribute node)
  96.         {
  97.             ArrayList nodes = Nodes;
  98.             for (int i = 0; i < nodes.Count; i++) {
  99.                 XmlAttribute tmp = (XmlAttribute)nodes[i];
  100.                 if (tmp.LocalNameHash == node.LocalNameHash && tmp.LocalName == node.LocalName && tmp.NamespaceURI == node.NamespaceURI) {
  101.                     return i;
  102.                 }
  103.             }
  104.             return -1;
  105.         }
  106.        
  107.         // Adds a XmlNode using its Name property
  108.         public override XmlNode SetNamedItem(XmlNode node)
  109.         {
  110.             if (node != null && !(node is XmlAttribute))
  111.                 throw new ArgumentException(Res.GetString(Res.Xdom_AttrCol_Object));
  112.            
  113.             int offset = FindNodeOffset(node.LocalName, node.NamespaceURI);
  114.             if (offset == -1) {
  115.                 return InternalAppendAttribute((XmlAttribute)node);
  116.             }
  117.             else {
  118.                 XmlNode oldNode = base.RemoveNodeAt(offset);
  119.                 InsertNodeAt(offset, node);
  120.                 return oldNode;
  121.             }
  122.         }
  123.        
  124.         // Inserts the specified node as the first node in the collection.
  125.         public XmlAttribute Prepend(XmlAttribute node)
  126.         {
  127.             if (node.OwnerDocument != null && node.OwnerDocument != parent.OwnerDocument)
  128.                 throw new ArgumentException(Res.GetString(Res.Xdom_NamedNode_Context));
  129.            
  130.             if (node.OwnerElement != null)
  131.                 Detach(node);
  132.            
  133.             RemoveDuplicateAttribute(node);
  134.            
  135.             InsertNodeAt(0, node);
  136.             return node;
  137.         }
  138.        
  139.         // Inserts the specified node as the last node in the collection.
  140.         public XmlAttribute Append(XmlAttribute node)
  141.         {
  142.             XmlDocument doc = node.OwnerDocument;
  143.             if (doc == null || doc.IsLoading == false) {
  144.                 if (doc != null && doc != parent.OwnerDocument) {
  145.                     throw new ArgumentException(Res.GetString(Res.Xdom_NamedNode_Context));
  146.                 }
  147.                 if (node.OwnerElement != null) {
  148.                     Detach(node);
  149.                 }
  150.                 AddNode(node);
  151.             }
  152.             else {
  153.                 base.AddNodeForLoad(node, doc);
  154.                 InsertParentIntoElementIdAttrMap(node);
  155.             }
  156.             return node;
  157.         }
  158.        
  159.         // Inserts the specified attribute immediately before the specified reference attribute.
  160.         public XmlAttribute InsertBefore(XmlAttribute newNode, XmlAttribute refNode)
  161.         {
  162.             if (newNode == refNode)
  163.                 return newNode;
  164.            
  165.             if (refNode == null)
  166.                 return Append(newNode);
  167.            
  168.             if (refNode.OwnerElement != parent)
  169.                 throw new ArgumentException(Res.GetString(Res.Xdom_AttrCol_Insert));
  170.            
  171.             if (newNode.OwnerDocument != null && newNode.OwnerDocument != parent.OwnerDocument)
  172.                 throw new ArgumentException(Res.GetString(Res.Xdom_NamedNode_Context));
  173.            
  174.             if (newNode.OwnerElement != null)
  175.                 Detach(newNode);
  176.            
  177.             int offset = FindNodeOffset(refNode.LocalName, refNode.NamespaceURI);
  178.             Debug.Assert(offset != -1);
  179.             // the if statement above guarantees that the ref node is in the collection
  180.             int dupoff = RemoveDuplicateAttribute(newNode);
  181.             if (dupoff >= 0 && dupoff < offset)
  182.                 offset--;
  183.             InsertNodeAt(offset, newNode);
  184.            
  185.             return newNode;
  186.         }
  187.        
  188.         // Inserts the specified attribute immediately after the specified reference attribute.
  189.         public XmlAttribute InsertAfter(XmlAttribute newNode, XmlAttribute refNode)
  190.         {
  191.             if (newNode == refNode)
  192.                 return newNode;
  193.            
  194.             if (refNode == null)
  195.                 return Prepend(newNode);
  196.            
  197.             if (refNode.OwnerElement != parent)
  198.                 throw new ArgumentException(Res.GetString(Res.Xdom_AttrCol_Insert));
  199.            
  200.             if (newNode.OwnerDocument != null && newNode.OwnerDocument != parent.OwnerDocument)
  201.                 throw new ArgumentException(Res.GetString(Res.Xdom_NamedNode_Context));
  202.            
  203.             if (newNode.OwnerElement != null)
  204.                 Detach(newNode);
  205.            
  206.             int offset = FindNodeOffset(refNode.LocalName, refNode.NamespaceURI);
  207.             Debug.Assert(offset != -1);
  208.             // the if statement above guarantees that the ref node is in the collection
  209.             int dupoff = RemoveDuplicateAttribute(newNode);
  210.             if (dupoff >= 0 && dupoff < offset)
  211.                 offset--;
  212.             InsertNodeAt(offset + 1, newNode);
  213.            
  214.             return newNode;
  215.         }
  216.        
  217.         // Removes the specified attribute node from the map.
  218.         public XmlAttribute Remove(XmlAttribute node)
  219.         {
  220.             if (nodes != null) {
  221.                 int cNodes = nodes.Count;
  222.                 for (int offset = 0; offset < cNodes; offset++) {
  223.                     if (nodes[offset] == node) {
  224.                         RemoveNodeAt(offset);
  225.                         return node;
  226.                     }
  227.                 }
  228.             }
  229.             return null;
  230.         }
  231.        
  232.         // Removes the attribute node with the specified index from the map.
  233.         public XmlAttribute RemoveAt(int i)
  234.         {
  235.             if (i < 0 || i >= Count || nodes == null)
  236.                 return null;
  237.            
  238.             return (XmlAttribute)RemoveNodeAt(i);
  239.         }
  240.        
  241.         // Removes all attributes from the map.
  242.         public void RemoveAll()
  243.         {
  244.             int n = Count;
  245.             while (n > 0) {
  246.                 n--;
  247.                 RemoveAt(n);
  248.             }
  249.         }
  250.        
  251.         void ICollection.CopyTo(Array array, int index)
  252.         {
  253.             for (int i = 0int max = Nodes.Count; i < max; i++,index++)
  254.                 array.SetValue(nodes[i], index);
  255.         }
  256.        
  257.         bool ICollection.IsSynchronized {
  258.             get { return false; }
  259.         }
  260.        
  261.         object ICollection.SyncRoot {
  262.             get { return this; }
  263.         }
  264.        
  265.         int ICollection.Count {
  266.             get { return base.Count; }
  267.         }
  268.        
  269.         public void CopyTo(XmlAttribute[] array, int index)
  270.         {
  271.             for (int i = 0int max = Count; i < max; i++,index++)
  272.                 array[index] = (XmlAttribute)(((XmlNode)nodes[i]).CloneNode(true));
  273.         }
  274.        
  275.         internal override XmlNode AddNode(XmlNode node)
  276.         {
  277.             //should be sure by now that the node doesn't have the same name with an existing node in the collection
  278.             RemoveDuplicateAttribute((XmlAttribute)node);
  279.             XmlNode retNode = base.AddNode(node);
  280.             Debug.Assert(retNode is XmlAttribute);
  281.             InsertParentIntoElementIdAttrMap((XmlAttribute)node);
  282.             return retNode;
  283.         }
  284.        
  285.         internal override XmlNode InsertNodeAt(int i, XmlNode node)
  286.         {
  287.             XmlNode retNode = base.InsertNodeAt(i, node);
  288.             InsertParentIntoElementIdAttrMap((XmlAttribute)node);
  289.             return retNode;
  290.         }
  291.        
  292.         internal override XmlNode RemoveNodeAt(int i)
  293.         {
  294.             //remove the node without checking replacement
  295.             XmlNode retNode = base.RemoveNodeAt(i);
  296.             Debug.Assert(retNode is XmlAttribute);
  297.             RemoveParentFromElementIdAttrMap((XmlAttribute)retNode);
  298.             // after remove the attribute, we need to check if a default attribute node should be created and inserted into the tree
  299.             XmlAttribute defattr = parent.OwnerDocument.GetDefaultAttribute((XmlElement)parent, retNode.Prefix, retNode.LocalName, retNode.NamespaceURI);
  300.             if (defattr != null)
  301.                 InsertNodeAt(i, defattr);
  302.             return retNode;
  303.         }
  304.        
  305.         internal void Detach(XmlAttribute attr)
  306.         {
  307.             attr.OwnerElement.Attributes.Remove(attr);
  308.         }
  309.        
  310.         //insert the parent element node into the map
  311.         internal void InsertParentIntoElementIdAttrMap(XmlAttribute attr)
  312.         {
  313.             XmlElement parentElem = parent as XmlElement;
  314.             if (parentElem != null) {
  315.                 if (parent.OwnerDocument == null)
  316.                     return;
  317.                 XmlName attrname = parent.OwnerDocument.GetIDInfoByElement(parentElem.XmlName);
  318.                 if (attrname != null && attrname.Prefix == attr.XmlName.Prefix && attrname.LocalName == attr.XmlName.LocalName) {
  319.                     parent.OwnerDocument.AddElementWithId(attr.Value, parentElem);
  320.                     //add the element into the hashtable
  321.                 }
  322.             }
  323.         }
  324.        
  325.         //remove the parent element node from the map when the ID attribute is removed
  326.         internal void RemoveParentFromElementIdAttrMap(XmlAttribute attr)
  327.         {
  328.             XmlElement parentElem = parent as XmlElement;
  329.             if (parentElem != null) {
  330.                 if (parent.OwnerDocument == null)
  331.                     return;
  332.                 XmlName attrname = parent.OwnerDocument.GetIDInfoByElement(parentElem.XmlName);
  333.                 if (attrname != null && attrname.Prefix == attr.XmlName.Prefix && attrname.LocalName == attr.XmlName.LocalName) {
  334.                     parent.OwnerDocument.RemoveElementWithId(attr.Value, parentElem);
  335.                     //remove the element from the hashtable
  336.                 }
  337.             }
  338.         }
  339.        
  340.         //the function checks if there is already node with the same name existing in the collection
  341.         // if so, remove it because the new one will be inserted to replace this one (could be in different position though )
  342.         // by the calling function later
  343.         internal int RemoveDuplicateAttribute(XmlAttribute attr)
  344.         {
  345.             int ind = FindNodeOffset(attr.LocalName, attr.NamespaceURI);
  346.             if (ind != -1) {
  347.                 XmlAttribute at = (XmlAttribute)Nodes[ind];
  348.                 base.RemoveNodeAt(ind);
  349.                 RemoveParentFromElementIdAttrMap(at);
  350.             }
  351.             return ind;
  352.         }
  353.        
  354.         internal bool PrepareParentInElementIdAttrMap(string attrPrefix, string attrLocalName)
  355.         {
  356.             XmlElement parentElem = parent as XmlElement;
  357.             Debug.Assert(parentElem != null);
  358.             XmlDocument doc = parent.OwnerDocument;
  359.             Debug.Assert(doc != null);
  360.             //The returned attrname if not null is the name with namespaceURI being set to string.Empty
  361.             //Because DTD doesn't support namespaceURI so all comparisons are based on no namespaceURI (string.Empty);
  362.             XmlName attrname = doc.GetIDInfoByElement(parentElem.XmlName);
  363.             if (attrname != null && attrname.Prefix == attrPrefix && attrname.LocalName == attrLocalName) {
  364.                 return true;
  365.             }
  366.             return false;
  367.         }
  368.        
  369.         internal void ResetParentInElementIdAttrMap(string oldVal, string newVal)
  370.         {
  371.             XmlElement parentElem = parent as XmlElement;
  372.             Debug.Assert(parentElem != null);
  373.             XmlDocument doc = parent.OwnerDocument;
  374.             Debug.Assert(doc != null);
  375.             doc.RemoveElementWithId(oldVal, parentElem);
  376.             //add the element into the hashtable
  377.             doc.AddElementWithId(newVal, parentElem);
  378.         }
  379.        
  380.         // WARNING:
  381.         // For performance reasons, this function does not check
  382.         // for xml attributes within the collection with the same full name.
  383.         // This means that any caller of this function must be sure that
  384.         // a duplicate attribute does not exist.
  385.         internal XmlAttribute InternalAppendAttribute(XmlAttribute node)
  386.         {
  387.             // a duplicate node better not exist
  388.             Debug.Assert(-1 == FindNodeOffset(node));
  389.            
  390.             XmlNode retNode = base.AddNode(node);
  391.             Debug.Assert(retNode is XmlAttribute);
  392.             InsertParentIntoElementIdAttrMap((XmlAttribute)node);
  393.             return (XmlAttribute)retNode;
  394.         }
  395.     }
  396. }

Developer Fusion