Updates referencesource to .NET 4.7
[mono-project.git] / mcs / class / referencesource / System.Xml / System / Xml / Dom / XmlAttributeCollection.cs
blobbfe86a970799052ee43844e84fff82c89f9aea34
1 //------------------------------------------------------------------------------
2 // <copyright file="XmlAttributeCollection.cs" company="Microsoft">
3 // Copyright (c) Microsoft Corporation. All rights reserved.
4 // </copyright>
5 // <owner current="true" primary="true">Microsoft</owner>
6 //------------------------------------------------------------------------------
9 namespace System.Xml {
10 using System;
11 using System.Collections;
12 using System.Diagnostics;
14 // Represents a collection of attributes that can be accessed by name or index.
15 public sealed class XmlAttributeCollection: XmlNamedNodeMap, ICollection {
16 internal XmlAttributeCollection( XmlNode parent ): base( parent ) {
19 // Gets the attribute with the specified index.
20 [System.Runtime.CompilerServices.IndexerName ("ItemOf")]
21 public XmlAttribute this[ int i ] {
22 get {
23 try {
24 return (XmlAttribute)nodes[i];
25 } catch ( ArgumentOutOfRangeException ) {
26 throw new IndexOutOfRangeException(Res.GetString(Res.Xdom_IndexOutOfRange));
31 // Gets the attribute with the specified name.
32 [System.Runtime.CompilerServices.IndexerName ("ItemOf")]
33 public XmlAttribute this[ string name ]
35 get {
36 int hash = XmlName.GetHashCode(name);
38 for (int i = 0; i < nodes.Count; i++) {
39 XmlAttribute node = (XmlAttribute) nodes[i];
41 if (hash == node.LocalNameHash
42 && name == node.Name )
44 return node;
48 return null;
52 // Gets the attribute with the specified LocalName and NamespaceUri.
53 [System.Runtime.CompilerServices.IndexerName ("ItemOf")]
54 public XmlAttribute this[ string localName, string namespaceURI ]
56 get {
57 int hash = XmlName.GetHashCode(localName);
59 for (int i = 0; i < nodes.Count; i++) {
60 XmlAttribute node = (XmlAttribute) nodes[i];
62 if (hash == node.LocalNameHash
63 && localName == node.LocalName
64 && namespaceURI == node.NamespaceURI)
66 return node;
70 return null;
74 internal int FindNodeOffset( XmlAttribute node ) {
75 for (int i = 0; i < nodes.Count; i++) {
76 XmlAttribute tmp = (XmlAttribute) nodes[i];
78 if (tmp.LocalNameHash == node.LocalNameHash
79 && tmp.Name == node.Name
80 && tmp.NamespaceURI == node.NamespaceURI )
82 return i;
85 return -1;
88 internal int FindNodeOffsetNS(XmlAttribute node) {
89 for (int i = 0; i < nodes.Count; i++) {
90 XmlAttribute tmp = (XmlAttribute) nodes[i];
91 if (tmp.LocalNameHash == node.LocalNameHash
92 && tmp.LocalName == node.LocalName
93 && tmp.NamespaceURI == node.NamespaceURI) {
94 return i;
97 return -1;
100 // Adds a XmlNode using its Name property
101 public override XmlNode SetNamedItem(XmlNode node) {
102 if (node != null && !(node is XmlAttribute))
103 throw new ArgumentException(Res.GetString(Res.Xdom_AttrCol_Object));
105 int offset = FindNodeOffset( node.LocalName, node.NamespaceURI );
106 if (offset == -1) {
107 return InternalAppendAttribute( (XmlAttribute) node );
109 else {
110 XmlNode oldNode = base.RemoveNodeAt( offset );
111 InsertNodeAt( offset, node );
112 return oldNode;
116 // Inserts the specified node as the first node in the collection.
117 public XmlAttribute Prepend( XmlAttribute node ) {
118 if (node.OwnerDocument != null && node.OwnerDocument != parent.OwnerDocument)
119 throw new ArgumentException(Res.GetString(Res.Xdom_NamedNode_Context));
121 if (node.OwnerElement != null)
122 Detach( node );
124 RemoveDuplicateAttribute( node );
126 InsertNodeAt( 0, node );
127 return node;
130 // Inserts the specified node as the last node in the collection.
131 public XmlAttribute Append(XmlAttribute node) {
132 XmlDocument doc = node.OwnerDocument;
133 if (doc == null || doc.IsLoading == false) {
134 if (doc != null && doc != parent.OwnerDocument) {
135 throw new ArgumentException(Res.GetString(Res.Xdom_NamedNode_Context));
137 if (node.OwnerElement != null) {
138 Detach(node);
140 AddNode(node);
142 else {
143 base.AddNodeForLoad(node, doc);
144 InsertParentIntoElementIdAttrMap(node);
146 return node;
149 // Inserts the specified attribute immediately before the specified reference attribute.
150 public XmlAttribute InsertBefore( XmlAttribute newNode, XmlAttribute refNode ) {
151 if ( newNode == refNode )
152 return newNode;
154 if (refNode == null)
155 return Append(newNode);
157 if (refNode.OwnerElement != parent)
158 throw new ArgumentException(Res.GetString(Res.Xdom_AttrCol_Insert));
160 if (newNode.OwnerDocument != null && newNode.OwnerDocument != parent.OwnerDocument)
161 throw new ArgumentException(Res.GetString(Res.Xdom_NamedNode_Context));
163 if (newNode.OwnerElement != null)
164 Detach( newNode );
166 int offset = FindNodeOffset( refNode.LocalName, refNode.NamespaceURI );
167 Debug.Assert( offset != -1 ); // the if statement above guarantees that the ref node is in the collection
169 int dupoff = RemoveDuplicateAttribute( newNode );
170 if ( dupoff >= 0 && dupoff < offset )
171 offset--;
172 InsertNodeAt( offset, newNode );
174 return newNode;
177 // Inserts the specified attribute immediately after the specified reference attribute.
178 public XmlAttribute InsertAfter( XmlAttribute newNode, XmlAttribute refNode ) {
179 if ( newNode == refNode )
180 return newNode;
182 if (refNode == null)
183 return Prepend(newNode);
185 if (refNode.OwnerElement != parent)
186 throw new ArgumentException(Res.GetString(Res.Xdom_AttrCol_Insert));
188 if (newNode.OwnerDocument != null && newNode.OwnerDocument != parent.OwnerDocument)
189 throw new ArgumentException(Res.GetString(Res.Xdom_NamedNode_Context));
191 if (newNode.OwnerElement != null)
192 Detach( newNode );
194 int offset = FindNodeOffset( refNode.LocalName, refNode.NamespaceURI );
195 Debug.Assert( offset != -1 ); // the if statement above guarantees that the ref node is in the collection
197 int dupoff = RemoveDuplicateAttribute( newNode );
198 if ( dupoff >= 0 && dupoff < offset )
199 offset--;
200 InsertNodeAt( offset+1, newNode );
202 return newNode;
205 // Removes the specified attribute node from the map.
206 public XmlAttribute Remove( XmlAttribute node ) {
207 int cNodes = nodes.Count;
208 for (int offset = 0; offset < cNodes; offset++) {
209 if (nodes[offset] == node) {
210 RemoveNodeAt( offset );
211 return node;
214 return null;
217 // Removes the attribute node with the specified index from the map.
218 public XmlAttribute RemoveAt( int i ) {
219 if (i < 0 || i >= Count)
220 return null;
222 return(XmlAttribute) RemoveNodeAt( i );
225 // Removes all attributes from the map.
226 public void RemoveAll() {
227 int n = Count;
228 while (n > 0) {
229 n--;
230 RemoveAt( n );
234 void ICollection.CopyTo(Array array, int index) {
235 for (int i=0, max=Count; i<max; i++, index++)
236 array.SetValue(nodes[i], index);
239 bool ICollection.IsSynchronized {
240 get { return false; }
243 object ICollection.SyncRoot {
244 get { return this; }
247 int ICollection.Count {
248 get { return base.Count; }
251 public void CopyTo(XmlAttribute[] array, int index) {
252 for (int i=0, max=Count; i<max; i++, index++)
253 array[index] = (XmlAttribute)(((XmlNode)nodes[i]).CloneNode(true));
256 internal override XmlNode AddNode( XmlNode node ) {
257 //should be sure by now that the node doesn't have the same name with an existing node in the collection
258 RemoveDuplicateAttribute( (XmlAttribute)node );
259 XmlNode retNode = base.AddNode( node );
260 Debug.Assert( retNode is XmlAttribute );
261 InsertParentIntoElementIdAttrMap( (XmlAttribute) node );
262 return retNode;
265 internal override XmlNode InsertNodeAt( int i, XmlNode node ) {
266 XmlNode retNode = base.InsertNodeAt(i, node);
267 InsertParentIntoElementIdAttrMap( (XmlAttribute)node );
268 return retNode;
271 internal override XmlNode RemoveNodeAt( int i ) {
272 //remove the node without checking replacement
273 XmlNode retNode = base.RemoveNodeAt( i );
274 Debug.Assert(retNode is XmlAttribute);
275 RemoveParentFromElementIdAttrMap( (XmlAttribute) retNode );
276 // after remove the attribute, we need to check if a default attribute node should be created and inserted into the tree
277 XmlAttribute defattr = parent.OwnerDocument.GetDefaultAttribute( (XmlElement)parent, retNode.Prefix, retNode.LocalName, retNode.NamespaceURI );
278 if ( defattr != null )
279 InsertNodeAt( i, defattr );
280 return retNode;
283 internal void Detach( XmlAttribute attr ) {
284 attr.OwnerElement.Attributes.Remove( attr );
287 //insert the parent element node into the map
288 internal void InsertParentIntoElementIdAttrMap(XmlAttribute attr)
290 XmlElement parentElem = parent as XmlElement;
291 if (parentElem != null)
293 if (parent.OwnerDocument == null)
294 return;
295 XmlName attrname = parent.OwnerDocument.GetIDInfoByElement(parentElem.XmlName);
296 if (attrname != null && attrname.Prefix == attr.XmlName.Prefix && attrname.LocalName == attr.XmlName.LocalName) {
297 parent.OwnerDocument.AddElementWithId(attr.Value, parentElem); //add the element into the hashtable
302 //remove the parent element node from the map when the ID attribute is removed
303 internal void RemoveParentFromElementIdAttrMap(XmlAttribute attr)
305 XmlElement parentElem = parent as XmlElement;
306 if (parentElem != null)
308 if (parent.OwnerDocument == null)
309 return;
310 XmlName attrname = parent.OwnerDocument.GetIDInfoByElement(parentElem.XmlName);
311 if (attrname != null && attrname.Prefix == attr.XmlName.Prefix && attrname.LocalName == attr.XmlName.LocalName) {
312 parent.OwnerDocument.RemoveElementWithId(attr.Value, parentElem); //remove the element from the hashtable
317 //the function checks if there is already node with the same name existing in the collection
318 // if so, remove it because the new one will be inserted to replace this one (could be in different position though )
319 // by the calling function later
320 internal int RemoveDuplicateAttribute( XmlAttribute attr ) {
321 int ind = FindNodeOffset( attr.LocalName, attr.NamespaceURI );
322 if ( ind != -1 ) {
323 XmlAttribute at = (XmlAttribute)nodes[ind];
324 base.RemoveNodeAt( ind );
325 RemoveParentFromElementIdAttrMap( at );
327 return ind;
330 internal bool PrepareParentInElementIdAttrMap(string attrPrefix, string attrLocalName) {
331 XmlElement parentElem = parent as XmlElement;
332 Debug.Assert( parentElem != null );
333 XmlDocument doc = parent.OwnerDocument;
334 Debug.Assert( doc != null );
335 //The returned attrname if not null is the name with namespaceURI being set to string.Empty
336 //Because DTD doesn't support namespaceURI so all comparisons are based on no namespaceURI (string.Empty);
337 XmlName attrname = doc.GetIDInfoByElement(parentElem.XmlName);
338 if (attrname != null && attrname.Prefix == attrPrefix && attrname.LocalName == attrLocalName) {
339 return true;
341 return false;
344 internal void ResetParentInElementIdAttrMap(string oldVal, string newVal) {
345 XmlElement parentElem = parent as XmlElement;
346 Debug.Assert( parentElem != null );
347 XmlDocument doc = parent.OwnerDocument;
348 Debug.Assert( doc != null );
349 doc.RemoveElementWithId(oldVal, parentElem); //add the element into the hashtable
350 doc.AddElementWithId(newVal, parentElem);
353 // WARNING:
354 // For performance reasons, this function does not check
355 // for xml attributes within the collection with the same full name.
356 // This means that any caller of this function must be sure that
357 // a duplicate attribute does not exist.
358 internal XmlAttribute InternalAppendAttribute( XmlAttribute node ) {
359 // a duplicate node better not exist
360 Debug.Assert( -1 == FindNodeOffset( node ));
362 XmlNode retNode = base.AddNode( node );
363 Debug.Assert( retNode is XmlAttribute );
364 InsertParentIntoElementIdAttrMap( (XmlAttribute) node );
365 return (XmlAttribute)retNode;