2006-07-27 Atsushi Enomoto <atsushi@ximian.com>
[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                 {
49                         this.MoveToRoot ();
50                         this.document = document;
51                 }
52
53                 // Copy constructor including position informations.
54                 public DTMXPathNavigator2 (DTMXPathNavigator2 org)
55                 {
56                         document = org.document;
57                         currentIsNode = org.currentIsNode;
58                         currentIsAttr = org.currentIsAttr;
59
60                         currentNode = org.currentNode;
61                         currentAttr = org.currentAttr;
62                         currentNs = org.currentNs;
63                 }
64
65                 XmlNameTable nameTable {
66                         get { return document.NameTable; }
67                 }
68
69                 // Created XPathDocument. This is used to identify the origin of the navigator.
70                 DTMXPathDocument2 document;
71
72                 DTMXPathLinkedNode2 [] nodes {
73                         get { return document.Nodes; }
74                 }
75                 DTMXPathAttributeNode2 [] attributes {
76                         get { return document.Attributes; }
77                 }
78                 DTMXPathNamespaceNode2 [] namespaces {
79                         get { return document.Namespaces; }
80                 }
81                 string [] atomicStringPool {
82                         get { return document.AtomicStringPool; }
83                 }
84                 string [] nonAtomicStringPool {
85                         get { return document.NonAtomicStringPool; }
86                 }
87
88                 // ID table
89                 Hashtable idTable {
90                         get { return document.IdTable; }
91                 }
92
93                 bool currentIsNode;
94                 bool currentIsAttr;
95
96                 int currentNode;
97                 int currentAttr;
98                 int currentNs;
99
100 #region Properties
101
102                 public override string BaseURI {
103                         get { return atomicStringPool [nodes [currentNode].BaseURI]; }
104                 }
105
106                 public override bool HasAttributes {
107                         get { return currentIsNode ? nodes [currentNode].FirstAttribute != 0 : false; }
108                 }
109                 
110                 public override bool HasChildren {
111                         get { return currentIsNode ? nodes [currentNode].FirstChild != 0 : false; }
112                 }
113
114                 public override bool IsEmptyElement {
115                         get { return currentIsNode ? nodes [currentNode].IsEmptyElement : false; }
116                 }
117
118                 int IXmlLineInfo.LineNumber {
119                         get {
120                                 return currentIsAttr ? attributes [currentAttr].LineNumber :
121                                         nodes [currentNode].LineNumber;
122                         }
123                 }
124
125                 int IXmlLineInfo.LinePosition {
126                         get {
127                                 return currentIsAttr ? attributes [currentAttr].LinePosition :
128                                         nodes [currentNode].LinePosition;
129                         }
130                 }
131
132                 public override string LocalName {
133                         get {
134                                 if (currentIsNode)
135                                         return atomicStringPool [nodes [currentNode].LocalName];
136                                 else if (currentIsAttr)
137                                         return atomicStringPool [attributes [currentAttr].LocalName];
138                                 else
139                                         return atomicStringPool [namespaces [currentNs].Name];
140                         }
141                 }
142
143                 // It maybe scarcely used, so I decided to compute it always.
144                 public override string Name {
145                         get {
146                                 string prefix;
147                                 string localName;
148                                 if (currentIsNode) {
149                                         prefix = atomicStringPool [nodes [currentNode].Prefix];
150                                         localName = atomicStringPool [nodes [currentNode].LocalName];
151                                 } else if (currentIsAttr) {
152                                         prefix = atomicStringPool [attributes [currentAttr].Prefix];
153                                         localName = atomicStringPool [attributes [currentAttr].LocalName];
154                                 } else
155                                         return atomicStringPool [namespaces [currentNs].Name];
156
157                                 if (prefix != "")
158                                         return prefix + ':' + localName;
159                                 else
160                                         return localName;
161                         }
162                 }
163
164                 public override string NamespaceURI {
165                         get {
166                                 if (currentIsNode)
167                                         return atomicStringPool [nodes [currentNode].NamespaceURI];
168                                 if (currentIsAttr)
169                                         return atomicStringPool [attributes [currentAttr].NamespaceURI];
170                                 return String.Empty;
171                         }
172                 }
173
174                 public override XmlNameTable NameTable {
175                         get { return nameTable; }
176                 }
177
178                 public override XPathNodeType NodeType {
179                         get {
180                                 if (currentIsNode)
181                                         return nodes [currentNode].NodeType;
182                                 else if (currentIsAttr)
183                                         return XPathNodeType.Attribute;
184                                 else
185                                         return XPathNodeType.Namespace;
186                         }
187                 }
188
189                 public override string Prefix {
190                         get {
191                                 if (currentIsNode)
192                                         return atomicStringPool [nodes [currentNode].Prefix];
193                                 else if (currentIsAttr)
194                                         return atomicStringPool [attributes [currentAttr].Prefix];
195                                 return String.Empty;
196                         }
197                 }
198
199                 public override string Value {
200                         get {
201                                 if (currentIsAttr)
202                                         return nonAtomicStringPool [attributes [currentAttr].Value];
203                                 else if (!currentIsNode)
204                                         return atomicStringPool [namespaces [currentNs].Namespace];
205                                 
206                                 switch (nodes [currentNode].NodeType) {
207                                 case XPathNodeType.Comment:
208                                 case XPathNodeType.ProcessingInstruction:
209                                 case XPathNodeType.Text:
210                                 case XPathNodeType.Whitespace:
211                                 case XPathNodeType.SignificantWhitespace:
212                                         return nonAtomicStringPool [nodes [currentNode].Value];
213                                 }
214
215                                 // Element - collect all content values
216                                 int iter = nodes [currentNode].FirstChild;
217                                 if (iter == 0)
218                                         return String.Empty;
219
220                                 StringBuilder builder = null;
221                                 BuildValue (iter, ref builder);
222                                 return builder == null ? String.Empty : builder.ToString ();
223                         }
224                 }
225
226                 void BuildValue (int iter, ref StringBuilder valueBuilder)
227                 {
228                         int end = nodes [currentNode].NextSibling;
229                         if (end == 0) {
230                                 int tmp = currentNode;
231                                 do {
232                                         tmp = nodes [tmp].Parent;
233                                         end = nodes [tmp].NextSibling;
234                                 } while (end == 0 && tmp != 0);
235                                 if (end == 0)
236                                         end = nodes.Length;
237                         }
238
239                         while (iter < end) {
240                                 switch (nodes [iter].NodeType) {
241                                 case XPathNodeType.Text:
242                                 case XPathNodeType.SignificantWhitespace:
243                                 case XPathNodeType.Whitespace:
244                                         if (valueBuilder == null)
245                                                 valueBuilder = new StringBuilder ();
246                                         valueBuilder.Append (nonAtomicStringPool [nodes [iter].Value]);
247                                         break;
248                                 }
249                                 iter++;
250                         }
251                 }
252
253                 public override string XmlLang {
254                         get { return atomicStringPool [nodes [currentNode].XmlLang]; }
255                 }
256
257 #endregion
258
259 #region Methods
260
261                 public override XPathNavigator Clone ()
262                 {
263                         return new DTMXPathNavigator2 (this);
264                 }
265
266                 public override XmlNodeOrder ComparePosition (XPathNavigator nav)
267                 {
268                         DTMXPathNavigator2 another = nav as DTMXPathNavigator2;
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 (atomicStringPool [attributes [cur].LocalName] == localName && atomicStringPool [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) ? nonAtomicStringPool [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 (atomicStringPool [namespaces [nsNode].Name] == name)
330                                                 return atomicStringPool [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                         DTMXPathNavigator2 another = nav as DTMXPathNavigator2;
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                         DTMXPathNavigator2 another = other as DTMXPathNavigator2;
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                         DTMXPathNavigator2 another = other as DTMXPathNavigator2;
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.IndexXML)
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 (atomicStringPool [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 }