2 // Mono.Xml.XPath.DTMXPathNavigator2
5 // Atsushi Enomoto <atsushi@ximian.com>
7 // (C) 2004 Novell Inc.
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
;
36 using System
.ServiceModel
.Dispatcher
;
38 namespace Mono
.Xml
.XPath
40 #if OUTSIDE_SYSTEM_XML
45 class SeekableDTMXPathNavigator2
: SeekableXPathNavigator
, IXmlLineInfo
48 public SeekableDTMXPathNavigator2 (DTMXPathDocument2 document
)
51 this.document
= document
;
54 // Copy constructor including position informations.
55 public SeekableDTMXPathNavigator2 (SeekableDTMXPathNavigator2 org
)
57 document
= org
.document
;
58 currentIsNode
= org
.currentIsNode
;
59 currentIsAttr
= org
.currentIsAttr
;
61 currentNode
= org
.currentNode
;
62 currentAttr
= org
.currentAttr
;
63 currentNs
= org
.currentNs
;
66 #region SeekableXPathNavigator members
68 public override long CurrentPosition
{
69 get { return currentIsNode ? currentNode : currentIsAttr ? nodes.Length + currentAttr : nodes.Length + attributes.Length + currentNs; }
71 int pos
= (int) value;
72 if (pos
< nodes
.Length
) {
73 currentIsAttr
= false;
76 } else if (pos
< nodes
.Length
+ attributes
.Length
) {
78 currentIsNode
= false;
79 currentAttr
= pos
- nodes
.Length
;
80 } else if (pos
< nodes
.Length
+ attributes
.Length
+ namespaces
.Length
) {
81 currentIsAttr
= false;
82 currentIsNode
= false;
83 currentNs
= pos
- nodes
.Length
- attributes
.Length
;
85 throw new ArgumentOutOfRangeException ();
89 public override XPathNodeType
GetNodeType (long nodePosition
)
92 nodePosition
< nodes
.Length
? nodes
[nodePosition
].NodeType
:
93 nodePosition
< nodes
.Length
+ attributes
.Length
? XPathNodeType
.Attribute
:
94 XPathNodeType
.Namespace
;
97 public override string GetLocalName (long nodePosition
)
99 switch (GetNodeType (nodePosition
)) {
100 case XPathNodeType
.Namespace
:
101 return atomicStringPool
[namespaces
[nodePosition
- nodes
.Length
- attributes
.Length
].Name
];
102 case XPathNodeType
.Attribute
:
103 return atomicStringPool
[attributes
[nodePosition
- nodes
.Length
].LocalName
];
105 return atomicStringPool
[nodes
[nodePosition
].LocalName
];
109 public override string GetName (long nodePosition
)
111 switch (GetNodeType (nodePosition
)) {
112 case XPathNodeType
.Namespace
:
113 return atomicStringPool
[namespaces
[nodePosition
- nodes
.Length
- attributes
.Length
].Name
];
114 case XPathNodeType
.Attribute
:
115 return atomicStringPool
[attributes
[nodePosition
- nodes
.Length
].LocalName
];
117 return atomicStringPool
[nodes
[nodePosition
].LocalName
];
121 public override string GetNamespace (long nodePosition
)
123 switch (GetNodeType (nodePosition
)) {
124 case XPathNodeType
.Namespace
:
125 return atomicStringPool
[namespaces
[nodePosition
- nodes
.Length
- attributes
.Length
].Namespace
];
126 case XPathNodeType
.Attribute
:
127 return atomicStringPool
[attributes
[nodePosition
- nodes
.Length
].NamespaceURI
];
129 return atomicStringPool
[nodes
[nodePosition
].NamespaceURI
];
133 public override string GetValue (long nodePosition
)
135 switch (GetNodeType (nodePosition
)) {
136 case XPathNodeType
.Namespace
:
137 return atomicStringPool
[namespaces
[nodePosition
- nodes
.Length
- attributes
.Length
].Namespace
];
138 case XPathNodeType
.Attribute
:
139 return nonAtomicStringPool
[attributes
[nodePosition
- nodes
.Length
].Value
];
141 return nonAtomicStringPool
[nodes
[nodePosition
].Value
];
145 int GetOwnerPosition (long position
)
147 int pos
= (int) position
;
148 return pos
< nodes
.Length
? pos
:
149 pos
< nodes
.Length
+ attributes
.Length
?
150 attributes
[(int) pos
].OwnerElement
:
151 namespaces
[(int) pos
].DeclaredElement
;
154 public override XmlNodeOrder
ComparePosition (long firstPosition
, long secondPosition
)
156 if (firstPosition
== secondPosition
)
157 return XmlNodeOrder
.Same
;
159 int link1
= GetOwnerPosition (firstPosition
);
160 int link2
= GetOwnerPosition (secondPosition
);
162 // on the same element. Comparison could be done numerically.
163 return firstPosition
< secondPosition
? XmlNodeOrder
.Before
:
166 // on different linked nodes.
167 return link1
< link2
?
168 XmlNodeOrder
.Before
:
174 XmlNameTable nameTable
{
175 get { return document.NameTable; }
178 // Created XPathDocument. This is used to identify the origin of the navigator.
179 DTMXPathDocument2 document
;
181 DTMXPathLinkedNode2
[] nodes
{
182 get { return document.Nodes; }
184 DTMXPathAttributeNode2
[] attributes
{
185 get { return document.Attributes; }
187 DTMXPathNamespaceNode2
[] namespaces
{
188 get { return document.Namespaces; }
190 string [] atomicStringPool
{
191 get { return document.AtomicStringPool; }
193 string [] nonAtomicStringPool
{
194 get { return document.NonAtomicStringPool; }
199 get { return document.IdTable; }
211 public override string BaseURI
{
212 get { return atomicStringPool [nodes [currentNode].BaseURI]; }
215 public override bool HasAttributes
{
216 get { return currentIsNode ? nodes [currentNode].FirstAttribute != 0 : false; }
219 public override bool HasChildren
{
220 get { return currentIsNode ? nodes [currentNode].FirstChild != 0 : false; }
223 public override bool IsEmptyElement
{
224 get { return currentIsNode ? nodes [currentNode].IsEmptyElement : false; }
227 int IXmlLineInfo
.LineNumber
{
229 return currentIsAttr
? attributes
[currentAttr
].LineNumber
:
230 nodes
[currentNode
].LineNumber
;
234 int IXmlLineInfo
.LinePosition
{
236 return currentIsAttr
? attributes
[currentAttr
].LinePosition
:
237 nodes
[currentNode
].LinePosition
;
241 public override string LocalName
{
244 return atomicStringPool
[nodes
[currentNode
].LocalName
];
245 else if (currentIsAttr
)
246 return atomicStringPool
[attributes
[currentAttr
].LocalName
];
248 return atomicStringPool
[namespaces
[currentNs
].Name
];
252 // It maybe scarcely used, so I decided to compute it always.
253 public override string Name
{
258 prefix
= atomicStringPool
[nodes
[currentNode
].Prefix
];
259 localName
= atomicStringPool
[nodes
[currentNode
].LocalName
];
260 } else if (currentIsAttr
) {
261 prefix
= atomicStringPool
[attributes
[currentAttr
].Prefix
];
262 localName
= atomicStringPool
[attributes
[currentAttr
].LocalName
];
264 return atomicStringPool
[namespaces
[currentNs
].Name
];
267 return prefix
+ ':' + localName
;
273 public override string NamespaceURI
{
276 return atomicStringPool
[nodes
[currentNode
].NamespaceURI
];
278 return atomicStringPool
[attributes
[currentAttr
].NamespaceURI
];
283 public override XmlNameTable NameTable
{
284 get { return nameTable; }
287 public override XPathNodeType NodeType
{
290 return nodes
[currentNode
].NodeType
;
291 else if (currentIsAttr
)
292 return XPathNodeType
.Attribute
;
294 return XPathNodeType
.Namespace
;
298 public override string Prefix
{
301 return atomicStringPool
[nodes
[currentNode
].Prefix
];
302 else if (currentIsAttr
)
303 return atomicStringPool
[attributes
[currentAttr
].Prefix
];
308 public override string Value
{
311 return nonAtomicStringPool
[attributes
[currentAttr
].Value
];
312 else if (!currentIsNode
)
313 return atomicStringPool
[namespaces
[currentNs
].Namespace
];
315 switch (nodes
[currentNode
].NodeType
) {
316 case XPathNodeType
.Comment
:
317 case XPathNodeType
.ProcessingInstruction
:
318 case XPathNodeType
.Text
:
319 case XPathNodeType
.Whitespace
:
320 case XPathNodeType
.SignificantWhitespace
:
321 return nonAtomicStringPool
[nodes
[currentNode
].Value
];
324 // Element - collect all content values
325 int iter
= nodes
[currentNode
].FirstChild
;
329 StringBuilder builder
= null;
330 BuildValue (iter
, ref builder
);
331 return builder
== null ? String
.Empty
: builder
.ToString ();
335 void BuildValue (int iter
, ref StringBuilder valueBuilder
)
337 int end
= nodes
[currentNode
].NextSibling
;
339 int tmp
= currentNode
;
341 tmp
= nodes
[tmp
].Parent
;
342 end
= nodes
[tmp
].NextSibling
;
343 } while (end
== 0 && tmp
!= 0);
349 switch (nodes
[iter
].NodeType
) {
350 case XPathNodeType
.Text
:
351 case XPathNodeType
.SignificantWhitespace
:
352 case XPathNodeType
.Whitespace
:
353 if (valueBuilder
== null)
354 valueBuilder
= new StringBuilder ();
355 valueBuilder
.Append (nonAtomicStringPool
[nodes
[iter
].Value
]);
362 public override string XmlLang
{
363 get { return atomicStringPool [nodes [currentNode].XmlLang]; }
370 public override XPathNavigator
Clone ()
372 return new SeekableDTMXPathNavigator2 (this);
375 public override XmlNodeOrder
ComparePosition (XPathNavigator nav
)
377 SeekableDTMXPathNavigator2 another
= nav
as SeekableDTMXPathNavigator2
;
379 if (another
== null || another
.document
!= this.document
)
380 return XmlNodeOrder
.Unknown
;
382 if (currentNode
> another
.currentNode
)
383 return XmlNodeOrder
.After
;
384 else if (currentNode
< another
.currentNode
)
385 return XmlNodeOrder
.Before
;
387 // another may attr or ns,
388 // and this may be also attr or ns.
389 if (another
.currentIsAttr
) {
390 if (this.currentIsAttr
) {
391 if (currentAttr
> another
.currentAttr
)
392 return XmlNodeOrder
.After
;
393 else if (currentAttr
< another
.currentAttr
)
394 return XmlNodeOrder
.Before
;
396 return XmlNodeOrder
.Same
;
398 return XmlNodeOrder
.Before
;
399 } else if (!another
.currentIsNode
) {
400 if (!this.currentIsNode
) {
401 if (currentNs
> another
.currentNs
)
402 return XmlNodeOrder
.After
;
403 else if (currentNs
< another
.currentNs
)
404 return XmlNodeOrder
.Before
;
406 return XmlNodeOrder
.Same
;
408 return XmlNodeOrder
.Before
;
410 return !another
.currentIsNode
? XmlNodeOrder
.Before
: XmlNodeOrder
.Same
;
413 private int findAttribute (string localName
, string namespaceURI
)
415 if (currentIsNode
&& nodes
[currentNode
].NodeType
== XPathNodeType
.Element
) {
416 int cur
= nodes
[currentNode
].FirstAttribute
;
418 if (atomicStringPool
[attributes
[cur
].LocalName
] == localName
&& atomicStringPool
[attributes
[cur
].NamespaceURI
] == namespaceURI
)
420 cur
= attributes
[cur
].NextAttribute
;
426 public override string GetAttribute (string localName
,
429 int attr
= findAttribute (localName
, namespaceURI
);
430 return (attr
!= 0) ? nonAtomicStringPool
[attributes
[attr
].Value
] : String
.Empty
;
433 public override string GetNamespace (string name
)
435 if (currentIsNode
&& nodes
[currentNode
].NodeType
== XPathNodeType
.Element
) {
436 int nsNode
= nodes
[currentNode
].FirstNamespace
;
437 while (nsNode
!= 0) {
438 if (atomicStringPool
[namespaces
[nsNode
].Name
] == name
)
439 return atomicStringPool
[namespaces
[nsNode
].Namespace
];
440 nsNode
= namespaces
[nsNode
].NextNamespace
;
446 bool IXmlLineInfo
.HasLineInfo ()
451 public override bool IsDescendant (XPathNavigator nav
)
453 SeekableDTMXPathNavigator2 another
= nav
as SeekableDTMXPathNavigator2
;
455 if (another
== null || another
.document
!= this.document
)
458 if (another
.currentNode
== currentNode
)
459 return !another
.currentIsNode
;
460 int tmp
= nodes
[another
.currentNode
].Parent
;
462 // ancestors must appear in prior on the node list.
463 if (tmp
< currentNode
)
467 if (tmp
== currentNode
)
469 tmp
= nodes
[tmp
].Parent
;
474 public override bool IsSamePosition (XPathNavigator other
)
476 SeekableDTMXPathNavigator2 another
= other
as SeekableDTMXPathNavigator2
;
478 if (another
== null || another
.document
!= this.document
)
481 if (this.currentNode
!= another
.currentNode
||
482 this.currentIsAttr
!= another
.currentIsAttr
||
483 this.currentIsNode
!= another
.currentIsNode
)
487 return this.currentAttr
== another
.currentAttr
;
488 else if (!currentIsNode
)
489 return this.currentNs
== another
.currentNs
;
493 public override bool MoveTo (XPathNavigator other
)
495 SeekableDTMXPathNavigator2 another
= other
as SeekableDTMXPathNavigator2
;
497 if (another
== null || another
.document
!= this.document
)
500 this.currentNode
= another
.currentNode
;
501 this.currentAttr
= another
.currentAttr
;
502 this.currentNs
= another
.currentNs
;
503 this.currentIsNode
= another
.currentIsNode
;
504 this.currentIsAttr
= another
.currentIsAttr
;
508 public override bool MoveToAttribute (string localName
,
511 int attr
= findAttribute (localName
, namespaceURI
);
516 currentIsAttr
= true;
517 currentIsNode
= false;
521 public override bool MoveToFirst ()
526 int cur
= nodes
[currentNode
].PreviousSibling
;
530 cur
= nodes
[cur
].Parent
;
531 cur
= nodes
[cur
].FirstChild
;
533 currentIsNode
= true;
537 public override bool MoveToFirstAttribute ()
542 int first
= nodes
[currentNode
].FirstAttribute
;
547 currentIsAttr
= true;
548 currentIsNode
= false;
552 public override bool MoveToFirstChild ()
557 int first
= nodes
[currentNode
].FirstChild
;
565 private bool moveToSpecifiedNamespace (int cur
,
566 XPathNamespaceScope namespaceScope
)
571 if (namespaceScope
== XPathNamespaceScope
.Local
&&
572 namespaces
[cur
].DeclaredElement
!= currentNode
)
575 if (namespaceScope
!= XPathNamespaceScope
.All
576 && namespaces
[cur
].Namespace
== XmlNamespaces
.IndexXML
)
580 moveToNamespace (cur
);
587 public override bool MoveToFirstNamespace (
588 XPathNamespaceScope namespaceScope
)
592 int cur
= nodes
[currentNode
].FirstNamespace
;
593 return moveToSpecifiedNamespace (cur
, namespaceScope
);
596 // Note that this support is extension to XPathDocument.
597 // XPathDocument does not support ID reference.
598 public override bool MoveToId (string id
)
600 if (idTable
.ContainsKey (id
)) {
601 currentNode
= (int) idTable
[id
];
602 currentIsNode
= true;
603 currentIsAttr
= false;
610 private void moveToNamespace (int nsNode
)
612 currentIsNode
= currentIsAttr
= false;
616 public override bool MoveToNamespace (string name
)
618 int cur
= nodes
[currentNode
].FirstNamespace
;
623 if (atomicStringPool
[namespaces
[cur
].Name
] == name
) {
624 moveToNamespace (cur
);
627 cur
= namespaces
[cur
].NextNamespace
;
632 public override bool MoveToNext ()
637 int next
= nodes
[currentNode
].NextSibling
;
641 currentIsNode
= true;
645 public override bool MoveToNextAttribute ()
650 int next
= attributes
[currentAttr
].NextAttribute
;
657 public override bool MoveToNextNamespace (
658 XPathNamespaceScope namespaceScope
)
660 if (currentIsAttr
|| currentIsNode
)
663 int cur
= namespaces
[currentNs
].NextNamespace
;
664 return moveToSpecifiedNamespace (cur
, namespaceScope
);
667 public override bool MoveToParent ()
669 if (!currentIsNode
) {
670 currentIsNode
= true;
671 currentIsAttr
= false;
675 int parent
= nodes
[currentNode
].Parent
;
676 if (parent
== 0) // It is root itself.
679 currentNode
= parent
;
683 public override bool MoveToPrevious ()
688 int previous
= nodes
[currentNode
].PreviousSibling
;
691 currentNode
= previous
;
692 currentIsNode
= true;
696 public override void MoveToRoot ()
698 currentNode
= 1; // root is 1.
699 currentIsNode
= true;
700 currentIsAttr
= false;
708 public const string XML
= "http://www.w3.org/XML/1998/namespace";
709 public const string XMLNS
= "http://www.w3.org/2000/xmlns/";
710 public const int IndexXML
= 2;
711 public const int IndexXMLNS
= 3;