2004-02-08 Atsushi Enomoto <atsushi@ximian.com>
[mono.git] / mcs / class / System.XML / System.Xml / XmlDocumentNavigator.cs
1 //
2 // System.Xml.XmlDocumentNavigator
3 //
4 // Authors:
5 //   Jason Diamond <jason@injektilo.org>
6 //   Atsushi Enomoto <ginga@kit.hi-ho.ne.jp>
7 //
8 // (C) 2002 Jason Diamond
9 // (C) 2003 Atsushi Enomoto
10 //
11
12 using System;
13 using System.Collections;
14 using System.Xml;
15 using System.Xml.XPath;
16
17 namespace System.Xml
18 {
19         internal class XmlDocumentNavigator : XPathNavigator, IHasXmlNode
20         {
21                 #region Constructors
22
23                 internal XmlDocumentNavigator(XmlNode node)
24                 {
25                         this.node = node;
26                         this.document = node.NodeType == XmlNodeType.Document ?
27                                 node as XmlDocument : node.OwnerDocument;
28                 }
29
30                 #endregion
31
32                 #region Fields
33                 private const string Xmlns = "http://www.w3.org/2000/xmlns/";
34                 private const string XmlnsXML = "http://www.w3.org/XML/1998/namespace";
35
36                 private XmlNode node;
37                 private XmlDocument document;
38                 // Current namespace node (ancestor's attribute of current node).
39                 private XmlNode nsNode;
40
41                 #endregion
42
43                 #region Properties
44
45                 public override string BaseURI {
46                         get {
47                                 return node.BaseURI;
48                         }
49                 }
50
51                 public override bool HasAttributes {
52                         get {
53                                 if (nsNode != null)
54                                         return false;
55
56                                 if (node.Attributes != null)
57                                         for (int i = 0; i < node.Attributes.Count; i++)
58                                                 if (node.Attributes [i].NamespaceURI != Xmlns)
59                                                         return true;
60                                 return false;
61                         }
62                 }
63
64                 public override bool HasChildren {
65                         get {
66                                 if (nsNode != null)
67                                         return false;
68
69                                 XPathNodeType nodeType = NodeType;
70                                 bool canHaveChildren = nodeType == XPathNodeType.Root || nodeType == XPathNodeType.Element;
71                                 return canHaveChildren && node.FirstChild != null;
72                         }
73                 }
74
75                 public override bool IsEmptyElement {
76                         get {
77                                 if (nsNode != null)
78                                         return false;
79
80                                 return node.NodeType == XmlNodeType.Element 
81                                         && ((XmlElement) node).IsEmpty;
82                         }
83                 }
84
85                 public override string LocalName {
86                         get {
87                                 if (nsNode != null) {
88                                         if (nsNode == document)
89                                                 return "xml";
90                                         else
91                                                 return (nsNode.Name == "xmlns") ? String.Empty : nsNode.LocalName;
92                                 }
93
94                                 XPathNodeType nodeType = NodeType;
95                                 bool canHaveName = 
96                                         nodeType == XPathNodeType.Element || 
97                                         nodeType == XPathNodeType.Attribute || 
98                                         nodeType == XPathNodeType.ProcessingInstruction ||
99                                         nodeType == XPathNodeType.Namespace;
100                                 return canHaveName ? node.LocalName : String.Empty;
101                         }
102                 }
103
104                 public override string Name {
105                         get {
106                                 if (nsNode != null)
107                                         return LocalName;
108
109                                 XPathNodeType nodeType = NodeType;
110                                 bool canHaveName = 
111                                         nodeType == XPathNodeType.Element || 
112                                         nodeType == XPathNodeType.Attribute || 
113                                         nodeType == XPathNodeType.ProcessingInstruction ||
114                                         nodeType == XPathNodeType.Namespace;
115                                 return canHaveName ? node.Name : String.Empty;
116                         }
117                 }
118
119                 public override string NamespaceURI {
120                         get { return (nsNode != null) ? String.Empty : node.NamespaceURI; }
121                 }
122
123                 public override XmlNameTable NameTable {
124                         get {
125                                 return document.NameTable;
126                         }
127                 }
128
129                 public override XPathNodeType NodeType {
130                         get { return (nsNode != null) ? XPathNodeType.Namespace : node.XPathNodeType; }
131                 }
132
133                 public override string Prefix {
134                         get { return (nsNode != null) ? String.Empty : node.Prefix; }
135                 }
136
137                 public override string Value {
138                         get {
139                                 switch (NodeType) {
140                                 case XPathNodeType.Attribute:
141                                 case XPathNodeType.Comment:
142                                 case XPathNodeType.ProcessingInstruction:
143                                 case XPathNodeType.Text:
144                                 case XPathNodeType.Whitespace:
145                                 case XPathNodeType.SignificantWhitespace:
146                                         return node.Value;
147                                 case XPathNodeType.Element:
148                                 case XPathNodeType.Root:
149                                         return node.InnerText;
150                                 case XPathNodeType.Namespace:
151                                         return nsNode == document ? XmlnsXML : nsNode.Value;
152                                 }
153                                 return String.Empty;
154                         }
155                 }
156
157                 public override string XmlLang {
158                         get {
159                                 return node.XmlLang;
160                         }
161                 }
162
163                 #endregion
164
165                 #region Methods
166
167                 public override XPathNavigator Clone ()
168                 {
169                         XmlDocumentNavigator clone = new XmlDocumentNavigator (node);
170                         clone.nsNode = nsNode;
171                         return clone;
172                 }
173
174                 public override string GetAttribute (string localName, string namespaceURI)
175                 {
176                         if (HasAttributes) {
177                                 XmlElement el = Node as XmlElement;
178                                 return el != null ? el.GetAttribute (localName, namespaceURI) : String.Empty;
179                         }
180                         return String.Empty;
181                 }
182
183                 public override string GetNamespace (string name)
184                 {
185                         // MSDN says "String.Empty if a matching namespace 
186                         // node is not found or if the navigator is not 
187                         // positioned on an element node", but in fact it
188                         // returns actual namespace for the other nodes.
189                         return Node.GetNamespaceOfPrefix (name);
190                 }
191                 
192                 public override bool IsSamePosition (XPathNavigator other)
193                 {
194                         XmlDocumentNavigator otherDocumentNavigator = other as XmlDocumentNavigator;
195                         if (otherDocumentNavigator != null)
196                                 return node == otherDocumentNavigator.node
197                                         && nsNode == otherDocumentNavigator.nsNode;
198                         return false;
199                 }
200
201                 public override bool MoveTo (XPathNavigator other)
202                 {
203                         XmlDocumentNavigator otherDocumentNavigator = other as XmlDocumentNavigator;
204                         if (otherDocumentNavigator != null) {
205                                 if (document == otherDocumentNavigator.document) {
206                                         node = otherDocumentNavigator.node;
207                                         nsNode = otherDocumentNavigator.nsNode;
208                                         return true;
209                                 }
210                         }
211                         return false;
212                 }
213
214                 public override bool MoveToAttribute (string localName, string namespaceURI)
215                 {
216                         if (node.Attributes != null) {
217                                 for (int i = 0; i < node.Attributes.Count; i++) {
218                                         XmlAttribute attr = node.Attributes [i];
219                                         if (attr.LocalName == localName
220                                                 && attr.NamespaceURI == namespaceURI) {
221                                                 node = attr;
222                                                 nsNode = null;
223                                                 return true;
224                                         }
225                                 }
226                         }
227                         return false;
228                 }
229
230                 public override bool MoveToFirst ()
231                 {
232                         if (nsNode == null && node.NodeType != XmlNodeType.Attribute && node.ParentNode != null) {
233                                 MoveToParent ();
234                                 // Follow these 2 steps so that we can skip 
235                                 // some types of nodes .
236                                 MoveToFirstChild ();
237                                 return true;
238                         }
239                         return false;
240                 }
241
242                 public override bool MoveToFirstAttribute ()
243                 {
244                         if (node.Attributes == null)
245                                 return false;
246                         if (NodeType == XPathNodeType.Element) {
247                                 for (int i = 0; i < node.Attributes.Count; i++) {
248                                         XmlAttribute attr = node.Attributes [i];
249                                         if (attr.NamespaceURI != Xmlns) {
250                                                 node = attr;
251                                                 nsNode = null;
252                                                 return true;
253                                         }
254                                 }
255                         }
256                         return false;
257                 }
258
259                 public override bool MoveToFirstChild ()
260                 {
261                         if (HasChildren) {
262                                 if (node == document) {
263                                         XmlNode n = node.FirstChild;
264                                         if (n == null)
265                                                 return false;
266                                         bool loop = true;
267                                         do {
268                                                 switch (n.NodeType) {
269                                                 case XmlNodeType.XmlDeclaration:
270                                                 case XmlNodeType.DocumentType:
271                                                         n = n.NextSibling;
272                                                         if (n == null)
273                                                                 return false;
274                                                         break;
275                                                 default:
276                                                         loop = false;
277                                                         break;
278                                                 }
279                                         } while (loop);
280                                         node = n;
281                                 } else {
282                                         do {
283                                                 node = node.FirstChild;
284                                                 if (node.NodeType != XmlNodeType.EntityReference)
285                                                         break;
286                                                 node = node.NextSibling;
287                                         } while (node != null);
288                                         if (node == null)
289                                                 return false;
290                                 }
291                                 return true;
292                         }
293                         return false;
294                 }
295
296                 public override bool MoveToFirstNamespace (XPathNamespaceScope namespaceScope)
297                 {
298                         if (NodeType != XPathNodeType.Element)
299                                 return false;
300                         XmlElement el = node as XmlElement;
301                         if (node.Attributes != null) {
302                                 do {
303                                         for (int i = 0; i < el.Attributes.Count; i++) {
304                                                 XmlAttribute attr = el.Attributes [i];
305                                                 if (attr.NamespaceURI == Xmlns) {
306                                                         nsNode = attr;
307                                                         return true;
308                                                 }
309                                         }
310                                         if (namespaceScope == XPathNamespaceScope.Local)
311                                                 return false;
312                                         el = el.ParentNode as XmlElement;
313                                 } while (el != null);
314                         }
315
316                         if (namespaceScope == XPathNamespaceScope.All) {
317                                 nsNode = document;
318                                 return true;
319                         }
320                         else
321                                 return false;
322                 }
323
324                 public override bool MoveToId (string id)
325                 {
326                         XmlElement eltNew = document.GetElementById (id);
327                         if (eltNew == null)
328                                 return false;
329
330                         node = eltNew;
331                         return true;
332                 }
333
334                 public override bool MoveToNamespace (string name)
335                 {
336                         if (name == "xml") {
337                                 nsNode = document;
338                                 return true;
339                         }
340
341                         if (NodeType != XPathNodeType.Element)
342                                 return false;
343
344                         XmlElement el = node as XmlElement;
345                         if (node.Attributes != null) {
346                                 do {
347                                         for (int i = 0; i < el.Attributes.Count; i++) {
348                                                 XmlAttribute attr = el.Attributes [i];
349                                                 if (attr.NamespaceURI == Xmlns && attr.Name == name) {
350                                                         nsNode = attr;
351                                                         return true;
352                                                 }
353                                         }
354                                         el = node.ParentNode as XmlElement;
355                                 } while (el != null);
356                         }
357                         return false;
358                 }
359
360                 public override bool MoveToNext ()
361                 {
362                         if (nsNode != null)
363                                 return false;
364
365                         if (node.NextSibling != null) {
366                                 XmlNode n = node.NextSibling;
367                                 if (node.ParentNode != null && node.ParentNode.NodeType == XmlNodeType.Document) {
368                                         while (n != null) {
369                                                 switch (n.NodeType) {
370                                                 case XmlNodeType.DocumentType:
371                                                 case XmlNodeType.XmlDeclaration:
372                                                         n = n.NextSibling;
373                                                         continue;
374                                                 }
375                                                 break;
376                                         }
377                                         if (n != null)
378                                                 node = n;
379                                         else
380                                                 return false;
381                                 } else {
382                                         while (n != null) {
383                                                 if (n.NodeType != XmlNodeType.EntityReference)
384                                                         break;
385                                                 n = n.NextSibling;
386                                         }
387                                         if (n != null)
388                                                 node = n;
389                                         else
390                                                 return false;
391                                 }
392                                 return true;
393                         }
394                         else
395                                 return false;
396                 }
397
398                 public override bool MoveToNextAttribute ()
399                 {
400                         if (node == null)
401                                 return false;
402                         if (NodeType != XPathNodeType.Attribute)
403                                 return false;
404
405                         // Find current attribute.
406                         int pos = 0;
407                         XmlElement owner = ((XmlAttribute) node).OwnerElement;
408                         if (owner == null)
409                                 return false;
410
411                         int count = owner.Attributes.Count;
412                         for(; pos < count; pos++)
413                                 if (owner.Attributes [pos] == node)
414                                         break;
415                         if (pos == count)
416                                 return false;   // Where is current attribute? Maybe removed.
417
418                         // Find next attribute.
419                         for(pos++; pos < count; pos++) {
420                                 if (owner.Attributes [pos].NamespaceURI != Xmlns) {
421                                         node = owner.Attributes [pos];
422                                         nsNode = null;
423                                         return true;
424                                 }
425                         }
426                         return false;
427                 }
428
429                 public override bool MoveToNextNamespace (XPathNamespaceScope namespaceScope)
430                 {
431                         if (nsNode == document)
432                                 // Current namespace is "xml", so there should be no more namespace nodes.
433                                 return false;
434
435                         if (nsNode == null)
436                                 return false;
437
438                         // Get current attribute's position.
439                         int pos = 0;
440                         XmlElement owner = ((XmlAttribute) nsNode).OwnerElement;
441                         if (owner == null)
442                                 return false;
443
444                         int count = owner.Attributes.Count;
445                         for(; pos < count; pos++)
446                                 if (owner.Attributes [pos] == nsNode)
447                                         break;
448                         if (pos == count)
449                                 return false;   // Where is current attribute? Maybe removed.
450
451                         // Find next namespace from the same element as current ns node.
452                         for(pos++; pos < count; pos++) {
453                                 if (owner.Attributes [pos].NamespaceURI == Xmlns) {
454                                         nsNode = owner.Attributes [pos];
455                                         return true;
456                                 }
457                         }
458
459                         // If not found more, then find from ancestors.
460                         // But if scope is Local, then it returns false here.
461                         if (namespaceScope == XPathNamespaceScope.Local)
462                                 return false;
463                         owner = owner.ParentNode as XmlElement;
464                         while (owner != null) {
465                                 for (int i = 0; i < owner.Attributes.Count; i++) {
466                                         XmlAttribute attr = owner.Attributes [i];
467                                         if (attr.NamespaceURI == Xmlns) {
468                                                 nsNode = attr;
469                                                 return true;
470                                         }
471                                 }
472                                 owner = owner.ParentNode as XmlElement;
473                         }
474
475                         if (namespaceScope == XPathNamespaceScope.All) {
476                                 nsNode = document;
477                                 return true;
478                         }
479                         else
480                                 return false;
481                 }
482
483                 public override bool MoveToParent ()
484                 {
485                         if (nsNode != null) {
486                                 nsNode = null;
487                                 return true;
488                         }
489                         else if (node.NodeType == XmlNodeType.Attribute) {
490                                 XmlElement ownerElement = ((XmlAttribute)node).OwnerElement;
491                                 if (ownerElement != null) {
492                                         node = ownerElement;
493                                         nsNode = null;
494                                         return true;
495                                 }
496                         } else if (node.ParentNode != null) {
497                                 node = node.ParentNode;
498                                 nsNode = null;
499                                 return true;
500                         }
501                         return false;
502                 }
503
504                 public override bool MoveToPrevious ()
505                 {
506                         if (nsNode != null)
507                                 return false;
508
509                         if (node.PreviousSibling != null) {
510                                 if (node.ParentNode != null && node.ParentNode.NodeType == XmlNodeType.Document) {
511                                         XmlNode n = node.PreviousSibling;
512                                         while (n != null) {
513                                                 switch (n.NodeType) {
514                                                 case XmlNodeType.DocumentType:
515                                                 case XmlNodeType.XmlDeclaration:
516                                                         n = n.PreviousSibling;
517                                                         continue;
518                                                 }
519                                                 break;
520                                         }
521                                         if (n != null)
522                                                 node = n;
523                                         else
524                                                 return false;
525                                 }
526                                 else
527                                         node = node.PreviousSibling;
528                                 
529                                 return true;
530                         }
531                         else
532                                 return false;
533                 }
534
535                 public override void MoveToRoot ()
536                 {
537                         XmlAttribute attr = node as XmlAttribute;
538                         XmlNode tmp = attr != null ? attr.OwnerElement : node;
539                         while (tmp.ParentNode != null)
540                                 tmp = tmp.ParentNode;
541                         node = tmp;
542                         nsNode = null;
543                 }
544
545                 internal XmlNode Node { get { return node; } }
546
547                 XmlNode IHasXmlNode.GetNode ()
548                 {
549                         return node;
550                 }
551
552                 #endregion
553         }
554 }