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
.Schema
;
37 using System
.Xml
.XPath
;
41 internal class XmlDocumentNavigator
: XPathNavigator
, IHasXmlNode
45 internal XmlDocumentNavigator (XmlNode node
)
48 if (node
.NodeType
== XmlNodeType
.Attribute
&& node
.NamespaceURI
== XmlNamespaceManager
.XmlnsXmlns
) {
49 nsNode
= (XmlAttribute
) node
;
50 node
= nsNode
.OwnerElement
;
57 private const string Xmlns
= "http://www.w3.org/2000/xmlns/";
58 private const string XmlnsXML
= "http://www.w3.org/XML/1998/namespace";
61 // Current namespace node (ancestor's attribute of current node).
62 private XmlAttribute nsNode
;
63 private ArrayList iteratedNsNames
;
68 internal XmlDocument Document
{
69 get { return node.NodeType == XmlNodeType.Document ? node as XmlDocument : node.OwnerDocument; }
72 public override string BaseURI
{
78 public override bool HasAttributes
{
83 XmlElement el
= node
as XmlElement
;
84 if (el
== null || !el
.HasAttributes
)
87 for (int i
= 0; i
< node
.Attributes
.Count
; i
++)
88 if (node
.Attributes
[i
].NamespaceURI
!= Xmlns
)
94 public override bool HasChildren
{
99 XPathNodeType nodeType
= NodeType
;
100 bool canHaveChildren
= nodeType
== XPathNodeType
.Root
|| nodeType
== XPathNodeType
.Element
;
101 return canHaveChildren
&& GetFirstChild (node
) != null;
105 public override bool IsEmptyElement
{
110 return node
.NodeType
== XmlNodeType
.Element
111 && ((XmlElement
) node
).IsEmpty
;
115 public XmlAttribute NsNode
{
116 get { return nsNode; }
119 iteratedNsNames
= null;
122 if (iteratedNsNames
== null)
123 iteratedNsNames
= new ArrayList();
126 if (iteratedNsNames
.IsReadOnly
)
127 iteratedNsNames
= new ArrayList(iteratedNsNames
);
129 iteratedNsNames
.Add (value.Name
);
135 public override string LocalName
{
137 XmlAttribute nsNode
= NsNode
;
138 if (nsNode
!= null) {
139 if (nsNode
== Document
.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
{
175 get { return Document.NameTable; }
178 public override XPathNodeType NodeType
{
181 return XPathNodeType
.Namespace
;
185 switch (n
.NodeType
) {
186 case XmlNodeType
.SignificantWhitespace
:
188 n
= GetNextSibling (n
);
190 case XmlNodeType
.Whitespace
:
191 n
= GetNextSibling (n
);
193 case XmlNodeType
.Text
:
194 case XmlNodeType
.CDATA
:
195 return XPathNodeType
.Text
;
202 XPathNodeType
.SignificantWhitespace
:
207 public override string Prefix
{
208 get { return (NsNode != null) ? String.Empty : node.Prefix; }
212 public override IXmlSchemaInfo SchemaInfo
{
213 get { return NsNode != null ? null : node.SchemaInfo; }
216 public override object UnderlyingObject
{
221 public override string Value
{
224 case XPathNodeType
.Attribute
:
225 case XPathNodeType
.Comment
:
226 case XPathNodeType
.ProcessingInstruction
:
228 case XPathNodeType
.Text
:
229 case XPathNodeType
.Whitespace
:
230 case XPathNodeType
.SignificantWhitespace
:
231 string value = node
.Value
;
232 for (XmlNode n
= GetNextSibling (node
); n
!= null; n
= GetNextSibling (n
)) {
233 switch (n
.XPathNodeType
) {
234 case XPathNodeType
.Text
:
235 case XPathNodeType
.Whitespace
:
236 case XPathNodeType
.SignificantWhitespace
:
243 case XPathNodeType
.Element
:
244 case XPathNodeType
.Root
:
245 return node
.InnerText
;
246 case XPathNodeType
.Namespace
:
247 return NsNode
== Document
.NsNodeXml
? XmlnsXML
: NsNode
.Value
;
253 public override string XmlLang
{
263 private bool CheckNsNameAppearance (string name
, string ns
)
265 if (iteratedNsNames
!= null && iteratedNsNames
.Contains (name
))
267 // default namespace erasure - just add name and never return this node
268 if (ns
== String
.Empty
) {
269 if (iteratedNsNames
== null)
270 iteratedNsNames
= new ArrayList();
273 if (iteratedNsNames
.IsReadOnly
)
274 iteratedNsNames
= new ArrayList(iteratedNsNames
);
276 iteratedNsNames
.Add ("xmlns");
283 public override XPathNavigator
Clone ()
285 XmlDocumentNavigator clone
= new XmlDocumentNavigator (node
);
286 clone
.nsNode
= nsNode
;
287 clone
.iteratedNsNames
= (iteratedNsNames
== null || iteratedNsNames
.IsReadOnly
) ? iteratedNsNames
: ArrayList
.ReadOnly(iteratedNsNames
);
291 public override string GetAttribute (string localName
, string namespaceURI
)
294 XmlElement el
= Node
as XmlElement
;
295 return el
!= null ? el
.GetAttribute (localName
, namespaceURI
) : String
.Empty
;
300 public override string GetNamespace (string name
)
302 // MSDN says "String.Empty if a matching namespace
303 // node is not found or if the navigator is not
304 // positioned on an element node", but in fact it
305 // returns actual namespace for the other nodes.
306 return Node
.GetNamespaceOfPrefix (name
);
309 public override bool IsDescendant (XPathNavigator other
)
313 XmlDocumentNavigator o
= other
as XmlDocumentNavigator
;
317 o
.node
.NodeType
== XmlNodeType
.Attribute
?
318 ((XmlAttribute
) o
.node
).OwnerElement
:
320 for (;n
!= null; n
= n
.ParentNode
)
326 public override bool IsSamePosition (XPathNavigator other
)
328 XmlDocumentNavigator otherDocumentNavigator
= other
as XmlDocumentNavigator
;
329 if (otherDocumentNavigator
!= null)
330 return node
== otherDocumentNavigator
.node
331 && NsNode
== otherDocumentNavigator
.NsNode
;
335 public override bool MoveTo (XPathNavigator other
)
337 XmlDocumentNavigator otherDocumentNavigator
= other
as XmlDocumentNavigator
;
338 if (otherDocumentNavigator
!= null) {
339 if (Document
== otherDocumentNavigator
.Document
) {
340 node
= otherDocumentNavigator
.node
;
341 NsNode
= otherDocumentNavigator
.NsNode
;
348 public override bool MoveToAttribute (string localName
, string namespaceURI
)
351 XmlAttribute attr
= node
.Attributes
[localName
, namespaceURI
];
363 public override bool MoveToFirst ()
365 return MoveToFirstImpl ();
369 public override bool MoveToFirstAttribute ()
371 if (NodeType
== XPathNodeType
.Element
) {
372 XmlElement el
= node
as XmlElement
;
373 if (!el
.HasAttributes
)
375 for (int i
= 0; i
< node
.Attributes
.Count
; i
++) {
376 XmlAttribute attr
= node
.Attributes
[i
];
377 if (attr
.NamespaceURI
!= Xmlns
) {
387 public override bool MoveToFirstChild ()
390 XmlNode n
= GetFirstChild (node
);
399 public override bool MoveToFirstNamespace (XPathNamespaceScope namespaceScope
)
401 if (NodeType
!= XPathNodeType
.Element
)
403 XmlElement el
= node
as XmlElement
;
405 if (el
.HasAttributes
) {
406 for (int i
= 0; i
< el
.Attributes
.Count
; i
++) {
407 XmlAttribute attr
= el
.Attributes
[i
];
408 if (attr
.NamespaceURI
== Xmlns
) {
409 if (CheckNsNameAppearance (attr
.Name
, attr
.Value
))
416 if (namespaceScope
== XPathNamespaceScope
.Local
)
418 el
= GetParentNode (el
) as XmlElement
;
419 } while (el
!= null);
421 if (namespaceScope
== XPathNamespaceScope
.All
) {
422 if (CheckNsNameAppearance (Document
.NsNodeXml
.Name
, Document
.NsNodeXml
.Value
))
424 NsNode
= Document
.NsNodeXml
;
431 public override bool MoveToId (string id
)
433 XmlElement eltNew
= Document
.GetElementById (id
);
441 public override bool MoveToNamespace (string name
)
444 NsNode
= Document
.NsNodeXml
;
448 if (NodeType
!= XPathNodeType
.Element
)
451 XmlElement el
= node
as XmlElement
;
453 if (el
.HasAttributes
) {
454 for (int i
= 0; i
< el
.Attributes
.Count
; i
++) {
455 XmlAttribute attr
= el
.Attributes
[i
];
456 if (attr
.NamespaceURI
== Xmlns
&& attr
.Name
== name
) {
462 el
= GetParentNode (node
) as XmlElement
;
463 } while (el
!= null);
467 public override bool MoveToNext ()
473 if (NodeType
== XPathNodeType
.Text
) {
475 n
= GetNextSibling (n
);
478 switch (n
.NodeType
) {
479 case XmlNodeType
.CDATA
:
480 case XmlNodeType
.SignificantWhitespace
:
481 case XmlNodeType
.Text
:
482 case XmlNodeType
.Whitespace
:
491 n
= GetNextSibling (n
);
498 public override bool MoveToNextAttribute ()
502 if (NodeType
!= XPathNodeType
.Attribute
)
505 // Find current attribute.
507 XmlElement owner
= ((XmlAttribute
) node
).OwnerElement
;
511 int count
= owner
.Attributes
.Count
;
512 for(; pos
< count
; pos
++)
513 if (owner
.Attributes
[pos
] == node
)
516 return false; // Where is current attribute? Maybe removed.
518 // Find next attribute.
519 for(pos
++; pos
< count
; pos
++) {
520 if (owner
.Attributes
[pos
].NamespaceURI
!= Xmlns
) {
521 node
= owner
.Attributes
[pos
];
529 public override bool MoveToNextNamespace (XPathNamespaceScope namespaceScope
)
531 if (NsNode
== Document
.NsNodeXml
)
532 // Current namespace is "xml", so there should be no more namespace nodes.
538 // Get current attribute's position.
540 XmlElement owner
= ((XmlAttribute
) NsNode
).OwnerElement
;
544 int count
= owner
.Attributes
.Count
;
545 for(; pos
< count
; pos
++)
546 if (owner
.Attributes
[pos
] == NsNode
)
549 return false; // Where is current attribute? Maybe removed.
551 // Find next namespace from the same element as current ns node.
552 for(pos
++; pos
< count
; pos
++) {
553 if (owner
.Attributes
[pos
].NamespaceURI
== Xmlns
) {
554 XmlAttribute a
= owner
.Attributes
[pos
];
555 if (CheckNsNameAppearance (a
.Name
, a
.Value
))
562 // If not found more, then find from ancestors.
563 // But if scope is Local, then it returns false here.
564 if (namespaceScope
== XPathNamespaceScope
.Local
)
566 owner
= GetParentNode (owner
) as XmlElement
;
567 while (owner
!= null) {
568 if (owner
.HasAttributes
) {
569 for (int i
= 0; i
< owner
.Attributes
.Count
; i
++) {
570 XmlAttribute attr
= owner
.Attributes
[i
];
571 if (attr
.NamespaceURI
== Xmlns
) {
572 if (CheckNsNameAppearance (attr
.Name
, attr
.Value
))
579 owner
= GetParentNode (owner
) as XmlElement
;
582 if (namespaceScope
== XPathNamespaceScope
.All
) {
583 if (CheckNsNameAppearance (Document
.NsNodeXml
.Name
, Document
.NsNodeXml
.Value
))
585 NsNode
= Document
.NsNodeXml
;
591 public override bool MoveToParent ()
593 if (NsNode
!= null) {
597 else if (node
.NodeType
== XmlNodeType
.Attribute
) {
598 XmlElement ownerElement
= ((XmlAttribute
)node
).OwnerElement
;
599 if (ownerElement
!= null) {
607 XmlNode n
= GetParentNode (node
);
615 public override bool MoveToPrevious ()
620 XmlNode p
= GetPreviousSibling (node
);
627 public override void MoveToRoot ()
629 XmlAttribute attr
= node
as XmlAttribute
;
630 XmlNode tmp
= attr
!= null ? attr
.OwnerElement
: node
;
632 return; // i.e. attr has null OwnerElement.
633 for (XmlNode tmp2
= GetParentNode (tmp
); tmp2
!= null; tmp2
= GetParentNode (tmp2
))
639 private XmlNode Node { get { return NsNode != null ? NsNode : node; }
}
641 XmlNode IHasXmlNode
.GetNode ()
646 private XmlNode
GetFirstChild (XmlNode n
)
648 if (n
.FirstChild
== null)
650 switch (n
.FirstChild
.NodeType
) {
651 case XmlNodeType
.XmlDeclaration
:
652 case XmlNodeType
.DocumentType
:
653 return GetNextSibling (n
.FirstChild
);
654 case XmlNodeType
.EntityReference
:
655 foreach (XmlNode c
in n
.ChildNodes
) {
656 if (c
.NodeType
== XmlNodeType
.EntityReference
) {
657 XmlNode ec
= GetFirstChild (c
);
670 private XmlNode
GetLastChild (XmlNode n
)
672 if (n
.LastChild
== null)
674 switch (n
.LastChild
.NodeType
) {
675 case XmlNodeType
.XmlDeclaration
:
676 case XmlNodeType
.DocumentType
:
677 return GetPreviousSibling (n
.LastChild
);
678 case XmlNodeType
.EntityReference
:
679 for (XmlNode c
= n
.LastChild
; c
!= null; c
= c
.PreviousSibling
) {
680 if (c
.NodeType
== XmlNodeType
.EntityReference
) {
681 XmlNode ec
= GetLastChild (c
);
694 private XmlNode
GetPreviousSibling (XmlNode n
)
696 XmlNode p
= n
.PreviousSibling
;
698 switch (p
.NodeType
) {
699 case XmlNodeType
.EntityReference
:
700 XmlNode c
= GetLastChild (p
);
703 else // empty entity reference etc.
704 return GetPreviousSibling (p
);
705 case XmlNodeType
.XmlDeclaration
:
706 case XmlNodeType
.DocumentType
:
707 return GetPreviousSibling (p
);
712 if (n
.ParentNode
== null || n
.ParentNode
.NodeType
!= XmlNodeType
.EntityReference
)
714 return GetPreviousSibling (n
.ParentNode
);
718 private XmlNode
GetNextSibling (XmlNode n
)
720 XmlNode nx
= n
.NextSibling
;
722 switch (nx
.NodeType
) {
723 case XmlNodeType
.EntityReference
:
724 XmlNode c
= GetFirstChild (nx
);
727 else // empty entity reference etc.
728 return GetNextSibling (nx
);
729 case XmlNodeType
.XmlDeclaration
:
730 case XmlNodeType
.DocumentType
:
731 return GetNextSibling (nx
);
733 return n
.NextSibling
;
736 if (n
.ParentNode
== null || n
.ParentNode
.NodeType
!= XmlNodeType
.EntityReference
)
738 return GetNextSibling (n
.ParentNode
);
742 private XmlNode
GetParentNode (XmlNode n
)
744 if (n
.ParentNode
== null)
746 for (XmlNode p
= n
.ParentNode
; p
!= null; p
= p
.ParentNode
)
747 if (p
.NodeType
!= XmlNodeType
.EntityReference
)
757 override string LookupNamespace (string prefix
)
760 return base.LookupNamespace (prefix
);
768 override string LookupPrefix (string namespaceUri
)
771 return base.LookupPrefix (namespaceUri
);
779 override bool MoveToChild (XPathNodeType type
)
782 return base.MoveToChild (type
);
790 override bool MoveToChild (string localName
, string namespaceURI
)
793 return base.MoveToChild (localName
, namespaceURI
);
801 override bool MoveToNext (string localName
, string namespaceURI
)
804 return base.MoveToNext (localName
, namespaceURI
);
812 override bool MoveToNext (XPathNodeType type
)
815 return base.MoveToNext (type
);
823 override bool MoveToFollowing (string localName
,
824 string namespaceURI
, XPathNavigator end
)
827 return base.MoveToFollowing (localName
, namespaceURI
, end
);
835 override bool MoveToFollowing (XPathNodeType type
,
839 return base.MoveToFollowing (type
, end
);