add ISafeSerializationData
[mcs.git] / class / Mono.Xml.Ext / Mono.Xml.XPath / DTMXPathNavigator.cs
blob6bd8a339a0ea77eba067a35362a3318c89cdd6f3
1 //
2 // Mono.Xml.XPath.DTMXPathNavigator
3 //
4 // Author:
5 // Atsushi Enomoto (ginga@kit.hi-ho.ne.jp)
6 //
7 // (C) 2003 Atsushi Enomoto
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;
37 namespace Mono.Xml.XPath
39 #if OUTSIDE_SYSTEM_XML
40 public
41 #else
42 internal
43 #endif
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,
53 Hashtable idTable)
55 this.nodes = nodes;
56 this.attributes = attributes;
57 this.namespaces = namespaces;
58 this.idTable = idTable;
59 this.nameTable = nameTable;
61 this.MoveToRoot ();
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,
69 org.idTable)
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];
88 // ID table
89 Hashtable idTable;
91 // // Key table (considered xsd:keyref for XPath 2.0)
92 // Hashtable keyRefTable; // [string key-name] -> idTable
93 // // idTable [string value] -> int nodeId
94 #endregion
96 bool currentIsNode;
97 bool currentIsAttr;
99 int currentNode;
100 int currentAttr;
101 int currentNs;
103 StringBuilder valueBuilder;
105 #region Ctor
107 internal DTMXPathNavigator (XmlNameTable nt)
109 this.nameTable = nt;
112 #endregion
114 #region Properties
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 {
133 get {
134 return currentIsAttr ? attributes [currentAttr].LineNumber :
135 nodes [currentNode].LineNumber;
139 int IXmlLineInfo.LinePosition {
140 get {
141 return currentIsAttr ? attributes [currentAttr].LinePosition :
142 nodes [currentNode].LinePosition;
146 public override string LocalName {
147 get {
148 if (currentIsNode)
149 return nodes [currentNode].LocalName;
150 else if (currentIsAttr)
151 return attributes [currentAttr].LocalName;
152 else
153 return namespaces [currentNs].Name;
157 // It maybe scarcely used, so I decided to compute it always.
158 public override string Name {
159 get {
160 string prefix;
161 string localName;
162 if (currentIsNode) {
163 prefix = nodes [currentNode].Prefix;
164 localName = nodes [currentNode].LocalName;
165 } else if (currentIsAttr) {
166 prefix = attributes [currentAttr].Prefix;
167 localName = attributes [currentAttr].LocalName;
168 } else
169 return namespaces [currentNs].Name;
171 if (prefix != "")
172 return prefix + ':' + localName;
173 else
174 return localName;
178 public override string NamespaceURI {
179 get {
180 if (currentIsNode)
181 return nodes [currentNode].NamespaceURI;
182 if (currentIsAttr)
183 return attributes [currentAttr].NamespaceURI;
184 return String.Empty;
188 public override XmlNameTable NameTable {
189 get { return nameTable; }
192 public override XPathNodeType NodeType {
193 get {
194 if (currentIsNode)
195 return nodes [currentNode].NodeType;
196 else if (currentIsAttr)
197 return XPathNodeType.Attribute;
198 else
199 return XPathNodeType.Namespace;
203 public override string Prefix {
204 get {
205 if (currentIsNode)
206 return nodes [currentNode].Prefix;
207 else if (currentIsAttr)
208 return attributes [currentAttr].Prefix;
209 return String.Empty;
213 public override string Value {
214 get {
215 if (currentIsAttr)
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;
229 // Element
230 if (valueBuilder == null)
231 valueBuilder = new StringBuilder ();
232 else
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:
241 break;
242 default:
243 valueBuilder.Append (nodes [iter].Value);
244 break;
246 iter++;
249 return valueBuilder.ToString ();
253 public override string XmlLang {
254 get { return nodes [currentNode].XmlLang; }
257 #endregion
259 #region Methods
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;
286 else
287 return XmlNodeOrder.Same;
288 } else
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;
296 else
297 return XmlNodeOrder.Same;
298 } else
299 return XmlNodeOrder.Before;
300 } else
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;
308 while (cur != 0) {
309 if (attributes [cur].LocalName == localName && attributes [cur].NamespaceURI == namespaceURI)
310 return cur;
311 cur = attributes [cur].NextAttribute;
314 return 0;
317 public override string GetAttribute (string localName,
318 string namespaceURI)
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;
334 return String.Empty;
337 bool IXmlLineInfo.HasLineInfo ()
339 return true;
342 public override bool IsDescendant (XPathNavigator nav)
344 DTMXPathNavigator another = nav as DTMXPathNavigator;
346 if (another == null || another.document != this.document)
347 return false;
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;
354 while (tmp != 0) {
355 if (tmp == currentNode)
356 return true;
357 tmp = nodes [tmp].Parent;
359 return false;
362 public override bool IsSamePosition (XPathNavigator other)
364 DTMXPathNavigator another = other as DTMXPathNavigator;
366 if (another == null || another.document != this.document)
367 return false;
369 if (this.currentNode != another.currentNode ||
370 this.currentIsAttr != another.currentIsAttr ||
371 this.currentIsNode != another.currentIsNode)
372 return false;
374 if (currentIsAttr)
375 return this.currentAttr == another.currentAttr;
376 else if (!currentIsNode)
377 return this.currentNs == another.currentNs;
378 return true;
381 public override bool MoveTo (XPathNavigator other)
383 DTMXPathNavigator another = other as DTMXPathNavigator;
385 if (another == null || another.document != this.document)
386 return false;
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;
393 return true;
396 public override bool MoveToAttribute (string localName,
397 string namespaceURI)
399 int attr = findAttribute (localName, namespaceURI);
400 if (attr == 0)
401 return false;
403 currentAttr = attr;
404 currentIsAttr = true;
405 currentIsNode = false;
406 return true;
409 public override bool MoveToFirst ()
411 if (currentIsAttr)
412 return false;
414 int cur = nodes [currentNode].PreviousSibling;
415 if (cur == 0)
416 return false;
418 int next = cur;
419 while (next != 0) {
420 cur = next;
421 next = nodes [cur].PreviousSibling;
423 currentNode = cur;
424 currentIsNode = true;
425 return true;
428 public override bool MoveToFirstAttribute ()
430 if (!currentIsNode)
431 return false;
433 int first = nodes [currentNode].FirstAttribute;
434 if (first == 0)
435 return false;
437 currentAttr = first;
438 currentIsAttr = true;
439 currentIsNode = false;
440 return true;
443 public override bool MoveToFirstChild ()
445 if (!currentIsNode)
446 return false;
448 int first = nodes [currentNode].FirstChild;
449 if (first == 0)
450 return false;
452 currentNode = first;
453 return true;
456 private bool moveToSpecifiedNamespace (int cur,
457 XPathNamespaceScope namespaceScope)
459 if (cur == 0)
460 return false;
462 if (namespaceScope == XPathNamespaceScope.Local &&
463 namespaces [cur].DeclaredElement != currentNode)
464 return false;
466 if (namespaceScope != XPathNamespaceScope.All
467 && namespaces [cur].Namespace == XmlNamespaces.XML)
468 return false;
470 if (cur != 0) {
471 moveToNamespace (cur);
472 return true;
474 else
475 return false;
478 public override bool MoveToFirstNamespace (
479 XPathNamespaceScope namespaceScope)
481 if (!currentIsNode)
482 return false;
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;
495 return true;
497 else
498 return false;
501 private void moveToNamespace (int nsNode)
503 currentIsNode = currentIsAttr = false;
504 currentNs = nsNode;
507 public override bool MoveToNamespace (string name)
509 int cur = nodes [currentNode].FirstNamespace;
510 if (cur == 0)
511 return false;
513 while (cur != 0) {
514 if (namespaces [cur].Name == name) {
515 moveToNamespace (cur);
516 return true;
518 cur = namespaces [cur].NextNamespace;
520 return false;
523 public override bool MoveToNext ()
525 if (currentIsAttr)
526 return false;
528 int next = nodes [currentNode].NextSibling;
529 if (next == 0)
530 return false;
531 currentNode = next;
532 currentIsNode = true;
533 return true;
536 public override bool MoveToNextAttribute ()
538 if (!currentIsAttr)
539 return false;
541 int next = attributes [currentAttr].NextAttribute;
542 if (next == 0)
543 return false;
544 currentAttr = next;
545 return true;
548 public override bool MoveToNextNamespace (
549 XPathNamespaceScope namespaceScope)
551 if (currentIsAttr || currentIsNode)
552 return false;
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;
563 return true;
566 int parent = nodes [currentNode].Parent;
567 if (parent == 0) // It is root itself.
568 return false;
570 currentNode = parent;
571 return true;
574 public override bool MoveToPrevious ()
576 if (currentIsAttr)
577 return false;
579 int previous = nodes [currentNode].PreviousSibling;
580 if (previous == 0)
581 return false;
582 currentNode = previous;
583 currentIsNode = true;
584 return true;
587 public override void MoveToRoot ()
589 currentNode = 1; // root is 1.
590 currentIsNode = true;
591 currentIsAttr = false;
594 #endregion
597 public string DebugDump {
598 get {
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/";