1 //------------------------------------------------------------------------------
2 // <copyright file="XmlAttributeCollection.cs" company="Microsoft">
3 // Copyright (c) Microsoft Corporation. All rights reserved.
5 // <owner current="true" primary="true">Microsoft</owner>
6 //------------------------------------------------------------------------------
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
] {
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
]
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
)
52 // Gets the attribute with the specified LocalName and NamespaceUri.
53 [System
.Runtime
.CompilerServices
.IndexerName ("ItemOf")]
54 public XmlAttribute
this[ string localName
, string namespaceURI
]
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
)
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
)
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
) {
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
);
107 return InternalAppendAttribute( (XmlAttribute
) node
);
110 XmlNode oldNode
= base.RemoveNodeAt( offset
);
111 InsertNodeAt( offset
, node
);
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)
124 RemoveDuplicateAttribute( node
);
126 InsertNodeAt( 0, 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) {
143 base.AddNodeForLoad(node
, doc
);
144 InsertParentIntoElementIdAttrMap(node
);
149 // Inserts the specified attribute immediately before the specified reference attribute.
150 public XmlAttribute
InsertBefore( XmlAttribute newNode
, XmlAttribute refNode
) {
151 if ( newNode
== refNode
)
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)
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
)
172 InsertNodeAt( offset
, newNode
);
177 // Inserts the specified attribute immediately after the specified reference attribute.
178 public XmlAttribute
InsertAfter( XmlAttribute newNode
, XmlAttribute refNode
) {
179 if ( newNode
== refNode
)
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)
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
)
200 InsertNodeAt( offset
+1, 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
);
217 // Removes the attribute node with the specified index from the map.
218 public XmlAttribute
RemoveAt( int i
) {
219 if (i
< 0 || i
>= Count
)
222 return(XmlAttribute
) RemoveNodeAt( i
);
225 // Removes all attributes from the map.
226 public void RemoveAll() {
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
{
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
);
265 internal override XmlNode
InsertNodeAt( int i
, XmlNode node
) {
266 XmlNode retNode
= base.InsertNodeAt(i
, node
);
267 InsertParentIntoElementIdAttrMap( (XmlAttribute
)node
);
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
);
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)
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)
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
);
323 XmlAttribute at
= (XmlAttribute
)nodes
[ind
];
324 base.RemoveNodeAt( ind
);
325 RemoveParentFromElementIdAttrMap( at
);
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
) {
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
);
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
;