2009-05-25 Atsushi Enomoto <atsushi@ximian.com>
[mono.git] / mcs / class / System.ServiceModel / Mono.Xml.XPath / DTMXPathNavigator2.cs
1 //
2 // Mono.Xml.XPath.DTMXPathNavigator2
3 //
4 // Author:
5 //      Atsushi Enomoto <atsushi@ximian.com>
6 //
7 // (C) 2004 Novell Inc.
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 using System.ServiceModel.Dispatcher;
37
38 namespace Mono.Xml.XPath
39 {
40 #if OUTSIDE_SYSTEM_XML
41         public
42 #else
43         internal
44 #endif
45                 class SeekableDTMXPathNavigator2 : SeekableXPathNavigator, IXmlLineInfo
46         {
47
48                 public SeekableDTMXPathNavigator2 (DTMXPathDocument2 document)
49                 {
50                         this.MoveToRoot ();
51                         this.document = document;
52                 }
53
54                 // Copy constructor including position informations.
55                 public SeekableDTMXPathNavigator2 (SeekableDTMXPathNavigator2 org)
56                 {
57                         document = org.document;
58                         currentIsNode = org.currentIsNode;
59                         currentIsAttr = org.currentIsAttr;
60
61                         currentNode = org.currentNode;
62                         currentAttr = org.currentAttr;
63                         currentNs = org.currentNs;
64                 }
65
66                 #region SeekableXPathNavigator members
67
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 ();
86                         }
87                 }
88
89                 public override XPathNodeType GetNodeType (long nodePosition)
90                 {
91                         return
92                                 nodePosition < nodes.Length ? nodes [nodePosition].NodeType :
93                                 nodePosition < nodes.Length + attributes.Length ? XPathNodeType.Attribute :
94                                 XPathNodeType.Namespace;
95                 }
96
97                 public override string GetLocalName (long nodePosition)
98                 {
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];
106                         }
107                 }
108
109                 public override string GetName (long nodePosition)
110                 {
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];
118                         }
119                 }
120
121                 public override string GetNamespace (long nodePosition)
122                 {
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];
130                         }
131                 }
132
133                 public override string GetValue (long nodePosition)
134                 {
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];
142                         }
143                 }
144
145                 int GetOwnerPosition (long position)
146                 {
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;
152                 }
153
154                 public override XmlNodeOrder ComparePosition (long firstPosition, long secondPosition)
155                 {
156                         if (firstPosition == secondPosition)
157                                 return XmlNodeOrder.Same;
158
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;
170                 }
171
172                 #endregion
173
174                 XmlNameTable nameTable {
175                         get { return document.NameTable; }
176                 }
177
178                 // Created XPathDocument. This is used to identify the origin of the navigator.
179                 DTMXPathDocument2 document;
180
181                 DTMXPathLinkedNode2 [] nodes {
182                         get { return document.Nodes; }
183                 }
184                 DTMXPathAttributeNode2 [] attributes {
185                         get { return document.Attributes; }
186                 }
187                 DTMXPathNamespaceNode2 [] namespaces {
188                         get { return document.Namespaces; }
189                 }
190                 string [] atomicStringPool {
191                         get { return document.AtomicStringPool; }
192                 }
193                 string [] nonAtomicStringPool {
194                         get { return document.NonAtomicStringPool; }
195                 }
196
197                 // ID table
198                 Hashtable idTable {
199                         get { return document.IdTable; }
200                 }
201
202                 bool currentIsNode;
203                 bool currentIsAttr;
204
205                 int currentNode;
206                 int currentAttr;
207                 int currentNs;
208
209 #region Properties
210
211                 public override string BaseURI {
212                         get { return atomicStringPool [nodes [currentNode].BaseURI]; }
213                 }
214
215                 public override bool HasAttributes {
216                         get { return currentIsNode ? nodes [currentNode].FirstAttribute != 0 : false; }
217                 }
218                 
219                 public override bool HasChildren {
220                         get { return currentIsNode ? nodes [currentNode].FirstChild != 0 : false; }
221                 }
222
223                 public override bool IsEmptyElement {
224                         get { return currentIsNode ? nodes [currentNode].IsEmptyElement : false; }
225                 }
226
227                 int IXmlLineInfo.LineNumber {
228                         get {
229                                 return currentIsAttr ? attributes [currentAttr].LineNumber :
230                                         nodes [currentNode].LineNumber;
231                         }
232                 }
233
234                 int IXmlLineInfo.LinePosition {
235                         get {
236                                 return currentIsAttr ? attributes [currentAttr].LinePosition :
237                                         nodes [currentNode].LinePosition;
238                         }
239                 }
240
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];
249                         }
250                 }
251
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];
265
266                                 if (prefix != "")
267                                         return prefix + ':' + localName;
268                                 else
269                                         return localName;
270                         }
271                 }
272
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;
280                         }
281                 }
282
283                 public override XmlNameTable NameTable {
284                         get { return nameTable; }
285                 }
286
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;
295                         }
296                 }
297
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;
305                         }
306                 }
307
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];
314                                 
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];
322                                 }
323
324                                 // Element - collect all content values
325                                 int iter = nodes [currentNode].FirstChild;
326                                 if (iter == 0)
327                                         return String.Empty;
328
329                                 StringBuilder builder = null;
330                                 BuildValue (iter, ref builder);
331                                 return builder == null ? String.Empty : builder.ToString ();
332                         }
333                 }
334
335                 void BuildValue (int iter, ref StringBuilder valueBuilder)
336                 {
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;
346                         }
347
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;
357                                 }
358                                 iter++;
359                         }
360                 }
361
362                 public override string XmlLang {
363                         get { return atomicStringPool [nodes [currentNode].XmlLang]; }
364                 }
365
366 #endregion
367
368 #region Methods
369
370                 public override XPathNavigator Clone ()
371                 {
372                         return new SeekableDTMXPathNavigator2 (this);
373                 }
374
375                 public override XmlNodeOrder ComparePosition (XPathNavigator nav)
376                 {
377                         SeekableDTMXPathNavigator2 another = nav as SeekableDTMXPathNavigator2;
378
379                         if (another == null || another.document != this.document)
380                                 return XmlNodeOrder.Unknown;
381
382                         if (currentNode > another.currentNode)
383                                 return XmlNodeOrder.After;
384                         else if (currentNode < another.currentNode)
385                                 return XmlNodeOrder.Before;
386
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;
411                 }
412
413                 private int findAttribute (string localName, string namespaceURI)
414                 {
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;
421                                 }
422                         }
423                         return 0;
424                 }
425
426                 public override string GetAttribute (string localName,
427                         string namespaceURI)
428                 {
429                         int attr = findAttribute (localName, namespaceURI);
430                         return (attr != 0) ? nonAtomicStringPool [attributes [attr].Value] : String.Empty;
431                 }
432
433                 public override string GetNamespace (string name)
434                 {
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;
441                                 }
442                         }
443                         return String.Empty;
444                 }
445
446                 bool IXmlLineInfo.HasLineInfo ()
447                 {
448                         return true;
449                 }
450
451                 public override bool IsDescendant (XPathNavigator nav)
452                 {
453                         SeekableDTMXPathNavigator2 another = nav as SeekableDTMXPathNavigator2;
454
455                         if (another == null || another.document != this.document)
456                                 return false;
457
458                         if (another.currentNode == currentNode)
459                                 return !another.currentIsNode;
460                         int tmp = nodes [another.currentNode].Parent;
461
462                         // ancestors must appear in prior on the node list.
463                         if (tmp < currentNode)
464                                 return false;
465
466                         while (tmp != 0) {
467                                 if (tmp == currentNode)
468                                         return true;
469                                 tmp = nodes [tmp].Parent;
470                         }
471                         return false;
472                 }
473
474                 public override bool IsSamePosition (XPathNavigator other)
475                 {
476                         SeekableDTMXPathNavigator2 another = other as SeekableDTMXPathNavigator2;
477
478                         if (another == null || another.document != this.document)
479                                 return false;
480
481                         if (this.currentNode != another.currentNode ||
482                                 this.currentIsAttr != another.currentIsAttr ||
483                                 this.currentIsNode != another.currentIsNode)
484                                 return false;
485
486                         if (currentIsAttr)
487                                 return this.currentAttr == another.currentAttr;
488                         else if (!currentIsNode)
489                                 return this.currentNs == another.currentNs;
490                         return true;
491                 }
492
493                 public override bool MoveTo (XPathNavigator other)
494                 {
495                         SeekableDTMXPathNavigator2 another = other as SeekableDTMXPathNavigator2;
496
497                         if (another == null || another.document != this.document)
498                                 return false;
499
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;
506                 }
507
508                 public override bool MoveToAttribute (string localName,
509                         string namespaceURI)
510                 {
511                         int attr = findAttribute (localName, namespaceURI);
512                         if (attr == 0)
513                                 return false;
514
515                         currentAttr = attr;
516                         currentIsAttr = true;
517                         currentIsNode = false;
518                         return true;
519                 }
520
521                 public override bool MoveToFirst ()
522                 {
523                         if (currentIsAttr)
524                                 return false;
525
526                         int cur = nodes [currentNode].PreviousSibling;
527                         if (cur == 0)
528                                 return false;
529
530                         cur = nodes [cur].Parent;
531                         cur = nodes [cur].FirstChild;
532                         currentNode = cur;
533                         currentIsNode = true;
534                         return true;
535                 }
536
537                 public override bool MoveToFirstAttribute ()
538                 {
539                         if (!currentIsNode)
540                                 return false;
541
542                         int first = nodes [currentNode].FirstAttribute;
543                         if (first == 0)
544                                 return false;
545
546                         currentAttr = first;
547                         currentIsAttr = true;
548                         currentIsNode = false;
549                         return true;
550                 }
551
552                 public override bool MoveToFirstChild ()
553                 {
554                         if (!currentIsNode)
555                                 return false;
556
557                         int first = nodes [currentNode].FirstChild;
558                         if (first == 0)
559                                 return false;
560
561                         currentNode = first;
562                         return true;
563                 }
564                 
565                 private bool moveToSpecifiedNamespace (int cur,
566                         XPathNamespaceScope namespaceScope)
567                 {
568                         if (cur == 0)
569                                 return false;
570
571                         if (namespaceScope == XPathNamespaceScope.Local &&
572                                         namespaces [cur].DeclaredElement != currentNode)
573                                 return false;
574
575                         if (namespaceScope != XPathNamespaceScope.All
576                                 && namespaces [cur].Namespace == XmlNamespaces.IndexXML)
577                                 return false;
578
579                         if (cur != 0) {
580                                 moveToNamespace (cur);
581                                 return true;
582                         }
583                         else
584                                 return false;
585                 }
586
587                 public override bool MoveToFirstNamespace (
588                         XPathNamespaceScope namespaceScope)
589                 {
590                         if (!currentIsNode)
591                                 return false;
592                         int cur = nodes [currentNode].FirstNamespace;
593                         return moveToSpecifiedNamespace (cur, namespaceScope);
594                 }
595
596                 // Note that this support is extension to XPathDocument.
597                 // XPathDocument does not support ID reference.
598                 public override bool MoveToId (string id)
599                 {
600                         if (idTable.ContainsKey (id)) {
601                                 currentNode = (int) idTable [id];
602                                 currentIsNode = true;
603                                 currentIsAttr = false;
604                                 return true;
605                         }
606                         else
607                                 return false;
608                 }
609
610                 private void moveToNamespace (int nsNode)
611                 {
612                         currentIsNode = currentIsAttr = false;
613                         currentNs = nsNode;
614                 }
615
616                 public override bool MoveToNamespace (string name)
617                 {
618                         int cur = nodes [currentNode].FirstNamespace;
619                         if (cur == 0)
620                                 return false;
621
622                         while (cur != 0) {
623                                 if (atomicStringPool [namespaces [cur].Name] == name) {
624                                         moveToNamespace (cur);
625                                         return true;
626                                 }
627                                 cur = namespaces [cur].NextNamespace;
628                         }
629                         return false;
630                 }
631
632                 public override bool MoveToNext ()
633                 {
634                         if (currentIsAttr)
635                                 return false;
636
637                         int next = nodes [currentNode].NextSibling;
638                         if (next == 0)
639                                 return false;
640                         currentNode = next;
641                         currentIsNode = true;
642                         return true;
643                 }
644
645                 public override bool MoveToNextAttribute ()
646                 {
647                         if (!currentIsAttr)
648                                 return false;
649
650                         int next = attributes [currentAttr].NextAttribute;
651                         if (next == 0)
652                                 return false;
653                         currentAttr = next;
654                         return true;
655                 }
656
657                 public override bool MoveToNextNamespace (
658                         XPathNamespaceScope namespaceScope)
659                 {
660                         if (currentIsAttr || currentIsNode)
661                                 return false;
662
663                         int cur = namespaces [currentNs].NextNamespace;
664                         return moveToSpecifiedNamespace (cur, namespaceScope);
665                 }
666
667                 public override bool MoveToParent ()
668                 {
669                         if (!currentIsNode) {
670                                 currentIsNode = true;
671                                 currentIsAttr = false;
672                                 return true;
673                         }
674
675                         int parent = nodes [currentNode].Parent;
676                         if (parent == 0)        // It is root itself.
677                                 return false;
678
679                         currentNode = parent;
680                         return true;
681                 }
682
683                 public override bool MoveToPrevious ()
684                 {
685                         if (currentIsAttr)
686                                 return false;
687
688                         int previous = nodes [currentNode].PreviousSibling;
689                         if (previous == 0)
690                                 return false;
691                         currentNode = previous;
692                         currentIsNode = true;
693                         return true;
694                 }
695
696                 public override void MoveToRoot ()
697                 {
698                         currentNode = 1;        // root is 1.
699                         currentIsNode = true;
700                         currentIsAttr = false;
701                 }
702
703 #endregion
704         }
705
706         class XmlNamespaces
707         {
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;
712         }
713 }