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