**** Merged r40732-r40872 from MCS ****
[mono.git] / mcs / class / System.XML / 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
37 namespace Mono.Xml.XPath
38 {
39 #if OUTSIDE_SYSTEM_XML
40         public
41 #else
42         internal
43 #endif
44                 class DTMXPathNavigator2 : XPathNavigator, IXmlLineInfo
45         {
46
47                 public DTMXPathNavigator2 (DTMXPathDocument2 document,
48                         XmlNameTable nameTable,
49                         DTMXPathLinkedNode2 [] nodes,
50                         DTMXPathAttributeNode2 [] attributes,
51                         DTMXPathNamespaceNode2 [] namespaces,
52                         string [] atomicStringPool,
53                         string [] nonAtomicStringPool,
54                         Hashtable idTable)
55                 {
56                         this.nodes = nodes;
57                         this.attributes = attributes;
58                         this.namespaces = namespaces;
59                         this.atomicStringPool = atomicStringPool;
60                         this.nonAtomicStringPool = nonAtomicStringPool;
61                         this.idTable = idTable;
62                         this.nameTable = nameTable;
63                         this.MoveToRoot ();
64                         this.document = document;
65                 }
66
67                 // Copy constructor including position informations.
68                 public DTMXPathNavigator2 (DTMXPathNavigator2 org)
69                         : this (org.document, org.nameTable,
70                         org.nodes, org.attributes, org.namespaces,
71                         org.atomicStringPool, org.nonAtomicStringPool,
72                         org.idTable)
73                 {
74                         currentIsNode = org.currentIsNode;
75                         currentIsAttr = org.currentIsAttr;
76
77                         currentNode = org.currentNode;
78                         currentAttr = org.currentAttr;
79                         currentNs = org.currentNs;
80                 }
81
82                 XmlNameTable nameTable;
83
84                 // Created XPathDocument. This is used to identify the origin of the navigator.
85                 DTMXPathDocument2 document;
86
87                 DTMXPathLinkedNode2 [] nodes;// = new DTMXPathLinkedNode [0];
88                 DTMXPathAttributeNode2 [] attributes;// = new DTMXPathAttributeNode [0];
89                 DTMXPathNamespaceNode2 [] namespaces;// = new DTMXPathNamespaceNode [0];
90
91                 // String pool
92                 string [] atomicStringPool;
93                 string [] nonAtomicStringPool;
94
95                 // ID table
96                 Hashtable idTable;
97
98                 bool currentIsNode;
99                 bool currentIsAttr;
100
101                 int currentNode;
102                 int currentAttr;
103                 int currentNs;
104
105                 StringBuilder valueBuilder;
106
107 #region Ctor
108
109                 internal DTMXPathNavigator2 (XmlNameTable nt)
110                 {
111                         this.nameTable = nt;
112                 }
113
114 #endregion
115
116 #region Properties
117
118                 public override string BaseURI {
119                         get { return atomicStringPool [nodes [currentNode].BaseURI]; }
120                 }
121
122                 public override bool HasAttributes {
123                         get { return currentIsNode ? nodes [currentNode].FirstAttribute != 0 : false; }
124                 }
125                 
126                 public override bool HasChildren {
127                         get { return currentIsNode ? nodes [currentNode].FirstChild != 0 : false; }
128                 }
129
130                 public override bool IsEmptyElement {
131                         get { return currentIsNode ? nodes [currentNode].IsEmptyElement : false; }
132                 }
133
134                 int IXmlLineInfo.LineNumber {
135                         get {
136                                 return currentIsAttr ? attributes [currentAttr].LineNumber :
137                                         nodes [currentNode].LineNumber;
138                         }
139                 }
140
141                 int IXmlLineInfo.LinePosition {
142                         get {
143                                 return currentIsAttr ? attributes [currentAttr].LinePosition :
144                                         nodes [currentNode].LinePosition;
145                         }
146                 }
147
148                 public override string LocalName {
149                         get {
150                                 if (currentIsNode)
151                                         return atomicStringPool [nodes [currentNode].LocalName];
152                                 else if (currentIsAttr)
153                                         return atomicStringPool [attributes [currentAttr].LocalName];
154                                 else
155                                         return atomicStringPool [namespaces [currentNs].Name];
156                         }
157                 }
158
159                 // It maybe scarcely used, so I decided to compute it always.
160                 public override string Name {
161                         get {
162                                 string prefix;
163                                 string localName;
164                                 if (currentIsNode) {
165                                         prefix = atomicStringPool [nodes [currentNode].Prefix];
166                                         localName = atomicStringPool [nodes [currentNode].LocalName];
167                                 } else if (currentIsAttr) {
168                                         prefix = atomicStringPool [attributes [currentAttr].Prefix];
169                                         localName = atomicStringPool [attributes [currentAttr].LocalName];
170                                 } else
171                                         return atomicStringPool [namespaces [currentNs].Name];
172
173                                 if (prefix != "")
174                                         return prefix + ':' + localName;
175                                 else
176                                         return localName;
177                         }
178                 }
179
180                 public override string NamespaceURI {
181                         get {
182                                 if (currentIsNode)
183                                         return atomicStringPool [nodes [currentNode].NamespaceURI];
184                                 if (currentIsAttr)
185                                         return atomicStringPool [attributes [currentAttr].NamespaceURI];
186                                 return String.Empty;
187                         }
188                 }
189
190                 public override XmlNameTable NameTable {
191                         get { return nameTable; }
192                 }
193
194                 public override XPathNodeType NodeType {
195                         get {
196                                 if (currentIsNode)
197                                         return nodes [currentNode].NodeType;
198                                 else if (currentIsAttr)
199                                         return XPathNodeType.Attribute;
200                                 else
201                                         return XPathNodeType.Namespace;
202                         }
203                 }
204
205                 public override string Prefix {
206                         get {
207                                 if (currentIsNode)
208                                         return atomicStringPool [nodes [currentNode].Prefix];
209                                 else if (currentIsAttr)
210                                         return atomicStringPool [attributes [currentAttr].Prefix];
211                                 return String.Empty;
212                         }
213                 }
214
215                 public override string Value {
216                         get {
217                                 if (currentIsAttr)
218                                         return nonAtomicStringPool [attributes [currentAttr].Value];
219                                 else if (!currentIsNode)
220                                         return atomicStringPool [namespaces [currentNs].Namespace];
221                                 
222                                 switch (nodes [currentNode].NodeType) {
223                                 case XPathNodeType.Comment:
224                                 case XPathNodeType.ProcessingInstruction:
225                                 case XPathNodeType.Text:
226                                 case XPathNodeType.Whitespace:
227                                 case XPathNodeType.SignificantWhitespace:
228                                         return nonAtomicStringPool [nodes [currentNode].Value];
229                                 }
230
231                                 // Element - collect all content values
232                                 int iter = nodes [currentNode].FirstChild;
233                                 if (iter == 0)
234                                         return String.Empty;
235
236                                 if (valueBuilder == null)
237                                         valueBuilder = new StringBuilder ();
238                                 else
239                                         valueBuilder.Length = 0;
240
241                                 int end = nodes [currentNode].NextSibling;
242                                 if (end == 0) {
243                                         int tmp = currentNode;
244                                         do {
245                                                 tmp = nodes [tmp].Parent;
246                                                 end = nodes [tmp].NextSibling;
247                                         } while (end == 0 && tmp != 0);
248                                         if (end == 0)
249                                                 end = nodes.Length;
250                                 }
251
252                                 while (iter < end) {
253                                         switch (nodes [iter].NodeType) {
254                                         case XPathNodeType.Text:
255                                         case XPathNodeType.SignificantWhitespace:
256                                         case XPathNodeType.Whitespace:
257                                                 valueBuilder.Append (nonAtomicStringPool [nodes [iter].Value]);
258                                                 break;
259                                         }
260                                         iter++;
261                                 }
262                                 
263                                 return valueBuilder.ToString ();
264                         }
265                 }
266
267                 public override string XmlLang {
268                         get { return atomicStringPool [nodes [currentNode].XmlLang]; }
269                 }
270
271 #endregion
272
273 #region Methods
274
275                 public override XPathNavigator Clone ()
276                 {
277                         return new DTMXPathNavigator2 (this);
278                 }
279
280                 public override XmlNodeOrder ComparePosition (XPathNavigator nav)
281                 {
282                         DTMXPathNavigator2 another = nav as DTMXPathNavigator2;
283
284                         if (another == null || another.document != this.document)
285                                 return XmlNodeOrder.Unknown;
286
287                         if (currentNode > another.currentNode)
288                                 return XmlNodeOrder.After;
289                         else if (currentNode < another.currentNode)
290                                 return XmlNodeOrder.Before;
291
292                         // another may attr or ns, 
293                         // and this may be also attr or ns.
294                         if (another.currentIsAttr) {
295                                 if (this.currentIsAttr) {
296                                         if (currentAttr > another.currentAttr)
297                                                 return XmlNodeOrder.After;
298                                         else if (currentAttr < another.currentAttr)
299                                                 return XmlNodeOrder.Before;
300                                         else
301                                                 return XmlNodeOrder.Same;
302                                 } else
303                                         return XmlNodeOrder.Before;
304                         } else if (!another.currentIsNode) {
305                                 if (!this.currentIsNode) {
306                                         if (currentNs > another.currentNs)
307                                                 return XmlNodeOrder.After;
308                                         else if (currentNs < another.currentNs)
309                                                 return XmlNodeOrder.Before;
310                                         else
311                                                 return XmlNodeOrder.Same;
312                                 } else
313                                         return XmlNodeOrder.Before;
314                         } else
315                                 return !another.currentIsNode ? XmlNodeOrder.Before : XmlNodeOrder.Same;
316                 }
317
318                 private int findAttribute (string localName, string namespaceURI)
319                 {
320                         if (currentIsNode && nodes [currentNode].NodeType == XPathNodeType.Element) {
321                                 int cur = nodes [currentNode].FirstAttribute;
322                                 while (cur != 0) {
323                                         if (atomicStringPool [attributes [cur].LocalName] == localName && atomicStringPool [attributes [cur].NamespaceURI] == namespaceURI)
324                                                 return cur;
325                                         cur = attributes [cur].NextAttribute;
326                                 }
327                         }
328                         return 0;
329                 }
330
331                 public override string GetAttribute (string localName,
332                         string namespaceURI)
333                 {
334                         int attr = findAttribute (localName, namespaceURI);
335                         return (attr != 0) ? nonAtomicStringPool [attributes [attr].Value] : String.Empty;
336                 }
337
338                 public override string GetNamespace (string name)
339                 {
340                         if (currentIsNode && nodes [currentNode].NodeType == XPathNodeType.Element) {
341                                 int nsNode = nodes [currentNode].FirstNamespace;
342                                 while (nsNode != 0) {
343                                         if (atomicStringPool [namespaces [nsNode].Name] == name)
344                                                 return atomicStringPool [namespaces [nsNode].Namespace];
345                                         nsNode = namespaces [nsNode].NextNamespace;
346                                 }
347                         }
348                         return String.Empty;
349                 }
350
351                 bool IXmlLineInfo.HasLineInfo ()
352                 {
353                         return true;
354                 }
355
356                 public override bool IsDescendant (XPathNavigator nav)
357                 {
358                         DTMXPathNavigator2 another = nav as DTMXPathNavigator2;
359
360                         if (another == null || another.document != this.document)
361                                 return false;
362
363                         // Maybe we can improve here more efficiently by
364                         // comparing node indices.
365                         if (another.currentNode == currentNode)
366                                 return !another.currentIsNode;
367                         int tmp = nodes [another.currentNode].Parent;
368                         while (tmp != 0) {
369                                 if (tmp == currentNode)
370                                         return true;
371                                 tmp = nodes [tmp].Parent;
372                         }
373                         return false;
374                 }
375
376                 public override bool IsSamePosition (XPathNavigator other)
377                 {
378                         DTMXPathNavigator2 another = other as DTMXPathNavigator2;
379
380                         if (another == null || another.document != this.document)
381                                 return false;
382
383                         if (this.currentNode != another.currentNode ||
384                                 this.currentIsAttr != another.currentIsAttr ||
385                                 this.currentIsNode != another.currentIsNode)
386                                 return false;
387
388                         if (currentIsAttr)
389                                 return this.currentAttr == another.currentAttr;
390                         else if (!currentIsNode)
391                                 return this.currentNs == another.currentNs;
392                         return true;
393                 }
394
395                 public override bool MoveTo (XPathNavigator other)
396                 {
397                         DTMXPathNavigator2 another = other as DTMXPathNavigator2;
398
399                         if (another == null || another.document != this.document)
400                                 return false;
401
402                         this.currentNode = another.currentNode;
403                         this.currentAttr = another.currentAttr;
404                         this.currentNs = another.currentNs;
405                         this.currentIsNode = another.currentIsNode;
406                         this.currentIsAttr = another.currentIsAttr;
407                         return true;
408                 }
409
410                 public override bool MoveToAttribute (string localName,
411                         string namespaceURI)
412                 {
413                         int attr = findAttribute (localName, namespaceURI);
414                         if (attr == 0)
415                                 return false;
416
417                         currentAttr = attr;
418                         currentIsAttr = true;
419                         currentIsNode = false;
420                         return true;
421                 }
422
423                 public override bool MoveToFirst ()
424                 {
425                         if (currentIsAttr)
426                                 return false;
427
428                         int cur = nodes [currentNode].PreviousSibling;
429                         if (cur == 0)
430                                 return false;
431
432                         int next = cur;
433                         while (next != 0) {
434                                 cur = next;
435                                 next = nodes [cur].PreviousSibling;
436                         }
437                         currentNode = cur;
438                         currentIsNode = true;
439                         return true;
440                 }
441
442                 public override bool MoveToFirstAttribute ()
443                 {
444                         if (!currentIsNode)
445                                 return false;
446
447                         int first = nodes [currentNode].FirstAttribute;
448                         if (first == 0)
449                                 return false;
450
451                         currentAttr = first;
452                         currentIsAttr = true;
453                         currentIsNode = false;
454                         return true;
455                 }
456
457                 public override bool MoveToFirstChild ()
458                 {
459                         if (!currentIsNode)
460                                 return false;
461
462                         int first = nodes [currentNode].FirstChild;
463                         if (first == 0)
464                                 return false;
465
466                         currentNode = first;
467                         return true;
468                 }
469                 
470                 private bool moveToSpecifiedNamespace (int cur,
471                         XPathNamespaceScope namespaceScope)
472                 {
473                         if (cur == 0)
474                                 return false;
475
476                         if (namespaceScope == XPathNamespaceScope.Local &&
477                                         namespaces [cur].DeclaredElement != currentNode)
478                                 return false;
479
480                         if (namespaceScope != XPathNamespaceScope.All
481                                 && namespaces [cur].Namespace == XmlNamespaces.IndexXML)
482                                 return false;
483
484                         if (cur != 0) {
485                                 moveToNamespace (cur);
486                                 return true;
487                         }
488                         else
489                                 return false;
490                 }
491
492                 public override bool MoveToFirstNamespace (
493                         XPathNamespaceScope namespaceScope)
494                 {
495                         if (!currentIsNode)
496                                 return false;
497                         int cur = nodes [currentNode].FirstNamespace;
498                         return moveToSpecifiedNamespace (cur, namespaceScope);
499                 }
500
501                 // Note that this support is extension to XPathDocument.
502                 // XPathDocument does not support ID reference.
503                 public override bool MoveToId (string id)
504                 {
505                         if (idTable.ContainsKey (id)) {
506                                 currentNode = (int) idTable [id];
507                                 currentIsNode = true;
508                                 currentIsAttr = false;
509                                 return true;
510                         }
511                         else
512                                 return false;
513                 }
514
515                 private void moveToNamespace (int nsNode)
516                 {
517                         currentIsNode = currentIsAttr = false;
518                         currentNs = nsNode;
519                 }
520
521                 public override bool MoveToNamespace (string name)
522                 {
523                         int cur = nodes [currentNode].FirstNamespace;
524                         if (cur == 0)
525                                 return false;
526
527                         while (cur != 0) {
528                                 if (atomicStringPool [namespaces [cur].Name] == name) {
529                                         moveToNamespace (cur);
530                                         return true;
531                                 }
532                                 cur = namespaces [cur].NextNamespace;
533                         }
534                         return false;
535                 }
536
537                 public override bool MoveToNext ()
538                 {
539                         if (currentIsAttr)
540                                 return false;
541
542                         int next = nodes [currentNode].NextSibling;
543                         if (next == 0)
544                                 return false;
545                         currentNode = next;
546                         currentIsNode = true;
547                         return true;
548                 }
549
550                 public override bool MoveToNextAttribute ()
551                 {
552                         if (!currentIsAttr)
553                                 return false;
554
555                         int next = attributes [currentAttr].NextAttribute;
556                         if (next == 0)
557                                 return false;
558                         currentAttr = next;
559                         return true;
560                 }
561
562                 public override bool MoveToNextNamespace (
563                         XPathNamespaceScope namespaceScope)
564                 {
565                         if (currentIsAttr || currentIsNode)
566                                 return false;
567
568                         int cur = namespaces [currentNs].NextNamespace;
569                         return moveToSpecifiedNamespace (cur, namespaceScope);
570                 }
571
572                 public override bool MoveToParent ()
573                 {
574                         if (!currentIsNode) {
575                                 currentIsNode = true;
576                                 currentIsAttr = false;
577                                 return true;
578                         }
579
580                         int parent = nodes [currentNode].Parent;
581                         if (parent == 0)        // It is root itself.
582                                 return false;
583
584                         currentNode = parent;
585                         return true;
586                 }
587
588                 public override bool MoveToPrevious ()
589                 {
590                         if (currentIsAttr)
591                                 return false;
592
593                         int previous = nodes [currentNode].PreviousSibling;
594                         if (previous == 0)
595                                 return false;
596                         currentNode = previous;
597                         currentIsNode = true;
598                         return true;
599                 }
600
601                 public override void MoveToRoot ()
602                 {
603                         currentNode = 1;        // root is 1.
604                         currentIsNode = true;
605                         currentIsAttr = false;
606                 }
607
608 #endregion
609         }
610 }