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