2 // Mono.Xml.XPath.DTMXPathNavigator
5 // Atsushi Enomoto (ginga@kit.hi-ho.ne.jp)
7 // (C) 2003 Atsushi Enomoto
11 // Permission is hereby granted, free of charge, to any person obtaining
12 // a copy of this software and associated documentation files (the
13 // "Software"), to deal in the Software without restriction, including
14 // without limitation the rights to use, copy, modify, merge, publish,
15 // distribute, sublicense, and/or sell copies of the Software, and to
16 // permit persons to whom the Software is furnished to do so, subject to
17 // the following conditions:
19 // The above copyright notice and this permission notice shall be
20 // included in all copies or substantial portions of the Software.
22 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
23 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
24 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
25 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
26 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
27 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
28 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
31 using System
.Collections
;
34 using System
.Xml
.Schema
;
35 using System
.Xml
.XPath
;
37 namespace Mono
.Xml
.XPath
39 #if OUTSIDE_SYSTEM_XML
44 class DTMXPathNavigator
: XPathNavigator
, IXmlLineInfo
47 #region Copy of XPathDocument
48 public DTMXPathNavigator (DTMXPathDocument document
,
49 XmlNameTable nameTable
,
50 DTMXPathLinkedNode
[] nodes
,
51 DTMXPathAttributeNode
[] attributes
,
52 DTMXPathNamespaceNode
[] namespaces
,
56 this.attributes
= attributes
;
57 this.namespaces
= namespaces
;
58 this.idTable
= idTable
;
59 this.nameTable
= nameTable
;
62 this.document
= document
;
65 // Copy constructor including position informations.
66 public DTMXPathNavigator (DTMXPathNavigator org
)
67 : this (org
.document
, org
.nameTable
,
68 org
.nodes
, org
.attributes
, org
.namespaces
,
71 currentIsNode
= org
.currentIsNode
;
72 currentIsAttr
= org
.currentIsAttr
;
74 currentNode
= org
.currentNode
;
75 currentAttr
= org
.currentAttr
;
76 currentNs
= org
.currentNs
;
79 XmlNameTable nameTable
;
81 // Created XPathDocument. This is used to identify the origin of the navigator.
82 DTMXPathDocument document
;
84 DTMXPathLinkedNode
[] nodes
;// = new DTMXPathLinkedNode [0];
85 DTMXPathAttributeNode
[] attributes
;// = new DTMXPathAttributeNode [0];
86 DTMXPathNamespaceNode
[] namespaces
;// = new DTMXPathNamespaceNode [0];
91 // // Key table (considered xsd:keyref for XPath 2.0)
92 // Hashtable keyRefTable; // [string key-name] -> idTable
93 // // idTable [string value] -> int nodeId
103 StringBuilder valueBuilder
;
107 internal DTMXPathNavigator (XmlNameTable nt
)
116 public override string BaseURI
{
117 get { return nodes [currentNode].BaseURI; }
120 public override bool HasAttributes
{
121 get { return currentIsNode ? nodes [currentNode].FirstAttribute != 0 : false; }
124 public override bool HasChildren
{
125 get { return currentIsNode ? nodes [currentNode].FirstChild != 0 : false; }
128 public override bool IsEmptyElement
{
129 get { return currentIsNode ? nodes [currentNode].IsEmptyElement : false; }
132 int IXmlLineInfo
.LineNumber
{
134 return currentIsAttr
? attributes
[currentAttr
].LineNumber
:
135 nodes
[currentNode
].LineNumber
;
139 int IXmlLineInfo
.LinePosition
{
141 return currentIsAttr
? attributes
[currentAttr
].LinePosition
:
142 nodes
[currentNode
].LinePosition
;
146 public override string LocalName
{
149 return nodes
[currentNode
].LocalName
;
150 else if (currentIsAttr
)
151 return attributes
[currentAttr
].LocalName
;
153 return namespaces
[currentNs
].Name
;
157 // It maybe scarcely used, so I decided to compute it always.
158 public override string Name
{
163 prefix
= nodes
[currentNode
].Prefix
;
164 localName
= nodes
[currentNode
].LocalName
;
165 } else if (currentIsAttr
) {
166 prefix
= attributes
[currentAttr
].Prefix
;
167 localName
= attributes
[currentAttr
].LocalName
;
169 return namespaces
[currentNs
].Name
;
172 return prefix
+ ':' + localName
;
178 public override string NamespaceURI
{
181 return nodes
[currentNode
].NamespaceURI
;
183 return attributes
[currentAttr
].NamespaceURI
;
188 public override XmlNameTable NameTable
{
189 get { return nameTable; }
192 public override XPathNodeType NodeType
{
195 return nodes
[currentNode
].NodeType
;
196 else if (currentIsAttr
)
197 return XPathNodeType
.Attribute
;
199 return XPathNodeType
.Namespace
;
203 public override string Prefix
{
206 return nodes
[currentNode
].Prefix
;
207 else if (currentIsAttr
)
208 return attributes
[currentAttr
].Prefix
;
213 public override string Value
{
216 return attributes
[currentAttr
].Value
;
217 else if (!currentIsNode
)
218 return namespaces
[currentNs
].Namespace
;
220 switch (nodes
[currentNode
].NodeType
) {
221 case XPathNodeType
.Comment
:
222 case XPathNodeType
.ProcessingInstruction
:
223 case XPathNodeType
.Text
:
224 case XPathNodeType
.Whitespace
:
225 case XPathNodeType
.SignificantWhitespace
:
226 return nodes
[currentNode
].Value
;
230 if (valueBuilder
== null)
231 valueBuilder
= new StringBuilder ();
233 valueBuilder
.Length
= 0;
235 int iter
= nodes
[currentNode
].FirstChild
;
236 int depth
= nodes
[currentNode
].Depth
;
237 while (iter
< nodes
.Length
&& nodes
[iter
].Depth
> depth
) {
238 switch (nodes
[iter
].NodeType
) {
239 case XPathNodeType
.Comment
:
240 case XPathNodeType
.ProcessingInstruction
:
243 valueBuilder
.Append (nodes
[iter
].Value
);
249 return valueBuilder
.ToString ();
253 public override string XmlLang
{
254 get { return nodes [currentNode].XmlLang; }
261 public override XPathNavigator
Clone ()
263 return new DTMXPathNavigator (this);
266 public override XmlNodeOrder
ComparePosition (XPathNavigator nav
)
268 DTMXPathNavigator another
= nav
as DTMXPathNavigator
;
270 if (another
== null || another
.document
!= this.document
)
271 return XmlNodeOrder
.Unknown
;
273 if (currentNode
> another
.currentNode
)
274 return XmlNodeOrder
.After
;
275 else if (currentNode
< another
.currentNode
)
276 return XmlNodeOrder
.Before
;
278 // another may attr or ns,
279 // and this may be also attr or ns.
280 if (another
.currentIsAttr
) {
281 if (this.currentIsAttr
) {
282 if (currentAttr
> another
.currentAttr
)
283 return XmlNodeOrder
.After
;
284 else if (currentAttr
< another
.currentAttr
)
285 return XmlNodeOrder
.Before
;
287 return XmlNodeOrder
.Same
;
289 return XmlNodeOrder
.Before
;
290 } else if (!another
.currentIsNode
) {
291 if (!this.currentIsNode
) {
292 if (currentNs
> another
.currentNs
)
293 return XmlNodeOrder
.After
;
294 else if (currentNs
< another
.currentNs
)
295 return XmlNodeOrder
.Before
;
297 return XmlNodeOrder
.Same
;
299 return XmlNodeOrder
.Before
;
301 return !another
.currentIsNode
? XmlNodeOrder
.Before
: XmlNodeOrder
.Same
;
304 private int findAttribute (string localName
, string namespaceURI
)
306 if (currentIsNode
&& nodes
[currentNode
].NodeType
== XPathNodeType
.Element
) {
307 int cur
= nodes
[currentNode
].FirstAttribute
;
309 if (attributes
[cur
].LocalName
== localName
&& attributes
[cur
].NamespaceURI
== namespaceURI
)
311 cur
= attributes
[cur
].NextAttribute
;
317 public override string GetAttribute (string localName
,
320 int attr
= findAttribute (localName
, namespaceURI
);
321 return (attr
!= 0) ? attributes
[attr
].Value
: String
.Empty
;
324 public override string GetNamespace (string name
)
326 if (currentIsNode
&& nodes
[currentNode
].NodeType
== XPathNodeType
.Element
) {
327 int nsNode
= nodes
[currentNode
].FirstNamespace
;
328 while (nsNode
!= 0) {
329 if (namespaces
[nsNode
].Name
== name
)
330 return namespaces
[nsNode
].Namespace
;
331 nsNode
= namespaces
[nsNode
].NextNamespace
;
337 bool IXmlLineInfo
.HasLineInfo ()
342 public override bool IsDescendant (XPathNavigator nav
)
344 DTMXPathNavigator another
= nav
as DTMXPathNavigator
;
346 if (another
== null || another
.document
!= this.document
)
349 // Maybe we can improve here more efficiently by
350 // comparing node indices.
351 if (another
.currentNode
== currentNode
)
352 return !another
.currentIsNode
;
353 int tmp
= nodes
[another
.currentNode
].Parent
;
355 if (tmp
== currentNode
)
357 tmp
= nodes
[tmp
].Parent
;
362 public override bool IsSamePosition (XPathNavigator other
)
364 DTMXPathNavigator another
= other
as DTMXPathNavigator
;
366 if (another
== null || another
.document
!= this.document
)
369 if (this.currentNode
!= another
.currentNode
||
370 this.currentIsAttr
!= another
.currentIsAttr
||
371 this.currentIsNode
!= another
.currentIsNode
)
375 return this.currentAttr
== another
.currentAttr
;
376 else if (!currentIsNode
)
377 return this.currentNs
== another
.currentNs
;
381 public override bool MoveTo (XPathNavigator other
)
383 DTMXPathNavigator another
= other
as DTMXPathNavigator
;
385 if (another
== null || another
.document
!= this.document
)
388 this.currentNode
= another
.currentNode
;
389 this.currentAttr
= another
.currentAttr
;
390 this.currentNs
= another
.currentNs
;
391 this.currentIsNode
= another
.currentIsNode
;
392 this.currentIsAttr
= another
.currentIsAttr
;
396 public override bool MoveToAttribute (string localName
,
399 int attr
= findAttribute (localName
, namespaceURI
);
404 currentIsAttr
= true;
405 currentIsNode
= false;
409 public override bool MoveToFirst ()
414 int cur
= nodes
[currentNode
].PreviousSibling
;
421 next
= nodes
[cur
].PreviousSibling
;
424 currentIsNode
= true;
428 public override bool MoveToFirstAttribute ()
433 int first
= nodes
[currentNode
].FirstAttribute
;
438 currentIsAttr
= true;
439 currentIsNode
= false;
443 public override bool MoveToFirstChild ()
448 int first
= nodes
[currentNode
].FirstChild
;
456 private bool moveToSpecifiedNamespace (int cur
,
457 XPathNamespaceScope namespaceScope
)
462 if (namespaceScope
== XPathNamespaceScope
.Local
&&
463 namespaces
[cur
].DeclaredElement
!= currentNode
)
466 if (namespaceScope
!= XPathNamespaceScope
.All
467 && namespaces
[cur
].Namespace
== XmlNamespaces
.XML
)
471 moveToNamespace (cur
);
478 public override bool MoveToFirstNamespace (
479 XPathNamespaceScope namespaceScope
)
483 int cur
= nodes
[currentNode
].FirstNamespace
;
484 return moveToSpecifiedNamespace (cur
, namespaceScope
);
487 // Note that this support is extension to XPathDocument.
488 // XPathDocument does not support ID reference.
489 public override bool MoveToId (string id
)
491 if (idTable
.ContainsKey (id
)) {
492 currentNode
= (int) idTable
[id
];
493 currentIsNode
= true;
494 currentIsAttr
= false;
501 private void moveToNamespace (int nsNode
)
503 currentIsNode
= currentIsAttr
= false;
507 public override bool MoveToNamespace (string name
)
509 int cur
= nodes
[currentNode
].FirstNamespace
;
514 if (namespaces
[cur
].Name
== name
) {
515 moveToNamespace (cur
);
518 cur
= namespaces
[cur
].NextNamespace
;
523 public override bool MoveToNext ()
528 int next
= nodes
[currentNode
].NextSibling
;
532 currentIsNode
= true;
536 public override bool MoveToNextAttribute ()
541 int next
= attributes
[currentAttr
].NextAttribute
;
548 public override bool MoveToNextNamespace (
549 XPathNamespaceScope namespaceScope
)
551 if (currentIsAttr
|| currentIsNode
)
554 int cur
= namespaces
[currentNs
].NextNamespace
;
555 return moveToSpecifiedNamespace (cur
, namespaceScope
);
558 public override bool MoveToParent ()
560 if (!currentIsNode
) {
561 currentIsNode
= true;
562 currentIsAttr
= false;
566 int parent
= nodes
[currentNode
].Parent
;
567 if (parent
== 0) // It is root itself.
570 currentNode
= parent
;
574 public override bool MoveToPrevious ()
579 int previous
= nodes
[currentNode
].PreviousSibling
;
582 currentNode
= previous
;
583 currentIsNode
= true;
587 public override void MoveToRoot ()
589 currentNode
= 1; // root is 1.
590 currentIsNode
= true;
591 currentIsAttr
= false;
597 public string DebugDump {
599 StringBuilder sb = new StringBuilder ();
601 for (int i = 0; i < namespaces.Length; i++) {
602 DTMXPathNamespaceNode n = namespaces [i];
603 sb.AppendFormat ("{0}: {1},{2} {3}/{4}\n", i,
604 n.DeclaredElement, n.NextNamespace,
605 n.Name, n.Namespace);
608 for (int i=0; i<this.nodes.Length; i++) {
609 DTMXPathLinkedNode n = nodes [i];
610 sb.AppendFormat ("{0}: {1}:{2} {3} {4} {5} {6} {7}\n", new object [] {i, n.Prefix, n.LocalName, n.NamespaceURI, n.FirstNamespace, n.FirstAttribute, n.FirstChild, n.Parent});
613 for (int i=0; i<this.attributes.Length; i++) {
614 DTMXPathAttributeNode n = attributes [i];
615 sb.AppendFormat ("{0}: {1}:{2} {3} {4}\n", i, n.Prefix, n.LocalName, n.NamespaceURI, n.NextAttribute);
618 return sb.ToString ();
625 internal class XmlNamespaces
627 public const string XML
= "http://www.w3.org/XML/1998/namespace";
628 public const string XMLNS
= "http://www.w3.org/2000/xmlns/";