2010-06-03 Jb Evain <jbevain@novell.com>
[mcs.git] / class / System.ServiceModel / Mono.Xml.XPath / DTMXPathNavigator2.cs
blobdf608748dc5fd57d229b624ba4f04a8ad1209ff3
1 //
2 // Mono.Xml.XPath.DTMXPathNavigator2
3 //
4 // Author:
5 // Atsushi Enomoto <atsushi@ximian.com>
6 //
7 // (C) 2004 Novell Inc.
8 //
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:
18 //
19 // The above copyright notice and this permission notice shall be
20 // included in all copies or substantial portions of the Software.
21 //
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.
30 using System;
31 using System.Collections;
32 using System.Text;
33 using System.Xml;
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
41 public
42 #else
43 internal
44 #endif
45 class SeekableDTMXPathNavigator2 : SeekableXPathNavigator, IXmlLineInfo
48 public SeekableDTMXPathNavigator2 (DTMXPathDocument2 document)
50 this.MoveToRoot ();
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; }
70 set {
71 int pos = (int) value;
72 if (pos < nodes.Length) {
73 currentIsAttr = false;
74 currentIsNode = true;
75 currentNode = pos;
76 } else if (pos < nodes.Length + attributes.Length) {
77 currentIsAttr = true;
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;
84 } else
85 throw new ArgumentOutOfRangeException ();
89 public override XPathNodeType GetNodeType (long nodePosition)
91 return
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];
104 default:
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];
116 default:
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];
128 default:
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];
140 default:
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);
161 if (link1 == link2)
162 // on the same element. Comparison could be done numerically.
163 return firstPosition < secondPosition ? XmlNodeOrder.Before :
164 XmlNodeOrder.After;
165 else
166 // on different linked nodes.
167 return link1 < link2 ?
168 XmlNodeOrder.Before :
169 XmlNodeOrder.After;
172 #endregion
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; }
197 // ID table
198 Hashtable idTable {
199 get { return document.IdTable; }
202 bool currentIsNode;
203 bool currentIsAttr;
205 int currentNode;
206 int currentAttr;
207 int currentNs;
209 #region Properties
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 {
228 get {
229 return currentIsAttr ? attributes [currentAttr].LineNumber :
230 nodes [currentNode].LineNumber;
234 int IXmlLineInfo.LinePosition {
235 get {
236 return currentIsAttr ? attributes [currentAttr].LinePosition :
237 nodes [currentNode].LinePosition;
241 public override string LocalName {
242 get {
243 if (currentIsNode)
244 return atomicStringPool [nodes [currentNode].LocalName];
245 else if (currentIsAttr)
246 return atomicStringPool [attributes [currentAttr].LocalName];
247 else
248 return atomicStringPool [namespaces [currentNs].Name];
252 // It maybe scarcely used, so I decided to compute it always.
253 public override string Name {
254 get {
255 string prefix;
256 string localName;
257 if (currentIsNode) {
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];
263 } else
264 return atomicStringPool [namespaces [currentNs].Name];
266 if (prefix != "")
267 return prefix + ':' + localName;
268 else
269 return localName;
273 public override string NamespaceURI {
274 get {
275 if (currentIsNode)
276 return atomicStringPool [nodes [currentNode].NamespaceURI];
277 if (currentIsAttr)
278 return atomicStringPool [attributes [currentAttr].NamespaceURI];
279 return String.Empty;
283 public override XmlNameTable NameTable {
284 get { return nameTable; }
287 public override XPathNodeType NodeType {
288 get {
289 if (currentIsNode)
290 return nodes [currentNode].NodeType;
291 else if (currentIsAttr)
292 return XPathNodeType.Attribute;
293 else
294 return XPathNodeType.Namespace;
298 public override string Prefix {
299 get {
300 if (currentIsNode)
301 return atomicStringPool [nodes [currentNode].Prefix];
302 else if (currentIsAttr)
303 return atomicStringPool [attributes [currentAttr].Prefix];
304 return String.Empty;
308 public override string Value {
309 get {
310 if (currentIsAttr)
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;
326 if (iter == 0)
327 return String.Empty;
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;
338 if (end == 0) {
339 int tmp = currentNode;
340 do {
341 tmp = nodes [tmp].Parent;
342 end = nodes [tmp].NextSibling;
343 } while (end == 0 && tmp != 0);
344 if (end == 0)
345 end = nodes.Length;
348 while (iter < end) {
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]);
356 break;
358 iter++;
362 public override string XmlLang {
363 get { return atomicStringPool [nodes [currentNode].XmlLang]; }
366 #endregion
368 #region Methods
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;
395 else
396 return XmlNodeOrder.Same;
397 } else
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;
405 else
406 return XmlNodeOrder.Same;
407 } else
408 return XmlNodeOrder.Before;
409 } else
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;
417 while (cur != 0) {
418 if (atomicStringPool [attributes [cur].LocalName] == localName && atomicStringPool [attributes [cur].NamespaceURI] == namespaceURI)
419 return cur;
420 cur = attributes [cur].NextAttribute;
423 return 0;
426 public override string GetAttribute (string localName,
427 string namespaceURI)
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;
443 return String.Empty;
446 bool IXmlLineInfo.HasLineInfo ()
448 return true;
451 public override bool IsDescendant (XPathNavigator nav)
453 SeekableDTMXPathNavigator2 another = nav as SeekableDTMXPathNavigator2;
455 if (another == null || another.document != this.document)
456 return false;
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)
464 return false;
466 while (tmp != 0) {
467 if (tmp == currentNode)
468 return true;
469 tmp = nodes [tmp].Parent;
471 return false;
474 public override bool IsSamePosition (XPathNavigator other)
476 SeekableDTMXPathNavigator2 another = other as SeekableDTMXPathNavigator2;
478 if (another == null || another.document != this.document)
479 return false;
481 if (this.currentNode != another.currentNode ||
482 this.currentIsAttr != another.currentIsAttr ||
483 this.currentIsNode != another.currentIsNode)
484 return false;
486 if (currentIsAttr)
487 return this.currentAttr == another.currentAttr;
488 else if (!currentIsNode)
489 return this.currentNs == another.currentNs;
490 return true;
493 public override bool MoveTo (XPathNavigator other)
495 SeekableDTMXPathNavigator2 another = other as SeekableDTMXPathNavigator2;
497 if (another == null || another.document != this.document)
498 return false;
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;
505 return true;
508 public override bool MoveToAttribute (string localName,
509 string namespaceURI)
511 int attr = findAttribute (localName, namespaceURI);
512 if (attr == 0)
513 return false;
515 currentAttr = attr;
516 currentIsAttr = true;
517 currentIsNode = false;
518 return true;
521 public override bool MoveToFirst ()
523 if (currentIsAttr)
524 return false;
526 int cur = nodes [currentNode].PreviousSibling;
527 if (cur == 0)
528 return false;
530 cur = nodes [cur].Parent;
531 cur = nodes [cur].FirstChild;
532 currentNode = cur;
533 currentIsNode = true;
534 return true;
537 public override bool MoveToFirstAttribute ()
539 if (!currentIsNode)
540 return false;
542 int first = nodes [currentNode].FirstAttribute;
543 if (first == 0)
544 return false;
546 currentAttr = first;
547 currentIsAttr = true;
548 currentIsNode = false;
549 return true;
552 public override bool MoveToFirstChild ()
554 if (!currentIsNode)
555 return false;
557 int first = nodes [currentNode].FirstChild;
558 if (first == 0)
559 return false;
561 currentNode = first;
562 return true;
565 private bool moveToSpecifiedNamespace (int cur,
566 XPathNamespaceScope namespaceScope)
568 if (cur == 0)
569 return false;
571 if (namespaceScope == XPathNamespaceScope.Local &&
572 namespaces [cur].DeclaredElement != currentNode)
573 return false;
575 if (namespaceScope != XPathNamespaceScope.All
576 && namespaces [cur].Namespace == XmlNamespaces.IndexXML)
577 return false;
579 if (cur != 0) {
580 moveToNamespace (cur);
581 return true;
583 else
584 return false;
587 public override bool MoveToFirstNamespace (
588 XPathNamespaceScope namespaceScope)
590 if (!currentIsNode)
591 return false;
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;
604 return true;
606 else
607 return false;
610 private void moveToNamespace (int nsNode)
612 currentIsNode = currentIsAttr = false;
613 currentNs = nsNode;
616 public override bool MoveToNamespace (string name)
618 int cur = nodes [currentNode].FirstNamespace;
619 if (cur == 0)
620 return false;
622 while (cur != 0) {
623 if (atomicStringPool [namespaces [cur].Name] == name) {
624 moveToNamespace (cur);
625 return true;
627 cur = namespaces [cur].NextNamespace;
629 return false;
632 public override bool MoveToNext ()
634 if (currentIsAttr)
635 return false;
637 int next = nodes [currentNode].NextSibling;
638 if (next == 0)
639 return false;
640 currentNode = next;
641 currentIsNode = true;
642 return true;
645 public override bool MoveToNextAttribute ()
647 if (!currentIsAttr)
648 return false;
650 int next = attributes [currentAttr].NextAttribute;
651 if (next == 0)
652 return false;
653 currentAttr = next;
654 return true;
657 public override bool MoveToNextNamespace (
658 XPathNamespaceScope namespaceScope)
660 if (currentIsAttr || currentIsNode)
661 return false;
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;
672 return true;
675 int parent = nodes [currentNode].Parent;
676 if (parent == 0) // It is root itself.
677 return false;
679 currentNode = parent;
680 return true;
683 public override bool MoveToPrevious ()
685 if (currentIsAttr)
686 return false;
688 int previous = nodes [currentNode].PreviousSibling;
689 if (previous == 0)
690 return false;
691 currentNode = previous;
692 currentIsNode = true;
693 return true;
696 public override void MoveToRoot ()
698 currentNode = 1; // root is 1.
699 currentIsNode = true;
700 currentIsAttr = false;
703 #endregion
706 class XmlNamespaces
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;