2 // System.Xml.XmlDocumentNavigator
5 // Jason Diamond <jason@injektilo.org>
6 // Atsushi Enomoto <ginga@kit.hi-ho.ne.jp>
8 // (C) 2002 Jason Diamond
9 // (C) 2003 Atsushi Enomoto
13 // Permission is hereby granted, free of charge, to any person obtaining
14 // a copy of this software and associated documentation files (the
15 // "Software"), to deal in the Software without restriction, including
16 // without limitation the rights to use, copy, modify, merge, publish,
17 // distribute, sublicense, and/or sell copies of the Software, and to
18 // permit persons to whom the Software is furnished to do so, subject to
19 // the following conditions:
21 // The above copyright notice and this permission notice shall be
22 // included in all copies or substantial portions of the Software.
24 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
25 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
26 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
27 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
28 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
29 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
30 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
34 using System
.Collections
;
36 using System
.Xml
.XPath
;
40 internal class XmlDocumentNavigator
: XPathNavigator
, IHasXmlNode
44 internal XmlDocumentNavigator (XmlNode node
)
47 nsNodeXml
= document
.CreateAttribute ("xmlns", "xml", Xmlns
);
48 nsNodeXml
.Value
= XmlnsXML
;
49 if (node
.NodeType
== XmlNodeType
.Attribute
&& node
.NamespaceURI
== XmlNamespaceManager
.XmlnsXmlns
) {
50 nsNode
= (XmlAttribute
) node
;
51 node
= nsNode
.OwnerElement
;
55 private XmlDocumentNavigator (XmlNode node
, XmlAttribute nsNodeXml
)
58 this.document
= node
.NodeType
== XmlNodeType
.Document
?
59 node
as XmlDocument
: node
.OwnerDocument
;
60 this.nsNodeXml
= nsNodeXml
;
66 private const string Xmlns
= "http://www.w3.org/2000/xmlns/";
67 private const string XmlnsXML
= "http://www.w3.org/XML/1998/namespace";
69 private XmlAttribute nsNodeXml
;
71 private XmlDocument document
;
72 // Current namespace node (ancestor's attribute of current node).
73 private XmlAttribute nsNode
;
74 private ArrayList iteratedNsNames
;
79 public override string BaseURI
{
85 public override bool HasAttributes
{
90 if (node
.Attributes
!= null)
91 for (int i
= 0; i
< node
.Attributes
.Count
; i
++)
92 if (node
.Attributes
[i
].NamespaceURI
!= Xmlns
)
98 public override bool HasChildren
{
103 XPathNodeType nodeType
= NodeType
;
104 bool canHaveChildren
= nodeType
== XPathNodeType
.Root
|| nodeType
== XPathNodeType
.Element
;
105 return canHaveChildren
&& node
.FirstChild
!= null;
109 public override bool IsEmptyElement
{
114 return node
.NodeType
== XmlNodeType
.Element
115 && ((XmlElement
) node
).IsEmpty
;
119 public XmlAttribute NsNode
{
120 get { return nsNode; }
123 if (iteratedNsNames
!= null)
124 iteratedNsNames
.Clear ();
127 if (iteratedNsNames
== null)
128 iteratedNsNames
= new ArrayList ();
129 iteratedNsNames
.Add (value.Name
);
135 public override string LocalName
{
137 XmlAttribute nsNode
= NsNode
;
138 if (nsNode
!= null) {
139 if (nsNode
== nsNodeXml
)
142 return (nsNode
.Name
== "xmlns") ? String
.Empty
: nsNode
.LocalName
;
145 XPathNodeType nodeType
= NodeType
;
147 nodeType
== XPathNodeType
.Element
||
148 nodeType
== XPathNodeType
.Attribute
||
149 nodeType
== XPathNodeType
.ProcessingInstruction
||
150 nodeType
== XPathNodeType
.Namespace
;
151 return canHaveName
? node
.LocalName
: String
.Empty
;
155 public override string Name
{
160 XPathNodeType nodeType
= NodeType
;
162 nodeType
== XPathNodeType
.Element
||
163 nodeType
== XPathNodeType
.Attribute
||
164 nodeType
== XPathNodeType
.ProcessingInstruction
||
165 nodeType
== XPathNodeType
.Namespace
;
166 return canHaveName
? node
.Name
: String
.Empty
;
170 public override string NamespaceURI
{
171 get { return (NsNode != null) ? String.Empty : node.NamespaceURI; }
174 public override XmlNameTable NameTable
{
176 return document
.NameTable
;
180 public override XPathNodeType NodeType
{
181 get { return (NsNode != null) ? XPathNodeType.Namespace : node.XPathNodeType; }
184 public override string Prefix
{
185 get { return (NsNode != null) ? String.Empty : node.Prefix; }
188 public override string Value
{
191 case XPathNodeType
.Attribute
:
192 case XPathNodeType
.Comment
:
193 case XPathNodeType
.ProcessingInstruction
:
195 case XPathNodeType
.Text
:
196 case XPathNodeType
.Whitespace
:
197 case XPathNodeType
.SignificantWhitespace
:
198 string value = node
.Value
;
199 for (XmlNode n
= node
.NextSibling
; n
!= null; n
= n
.NextSibling
) {
200 switch (n
.XPathNodeType
) {
201 case XPathNodeType
.Text
:
202 case XPathNodeType
.Whitespace
:
203 case XPathNodeType
.SignificantWhitespace
:
210 case XPathNodeType
.Element
:
211 case XPathNodeType
.Root
:
212 return node
.InnerText
;
213 case XPathNodeType
.Namespace
:
214 return NsNode
== nsNodeXml
? XmlnsXML
: NsNode
.Value
;
220 public override string XmlLang
{
230 private bool CheckNsNameAppearance (string name
, string ns
)
232 if (iteratedNsNames
!= null && iteratedNsNames
.Contains (name
))
234 // default namespace erasure - just add name and never return this node
235 if (ns
== String
.Empty
) {
236 if (iteratedNsNames
== null)
237 iteratedNsNames
= new ArrayList ();
238 iteratedNsNames
.Add ("xmlns");
245 public override XPathNavigator
Clone ()
247 XmlDocumentNavigator clone
= new XmlDocumentNavigator (node
, nsNodeXml
);
248 clone
.nsNode
= nsNode
;
249 if (iteratedNsNames
!= null)
250 clone
.iteratedNsNames
= (ArrayList
) iteratedNsNames
.Clone ();
254 public override string GetAttribute (string localName
, string namespaceURI
)
257 XmlElement el
= Node
as XmlElement
;
258 return el
!= null ? el
.GetAttribute (localName
, namespaceURI
) : String
.Empty
;
263 public override string GetNamespace (string name
)
265 // MSDN says "String.Empty if a matching namespace
266 // node is not found or if the navigator is not
267 // positioned on an element node", but in fact it
268 // returns actual namespace for the other nodes.
269 return Node
.GetNamespaceOfPrefix (name
);
272 public override bool IsSamePosition (XPathNavigator other
)
274 XmlDocumentNavigator otherDocumentNavigator
= other
as XmlDocumentNavigator
;
275 if (otherDocumentNavigator
!= null)
276 return node
== otherDocumentNavigator
.node
277 && NsNode
== otherDocumentNavigator
.NsNode
;
281 public override bool MoveTo (XPathNavigator other
)
283 XmlDocumentNavigator otherDocumentNavigator
= other
as XmlDocumentNavigator
;
284 if (otherDocumentNavigator
!= null) {
285 if (document
== otherDocumentNavigator
.document
) {
286 node
= otherDocumentNavigator
.node
;
287 NsNode
= otherDocumentNavigator
.NsNode
;
294 public override bool MoveToAttribute (string localName
, string namespaceURI
)
296 if (node
.Attributes
!= null) {
297 for (int i
= 0; i
< node
.Attributes
.Count
; i
++) {
298 XmlAttribute attr
= node
.Attributes
[i
];
299 if (attr
.LocalName
== localName
300 && attr
.NamespaceURI
== namespaceURI
) {
310 public override bool MoveToFirst ()
312 if (NsNode
== null && node
.NodeType
!= XmlNodeType
.Attribute
&& node
.ParentNode
!= null) {
313 if (!MoveToParent ())
315 // Follow these 2 steps so that we can skip
316 // some types of nodes .
323 public override bool MoveToFirstAttribute ()
325 if (node
.Attributes
== null)
327 if (NodeType
== XPathNodeType
.Element
) {
328 for (int i
= 0; i
< node
.Attributes
.Count
; i
++) {
329 XmlAttribute attr
= node
.Attributes
[i
];
330 if (attr
.NamespaceURI
!= Xmlns
) {
340 public override bool MoveToFirstChild ()
343 if (node
== document
) {
344 XmlNode n
= node
.FirstChild
;
349 switch (n
.NodeType
) {
350 case XmlNodeType
.XmlDeclaration
:
351 case XmlNodeType
.DocumentType
:
365 n2
= node
.FirstChild
;
366 if (node
.NodeType
!= XmlNodeType
.EntityReference
)
368 n2
= node
.NextSibling
;
369 } while (n2
!= null);
379 public override bool MoveToFirstNamespace (XPathNamespaceScope namespaceScope
)
381 if (NodeType
!= XPathNodeType
.Element
)
383 XmlElement el
= node
as XmlElement
;
384 if (node
.Attributes
!= null) {
386 for (int i
= 0; i
< el
.Attributes
.Count
; i
++) {
387 XmlAttribute attr
= el
.Attributes
[i
];
388 if (attr
.NamespaceURI
== Xmlns
) {
389 if (CheckNsNameAppearance (attr
.Name
, attr
.Value
))
395 if (namespaceScope
== XPathNamespaceScope
.Local
)
397 el
= el
.ParentNode
as XmlElement
;
398 } while (el
!= null);
401 if (namespaceScope
== XPathNamespaceScope
.All
) {
402 if (CheckNsNameAppearance (nsNodeXml
.Name
, nsNodeXml
.Value
))
411 public override bool MoveToId (string id
)
413 XmlElement eltNew
= document
.GetElementById (id
);
421 public override bool MoveToNamespace (string name
)
428 if (NodeType
!= XPathNodeType
.Element
)
431 XmlElement el
= node
as XmlElement
;
432 if (node
.Attributes
!= null) {
434 for (int i
= 0; i
< el
.Attributes
.Count
; i
++) {
435 XmlAttribute attr
= el
.Attributes
[i
];
436 if (attr
.NamespaceURI
== Xmlns
&& attr
.Name
== name
) {
441 el
= node
.ParentNode
as XmlElement
;
442 } while (el
!= null);
447 public override bool MoveToNext ()
453 if (NodeType
== XPathNodeType
.Text
) {
458 switch (n
.NodeType
) {
459 case XmlNodeType
.CDATA
:
460 case XmlNodeType
.EntityReference
:
461 case XmlNodeType
.SignificantWhitespace
:
462 case XmlNodeType
.Text
:
463 case XmlNodeType
.Whitespace
:
476 if (n
.ParentNode
!= null && n
.ParentNode
.NodeType
== XmlNodeType
.Document
) {
478 switch (n
.NodeType
) {
479 case XmlNodeType
.DocumentType
:
480 case XmlNodeType
.XmlDeclaration
:
492 if (n
.NodeType
!= XmlNodeType
.EntityReference
)
504 public override bool MoveToNextAttribute ()
508 if (NodeType
!= XPathNodeType
.Attribute
)
511 // Find current attribute.
513 XmlElement owner
= ((XmlAttribute
) node
).OwnerElement
;
517 int count
= owner
.Attributes
.Count
;
518 for(; pos
< count
; pos
++)
519 if (owner
.Attributes
[pos
] == node
)
522 return false; // Where is current attribute? Maybe removed.
524 // Find next attribute.
525 for(pos
++; pos
< count
; pos
++) {
526 if (owner
.Attributes
[pos
].NamespaceURI
!= Xmlns
) {
527 node
= owner
.Attributes
[pos
];
535 public override bool MoveToNextNamespace (XPathNamespaceScope namespaceScope
)
537 if (NsNode
== nsNodeXml
)
538 // Current namespace is "xml", so there should be no more namespace nodes.
544 // Get current attribute's position.
546 XmlElement owner
= ((XmlAttribute
) NsNode
).OwnerElement
;
550 int count
= owner
.Attributes
.Count
;
551 for(; pos
< count
; pos
++)
552 if (owner
.Attributes
[pos
] == NsNode
)
555 return false; // Where is current attribute? Maybe removed.
557 // Find next namespace from the same element as current ns node.
558 for(pos
++; pos
< count
; pos
++) {
559 if (owner
.Attributes
[pos
].NamespaceURI
== Xmlns
) {
560 XmlAttribute a
= owner
.Attributes
[pos
];
561 if (CheckNsNameAppearance (a
.Name
, a
.Value
))
568 // If not found more, then find from ancestors.
569 // But if scope is Local, then it returns false here.
570 if (namespaceScope
== XPathNamespaceScope
.Local
)
572 owner
= owner
.ParentNode
as XmlElement
;
573 while (owner
!= null) {
574 for (int i
= 0; i
< owner
.Attributes
.Count
; i
++) {
575 XmlAttribute attr
= owner
.Attributes
[i
];
576 if (attr
.NamespaceURI
== Xmlns
) {
577 if (CheckNsNameAppearance (attr
.Name
, attr
.Value
))
583 owner
= owner
.ParentNode
as XmlElement
;
586 if (namespaceScope
== XPathNamespaceScope
.All
) {
587 if (CheckNsNameAppearance (nsNodeXml
.Name
, nsNodeXml
.Value
))
595 public override bool MoveToParent ()
597 if (NsNode
!= null) {
601 else if (node
.NodeType
== XmlNodeType
.Attribute
) {
602 XmlElement ownerElement
= ((XmlAttribute
)node
).OwnerElement
;
603 if (ownerElement
!= null) {
608 } else if (node
.ParentNode
!= null) {
609 node
= node
.ParentNode
;
616 public override bool MoveToPrevious ()
621 if (node
.PreviousSibling
!= null) {
622 if (node
.ParentNode
!= null && node
.ParentNode
.NodeType
== XmlNodeType
.Document
) {
623 XmlNode n
= node
.PreviousSibling
;
625 switch (n
.NodeType
) {
626 case XmlNodeType
.DocumentType
:
627 case XmlNodeType
.XmlDeclaration
:
628 n
= n
.PreviousSibling
;
639 node
= node
.PreviousSibling
;
647 public override void MoveToRoot ()
649 XmlAttribute attr
= node
as XmlAttribute
;
650 XmlNode tmp
= attr
!= null ? attr
.OwnerElement
: node
;
651 while (tmp
.ParentNode
!= null)
652 tmp
= tmp
.ParentNode
;
657 private XmlNode Node { get { return NsNode != null ? NsNode : node; }
}
659 XmlNode IHasXmlNode
.GetNode ()