Welcome to mirror list, hosted at ThFree Co, Russian Federation.

XmlAttributeCollection.cs « Dom « Xml « System « System.Xml « referencesource « class « mcs - github.com/mono/mono.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
blob: 5825e1d6b774d0f342421d5d8ec92e974eceb1d8 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
//------------------------------------------------------------------------------
// <copyright file="XmlAttributeCollection.cs" company="Microsoft">
//     Copyright (c) Microsoft Corporation.  All rights reserved.
// </copyright>                                                                
// <owner current="true" primary="true">[....]</owner>
//------------------------------------------------------------------------------


namespace System.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(Res.GetString(Res.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(Res.GetString(Res.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(Res.GetString(Res.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(Res.GetString(Res.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(Res.GetString(Res.Xdom_AttrCol_Insert));

            if (newNode.OwnerDocument != null && newNode.OwnerDocument != parent.OwnerDocument)
                throw new ArgumentException(Res.GetString(Res.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(Res.GetString(Res.Xdom_AttrCol_Insert));

            if (newNode.OwnerDocument != null && newNode.OwnerDocument != parent.OwnerDocument)
                throw new ArgumentException(Res.GetString(Res.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;
        }
    }
}