2005-01-31 Zoltan Varga <vargaz@freemail.hu>
[mono.git] / mcs / class / System.XML / Mono.Xml.XPath / DTMXPathNavigator.cs
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 //
9
10 //
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.
29 //
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
37 namespace Mono.Xml.XPath
38 {
39 #if OUTSIDE_SYSTEM_XML
40         public
41 #else
42         internal
43 #endif
44                 class DTMXPathNavigator : XPathNavigator, IXmlLineInfo
45         {
46
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)
54                 {
55                         this.nodes = nodes;
56                         this.attributes = attributes;
57                         this.namespaces = namespaces;
58                         this.idTable = idTable;
59                         this.nameTable = nameTable;
60
61                         this.MoveToRoot ();
62                         this.document = document;
63                 }
64
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)
70                 {
71                         currentIsNode = org.currentIsNode;
72                         currentIsAttr = org.currentIsAttr;
73
74                         currentNode = org.currentNode;
75                         currentAttr = org.currentAttr;
76                         currentNs = org.currentNs;
77                 }
78
79                 XmlNameTable nameTable;
80
81                 // Created XPathDocument. This is used to identify the origin of the navigator.
82                 DTMXPathDocument document;
83
84                 DTMXPathLinkedNode [] nodes;// = new DTMXPathLinkedNode [0];
85                 DTMXPathAttributeNode [] attributes;// = new DTMXPathAttributeNode [0];
86                 DTMXPathNamespaceNode [] namespaces;// = new DTMXPathNamespaceNode [0];
87
88                 // ID table
89                 Hashtable idTable;
90
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
95
96                 bool currentIsNode;
97                 bool currentIsAttr;
98
99                 int currentNode;
100                 int currentAttr;
101                 int currentNs;
102
103                 StringBuilder valueBuilder;
104
105 #region Ctor
106
107                 internal DTMXPathNavigator (XmlNameTable nt)
108                 {
109                         this.nameTable = nt;
110                 }
111
112 #endregion
113
114 #region Properties
115
116                 public override string BaseURI {
117                         get { return nodes [currentNode].BaseURI; }
118                 }
119
120                 public override bool HasAttributes {
121                         get { return currentIsNode ? nodes [currentNode].FirstAttribute != 0 : false; }
122                 }
123                 
124                 public override bool HasChildren {
125                         get { return currentIsNode ? nodes [currentNode].FirstChild != 0 : false; }
126                 }
127
128                 public override bool IsEmptyElement {
129                         get { return currentIsNode ? nodes [currentNode].IsEmptyElement : false; }
130                 }
131
132                 int IXmlLineInfo.LineNumber {
133                         get {
134                                 return currentIsAttr ? attributes [currentAttr].LineNumber :
135                                         nodes [currentNode].LineNumber;
136                         }
137                 }
138
139                 int IXmlLineInfo.LinePosition {
140                         get {
141                                 return currentIsAttr ? attributes [currentAttr].LinePosition :
142                                         nodes [currentNode].LinePosition;
143                         }
144                 }
145
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;
154                         }
155                 }
156
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;
170
171                                 if (prefix != "")
172                                         return prefix + ':' + localName;
173                                 else
174                                         return localName;
175                         }
176                 }
177
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;
185                         }
186                 }
187
188                 public override XmlNameTable NameTable {
189                         get { return nameTable; }
190                 }
191
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;
200                         }
201                 }
202
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;
210                         }
211                 }
212
213                 public override string Value {
214                         get {
215                                 if (currentIsAttr)
216                                         return attributes [currentAttr].Value;
217                                 else if (!currentIsNode)
218                                         return namespaces [currentNs].Namespace;
219                                 
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;
227                                 }
228
229                                 // Element
230                                 if (valueBuilder == null)
231                                         valueBuilder = new StringBuilder ();
232                                 else
233                                         valueBuilder.Length = 0;
234                                 
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;
245                                         }
246                                         iter++;
247                                 }
248                                 
249                                 return valueBuilder.ToString ();
250                         }
251                 }
252
253                 public override string XmlLang {
254                         get { return nodes [currentNode].XmlLang; }
255                 }
256
257 #endregion
258
259 #region Methods
260
261                 public override XPathNavigator Clone ()
262                 {
263                         return new DTMXPathNavigator (this);
264                 }
265
266                 public override XmlNodeOrder ComparePosition (XPathNavigator nav)
267                 {
268                         DTMXPathNavigator another = nav as DTMXPathNavigator;
269
270                         if (another == null || another.document != this.document)
271                                 return XmlNodeOrder.Unknown;
272
273                         if (currentNode > another.currentNode)
274                                 return XmlNodeOrder.After;
275                         else if (currentNode < another.currentNode)
276                                 return XmlNodeOrder.Before;
277
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;
302                 }
303
304                 private int findAttribute (string localName, string namespaceURI)
305                 {
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;
312                                 }
313                         }
314                         return 0;
315                 }
316
317                 public override string GetAttribute (string localName,
318                         string namespaceURI)
319                 {
320                         int attr = findAttribute (localName, namespaceURI);
321                         return (attr != 0) ? attributes [attr].Value : String.Empty;
322                 }
323
324                 public override string GetNamespace (string name)
325                 {
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;
332                                 }
333                         }
334                         return String.Empty;
335                 }
336
337                 bool IXmlLineInfo.HasLineInfo ()
338                 {
339                         return true;
340                 }
341
342                 public override bool IsDescendant (XPathNavigator nav)
343                 {
344                         DTMXPathNavigator another = nav as DTMXPathNavigator;
345
346                         if (another == null || another.document != this.document)
347                                 return false;
348
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;
358                         }
359                         return false;
360                 }
361
362                 public override bool IsSamePosition (XPathNavigator other)
363                 {
364                         DTMXPathNavigator another = other as DTMXPathNavigator;
365
366                         if (another == null || another.document != this.document)
367                                 return false;
368
369                         if (this.currentNode != another.currentNode ||
370                                 this.currentIsAttr != another.currentIsAttr ||
371                                 this.currentIsNode != another.currentIsNode)
372                                 return false;
373
374                         if (currentIsAttr)
375                                 return this.currentAttr == another.currentAttr;
376                         else if (!currentIsNode)
377                                 return this.currentNs == another.currentNs;
378                         return true;
379                 }
380
381                 public override bool MoveTo (XPathNavigator other)
382                 {
383                         DTMXPathNavigator another = other as DTMXPathNavigator;
384
385                         if (another == null || another.document != this.document)
386                                 return false;
387
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;
394                 }
395
396                 public override bool MoveToAttribute (string localName,
397                         string namespaceURI)
398                 {
399                         int attr = findAttribute (localName, namespaceURI);
400                         if (attr == 0)
401                                 return false;
402
403                         currentAttr = attr;
404                         currentIsAttr = true;
405                         currentIsNode = false;
406                         return true;
407                 }
408
409                 public override bool MoveToFirst ()
410                 {
411                         if (currentIsAttr)
412                                 return false;
413
414                         int cur = nodes [currentNode].PreviousSibling;
415                         if (cur == 0)
416                                 return false;
417
418                         int next = cur;
419                         while (next != 0) {
420                                 cur = next;
421                                 next = nodes [cur].PreviousSibling;
422                         }
423                         currentNode = cur;
424                         currentIsNode = true;
425                         return true;
426                 }
427
428                 public override bool MoveToFirstAttribute ()
429                 {
430                         if (!currentIsNode)
431                                 return false;
432
433                         int first = nodes [currentNode].FirstAttribute;
434                         if (first == 0)
435                                 return false;
436
437                         currentAttr = first;
438                         currentIsAttr = true;
439                         currentIsNode = false;
440                         return true;
441                 }
442
443                 public override bool MoveToFirstChild ()
444                 {
445                         if (!currentIsNode)
446                                 return false;
447
448                         int first = nodes [currentNode].FirstChild;
449                         if (first == 0)
450                                 return false;
451
452                         currentNode = first;
453                         return true;
454                 }
455                 
456                 private bool moveToSpecifiedNamespace (int cur,
457                         XPathNamespaceScope namespaceScope)
458                 {
459                         if (cur == 0)
460                                 return false;
461
462                         if (namespaceScope == XPathNamespaceScope.Local &&
463                                         namespaces [cur].DeclaredElement != currentNode)
464                                 return false;
465
466                         if (namespaceScope != XPathNamespaceScope.All
467                                 && namespaces [cur].Namespace == XmlNamespaces.XML)
468                                 return false;
469
470                         if (cur != 0) {
471                                 moveToNamespace (cur);
472                                 return true;
473                         }
474                         else
475                                 return false;
476                 }
477
478                 public override bool MoveToFirstNamespace (
479                         XPathNamespaceScope namespaceScope)
480                 {
481                         if (!currentIsNode)
482                                 return false;
483                         int cur = nodes [currentNode].FirstNamespace;
484                         return moveToSpecifiedNamespace (cur, namespaceScope);
485                 }
486
487                 // Note that this support is extension to XPathDocument.
488                 // XPathDocument does not support ID reference.
489                 public override bool MoveToId (string id)
490                 {
491                         if (idTable.ContainsKey (id)) {
492                                 currentNode = (int) idTable [id];
493                                 currentIsNode = true;
494                                 currentIsAttr = false;
495                                 return true;
496                         }
497                         else
498                                 return false;
499                 }
500
501                 private void moveToNamespace (int nsNode)
502                 {
503                         currentIsNode = currentIsAttr = false;
504                         currentNs = nsNode;
505                 }
506
507                 public override bool MoveToNamespace (string name)
508                 {
509                         int cur = nodes [currentNode].FirstNamespace;
510                         if (cur == 0)
511                                 return false;
512
513                         while (cur != 0) {
514                                 if (namespaces [cur].Name == name) {
515                                         moveToNamespace (cur);
516                                         return true;
517                                 }
518                                 cur = namespaces [cur].NextNamespace;
519                         }
520                         return false;
521                 }
522
523                 public override bool MoveToNext ()
524                 {
525                         if (currentIsAttr)
526                                 return false;
527
528                         int next = nodes [currentNode].NextSibling;
529                         if (next == 0)
530                                 return false;
531                         currentNode = next;
532                         currentIsNode = true;
533                         return true;
534                 }
535
536                 public override bool MoveToNextAttribute ()
537                 {
538                         if (!currentIsAttr)
539                                 return false;
540
541                         int next = attributes [currentAttr].NextAttribute;
542                         if (next == 0)
543                                 return false;
544                         currentAttr = next;
545                         return true;
546                 }
547
548                 public override bool MoveToNextNamespace (
549                         XPathNamespaceScope namespaceScope)
550                 {
551                         if (currentIsAttr || currentIsNode)
552                                 return false;
553
554                         int cur = namespaces [currentNs].NextNamespace;
555                         return moveToSpecifiedNamespace (cur, namespaceScope);
556                 }
557
558                 public override bool MoveToParent ()
559                 {
560                         if (!currentIsNode) {
561                                 currentIsNode = true;
562                                 currentIsAttr = false;
563                                 return true;
564                         }
565
566                         int parent = nodes [currentNode].Parent;
567                         if (parent == 0)        // It is root itself.
568                                 return false;
569
570                         currentNode = parent;
571                         return true;
572                 }
573
574                 public override bool MoveToPrevious ()
575                 {
576                         if (currentIsAttr)
577                                 return false;
578
579                         int previous = nodes [currentNode].PreviousSibling;
580                         if (previous == 0)
581                                 return false;
582                         currentNode = previous;
583                         currentIsNode = true;
584                         return true;
585                 }
586
587                 public override void MoveToRoot ()
588                 {
589                         currentNode = 1;        // root is 1.
590                         currentIsNode = true;
591                         currentIsAttr = false;
592                 }
593
594 #endregion
595
596                 /*
597                 public string DebugDump {
598                         get {
599                                 StringBuilder sb = new StringBuilder ();
600
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);
606                                 }
607
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});
611                                 }
612
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);
616                                 }
617
618                                 return sb.ToString ();
619                         }
620                 }
621                 */
622
623         }
624
625         internal class XmlNamespaces
626         {
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;
631         }
632 }