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/";
629 public const int IndexXML = 2;
630 public const int IndexXMLNS = 3;