**** Merged from MCS ****
[mono-project.git] / mcs / class / System.XML / System.Xml / XmlDocumentNavigator.cs
blob1971ee99a55685d164b2d15546527cbb254b71fb
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
13 // Permission is hereby granted, free of charge, to any person obtaining
14 // a copy of this software and associated documentation files (the
15 // "Software"), to deal in the Software without restriction, including
16 // without limitation the rights to use, copy, modify, merge, publish,
17 // distribute, sublicense, and/or sell copies of the Software, and to
18 // permit persons to whom the Software is furnished to do so, subject to
19 // the following conditions:
20 //
21 // The above copyright notice and this permission notice shall be
22 // included in all copies or substantial portions of the Software.
23 //
24 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
25 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
26 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
27 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
28 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
29 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
30 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
33 using System;
34 using System.Collections;
35 using System.Xml;
36 using System.Xml.XPath;
38 namespace System.Xml
40 internal class XmlDocumentNavigator : XPathNavigator, IHasXmlNode
42 #region Constructors
44 internal XmlDocumentNavigator (XmlNode node)
45 : this (node, null)
47 nsNodeXml = document.CreateAttribute ("xmlns", "xml", Xmlns);
48 nsNodeXml.Value = XmlnsXML;
49 if (node.NodeType == XmlNodeType.Attribute && node.NamespaceURI == XmlNamespaceManager.XmlnsXmlns) {
50 nsNode = (XmlAttribute) node;
51 node = nsNode.OwnerElement;
55 private XmlDocumentNavigator (XmlNode node, XmlAttribute nsNodeXml)
57 this.node = node;
58 this.document = node.NodeType == XmlNodeType.Document ?
59 node as XmlDocument : node.OwnerDocument;
60 this.nsNodeXml = nsNodeXml;
63 #endregion
65 #region Fields
66 private const string Xmlns = "http://www.w3.org/2000/xmlns/";
67 private const string XmlnsXML = "http://www.w3.org/XML/1998/namespace";
69 private XmlAttribute nsNodeXml;
70 private XmlNode node;
71 private XmlDocument document;
72 // Current namespace node (ancestor's attribute of current node).
73 private XmlAttribute nsNode;
74 private ArrayList iteratedNsNames;
75 #endregion
77 #region Properties
79 public override string BaseURI {
80 get {
81 return node.BaseURI;
85 public override bool HasAttributes {
86 get {
87 if (NsNode != null)
88 return false;
90 if (node.Attributes != null)
91 for (int i = 0; i < node.Attributes.Count; i++)
92 if (node.Attributes [i].NamespaceURI != Xmlns)
93 return true;
94 return false;
98 public override bool HasChildren {
99 get {
100 if (NsNode != null)
101 return false;
103 XPathNodeType nodeType = NodeType;
104 bool canHaveChildren = nodeType == XPathNodeType.Root || nodeType == XPathNodeType.Element;
105 return canHaveChildren && node.FirstChild != null;
109 public override bool IsEmptyElement {
110 get {
111 if (NsNode != null)
112 return false;
114 return node.NodeType == XmlNodeType.Element
115 && ((XmlElement) node).IsEmpty;
119 public XmlAttribute NsNode {
120 get { return nsNode; }
121 set {
122 if (value == null) {
123 if (iteratedNsNames != null)
124 iteratedNsNames.Clear ();
126 else {
127 if (iteratedNsNames == null)
128 iteratedNsNames = new ArrayList ();
129 iteratedNsNames.Add (value.Name);
131 nsNode = value;
135 public override string LocalName {
136 get {
137 XmlAttribute nsNode = NsNode;
138 if (nsNode != null) {
139 if (nsNode == nsNodeXml)
140 return "xml";
141 else
142 return (nsNode.Name == "xmlns") ? String.Empty : nsNode.LocalName;
145 XPathNodeType nodeType = NodeType;
146 bool canHaveName =
147 nodeType == XPathNodeType.Element ||
148 nodeType == XPathNodeType.Attribute ||
149 nodeType == XPathNodeType.ProcessingInstruction ||
150 nodeType == XPathNodeType.Namespace;
151 return canHaveName ? node.LocalName : String.Empty;
155 public override string Name {
156 get {
157 if (NsNode != null)
158 return LocalName;
160 XPathNodeType nodeType = NodeType;
161 bool canHaveName =
162 nodeType == XPathNodeType.Element ||
163 nodeType == XPathNodeType.Attribute ||
164 nodeType == XPathNodeType.ProcessingInstruction ||
165 nodeType == XPathNodeType.Namespace;
166 return canHaveName ? node.Name : String.Empty;
170 public override string NamespaceURI {
171 get { return (NsNode != null) ? String.Empty : node.NamespaceURI; }
174 public override XmlNameTable NameTable {
175 get {
176 return document.NameTable;
180 public override XPathNodeType NodeType {
181 get { return (NsNode != null) ? XPathNodeType.Namespace : node.XPathNodeType; }
184 public override string Prefix {
185 get { return (NsNode != null) ? String.Empty : node.Prefix; }
188 public override string Value {
189 get {
190 switch (NodeType) {
191 case XPathNodeType.Attribute:
192 case XPathNodeType.Comment:
193 case XPathNodeType.ProcessingInstruction:
194 return node.Value;
195 case XPathNodeType.Text:
196 case XPathNodeType.Whitespace:
197 case XPathNodeType.SignificantWhitespace:
198 string value = node.Value;
199 for (XmlNode n = node.NextSibling; n != null; n = n.NextSibling) {
200 switch (n.XPathNodeType) {
201 case XPathNodeType.Text:
202 case XPathNodeType.Whitespace:
203 case XPathNodeType.SignificantWhitespace:
204 value += n.Value;
205 continue;
207 break;
209 return value;
210 case XPathNodeType.Element:
211 case XPathNodeType.Root:
212 return node.InnerText;
213 case XPathNodeType.Namespace:
214 return NsNode == nsNodeXml ? XmlnsXML : NsNode.Value;
216 return String.Empty;
220 public override string XmlLang {
221 get {
222 return node.XmlLang;
226 #endregion
228 #region Methods
230 private bool CheckNsNameAppearance (string name, string ns)
232 if (iteratedNsNames != null && iteratedNsNames.Contains (name))
233 return true;
234 // default namespace erasure - just add name and never return this node
235 if (ns == String.Empty) {
236 if (iteratedNsNames == null)
237 iteratedNsNames = new ArrayList ();
238 iteratedNsNames.Add ("xmlns");
239 return true;
242 return false;
245 public override XPathNavigator Clone ()
247 XmlDocumentNavigator clone = new XmlDocumentNavigator (node, nsNodeXml);
248 clone.nsNode = nsNode;
249 if (iteratedNsNames != null)
250 clone.iteratedNsNames = (ArrayList) iteratedNsNames.Clone ();
251 return clone;
254 public override string GetAttribute (string localName, string namespaceURI)
256 if (HasAttributes) {
257 XmlElement el = Node as XmlElement;
258 return el != null ? el.GetAttribute (localName, namespaceURI) : String.Empty;
260 return String.Empty;
263 public override string GetNamespace (string name)
265 // MSDN says "String.Empty if a matching namespace
266 // node is not found or if the navigator is not
267 // positioned on an element node", but in fact it
268 // returns actual namespace for the other nodes.
269 return Node.GetNamespaceOfPrefix (name);
272 public override bool IsSamePosition (XPathNavigator other)
274 XmlDocumentNavigator otherDocumentNavigator = other as XmlDocumentNavigator;
275 if (otherDocumentNavigator != null)
276 return node == otherDocumentNavigator.node
277 && NsNode == otherDocumentNavigator.NsNode;
278 return false;
281 public override bool MoveTo (XPathNavigator other)
283 XmlDocumentNavigator otherDocumentNavigator = other as XmlDocumentNavigator;
284 if (otherDocumentNavigator != null) {
285 if (document == otherDocumentNavigator.document) {
286 node = otherDocumentNavigator.node;
287 NsNode = otherDocumentNavigator.NsNode;
288 return true;
291 return false;
294 public override bool MoveToAttribute (string localName, string namespaceURI)
296 if (node.Attributes != null) {
297 for (int i = 0; i < node.Attributes.Count; i++) {
298 XmlAttribute attr = node.Attributes [i];
299 if (attr.LocalName == localName
300 && attr.NamespaceURI == namespaceURI) {
301 node = attr;
302 NsNode = null;
303 return true;
307 return false;
310 public override bool MoveToFirst ()
312 if (NsNode == null && node.NodeType != XmlNodeType.Attribute && node.ParentNode != null) {
313 if (!MoveToParent ())
314 return false;
315 // Follow these 2 steps so that we can skip
316 // some types of nodes .
317 MoveToFirstChild ();
318 return true;
320 return false;
323 public override bool MoveToFirstAttribute ()
325 if (node.Attributes == null)
326 return false;
327 if (NodeType == XPathNodeType.Element) {
328 for (int i = 0; i < node.Attributes.Count; i++) {
329 XmlAttribute attr = node.Attributes [i];
330 if (attr.NamespaceURI != Xmlns) {
331 node = attr;
332 NsNode = null;
333 return true;
337 return false;
340 public override bool MoveToFirstChild ()
342 if (HasChildren) {
343 if (node == document) {
344 XmlNode n = node.FirstChild;
345 if (n == null)
346 return false;
347 bool loop = true;
348 do {
349 switch (n.NodeType) {
350 case XmlNodeType.XmlDeclaration:
351 case XmlNodeType.DocumentType:
352 n = n.NextSibling;
353 if (n == null)
354 return false;
355 break;
356 default:
357 loop = false;
358 break;
360 } while (loop);
361 node = n;
362 } else {
363 XmlNode n2 = null;
364 do {
365 n2 = node.FirstChild;
366 if (node.NodeType != XmlNodeType.EntityReference)
367 break;
368 n2 = node.NextSibling;
369 } while (n2 != null);
370 if (n2 == null)
371 return false;
372 node = n2;
374 return true;
376 return false;
379 public override bool MoveToFirstNamespace (XPathNamespaceScope namespaceScope)
381 if (NodeType != XPathNodeType.Element)
382 return false;
383 XmlElement el = node as XmlElement;
384 if (node.Attributes != null) {
385 do {
386 for (int i = 0; i < el.Attributes.Count; i++) {
387 XmlAttribute attr = el.Attributes [i];
388 if (attr.NamespaceURI == Xmlns) {
389 if (CheckNsNameAppearance (attr.Name, attr.Value))
390 continue;
391 NsNode = attr;
392 return true;
395 if (namespaceScope == XPathNamespaceScope.Local)
396 return false;
397 el = el.ParentNode as XmlElement;
398 } while (el != null);
401 if (namespaceScope == XPathNamespaceScope.All) {
402 if (CheckNsNameAppearance (nsNodeXml.Name, nsNodeXml.Value))
403 return false;
404 NsNode = nsNodeXml;
405 return true;
407 else
408 return false;
411 public override bool MoveToId (string id)
413 XmlElement eltNew = document.GetElementById (id);
414 if (eltNew == null)
415 return false;
417 node = eltNew;
418 return true;
421 public override bool MoveToNamespace (string name)
423 if (name == "xml") {
424 NsNode = nsNodeXml;
425 return true;
428 if (NodeType != XPathNodeType.Element)
429 return false;
431 XmlElement el = node as XmlElement;
432 if (node.Attributes != null) {
433 do {
434 for (int i = 0; i < el.Attributes.Count; i++) {
435 XmlAttribute attr = el.Attributes [i];
436 if (attr.NamespaceURI == Xmlns && attr.Name == name) {
437 NsNode = attr;
438 return true;
441 el = node.ParentNode as XmlElement;
442 } while (el != null);
444 return false;
447 public override bool MoveToNext ()
449 if (NsNode != null)
450 return false;
452 XmlNode n = node;
453 if (NodeType == XPathNodeType.Text) {
454 do {
455 n = n.NextSibling;
456 if (n == null)
457 return false;
458 switch (n.NodeType) {
459 case XmlNodeType.CDATA:
460 case XmlNodeType.EntityReference:
461 case XmlNodeType.SignificantWhitespace:
462 case XmlNodeType.Text:
463 case XmlNodeType.Whitespace:
464 continue;
465 default:
466 break;
468 break;
469 } while (true);
470 } else {
471 n = n.NextSibling;
472 if (n == null)
473 return false;
476 if (n.ParentNode != null && n.ParentNode.NodeType == XmlNodeType.Document) {
477 while (n != null) {
478 switch (n.NodeType) {
479 case XmlNodeType.DocumentType:
480 case XmlNodeType.XmlDeclaration:
481 n = n.NextSibling;
482 continue;
484 break;
486 if (n != null)
487 node = n;
488 else
489 return false;
490 } else {
491 while (n != null) {
492 if (n.NodeType != XmlNodeType.EntityReference)
493 break;
494 n = n.NextSibling;
496 if (n != null)
497 node = n;
498 else
499 return false;
501 return true;
504 public override bool MoveToNextAttribute ()
506 if (node == null)
507 return false;
508 if (NodeType != XPathNodeType.Attribute)
509 return false;
511 // Find current attribute.
512 int pos = 0;
513 XmlElement owner = ((XmlAttribute) node).OwnerElement;
514 if (owner == null)
515 return false;
517 int count = owner.Attributes.Count;
518 for(; pos < count; pos++)
519 if (owner.Attributes [pos] == node)
520 break;
521 if (pos == count)
522 return false; // Where is current attribute? Maybe removed.
524 // Find next attribute.
525 for(pos++; pos < count; pos++) {
526 if (owner.Attributes [pos].NamespaceURI != Xmlns) {
527 node = owner.Attributes [pos];
528 NsNode = null;
529 return true;
532 return false;
535 public override bool MoveToNextNamespace (XPathNamespaceScope namespaceScope)
537 if (NsNode == nsNodeXml)
538 // Current namespace is "xml", so there should be no more namespace nodes.
539 return false;
541 if (NsNode == null)
542 return false;
544 // Get current attribute's position.
545 int pos = 0;
546 XmlElement owner = ((XmlAttribute) NsNode).OwnerElement;
547 if (owner == null)
548 return false;
550 int count = owner.Attributes.Count;
551 for(; pos < count; pos++)
552 if (owner.Attributes [pos] == NsNode)
553 break;
554 if (pos == count)
555 return false; // Where is current attribute? Maybe removed.
557 // Find next namespace from the same element as current ns node.
558 for(pos++; pos < count; pos++) {
559 if (owner.Attributes [pos].NamespaceURI == Xmlns) {
560 XmlAttribute a = owner.Attributes [pos];
561 if (CheckNsNameAppearance (a.Name, a.Value))
562 continue;
563 NsNode = a;
564 return true;
568 // If not found more, then find from ancestors.
569 // But if scope is Local, then it returns false here.
570 if (namespaceScope == XPathNamespaceScope.Local)
571 return false;
572 owner = owner.ParentNode as XmlElement;
573 while (owner != null) {
574 for (int i = 0; i < owner.Attributes.Count; i++) {
575 XmlAttribute attr = owner.Attributes [i];
576 if (attr.NamespaceURI == Xmlns) {
577 if (CheckNsNameAppearance (attr.Name, attr.Value))
578 continue;
579 NsNode = attr;
580 return true;
583 owner = owner.ParentNode as XmlElement;
586 if (namespaceScope == XPathNamespaceScope.All) {
587 if (CheckNsNameAppearance (nsNodeXml.Name, nsNodeXml.Value))
588 return false;
589 NsNode = nsNodeXml;
590 return true;
592 return false;
595 public override bool MoveToParent ()
597 if (NsNode != null) {
598 NsNode = null;
599 return true;
601 else if (node.NodeType == XmlNodeType.Attribute) {
602 XmlElement ownerElement = ((XmlAttribute)node).OwnerElement;
603 if (ownerElement != null) {
604 node = ownerElement;
605 NsNode = null;
606 return true;
608 } else if (node.ParentNode != null) {
609 node = node.ParentNode;
610 NsNode = null;
611 return true;
613 return false;
616 public override bool MoveToPrevious ()
618 if (NsNode != null)
619 return false;
621 if (node.PreviousSibling != null) {
622 if (node.ParentNode != null && node.ParentNode.NodeType == XmlNodeType.Document) {
623 XmlNode n = node.PreviousSibling;
624 while (n != null) {
625 switch (n.NodeType) {
626 case XmlNodeType.DocumentType:
627 case XmlNodeType.XmlDeclaration:
628 n = n.PreviousSibling;
629 continue;
631 break;
633 if (n != null)
634 node = n;
635 else
636 return false;
638 else
639 node = node.PreviousSibling;
641 return true;
643 else
644 return false;
647 public override void MoveToRoot ()
649 XmlAttribute attr = node as XmlAttribute;
650 XmlNode tmp = attr != null ? attr.OwnerElement : node;
651 while (tmp.ParentNode != null)
652 tmp = tmp.ParentNode;
653 node = tmp;
654 NsNode = null;
657 private XmlNode Node { get { return NsNode != null ? NsNode : node; } }
659 XmlNode IHasXmlNode.GetNode ()
661 return Node;
664 #endregion