2 // XPathNavigatorReader.cs
\r
5 // Atsushi Enomoto <ginga@kit.hi-ho.ne.jp>
\r
7 // 2003 Atsushi Enomoto. No rights reserved.
\r
8 // Copyright (C) 2004 Novell Inc.
\r
11 // Permission is hereby granted, free of charge, to any person obtaining
\r
12 // a copy of this software and associated documentation files (the
\r
13 // "Software"), to deal in the Software without restriction, including
\r
14 // without limitation the rights to use, copy, modify, merge, publish,
\r
15 // distribute, sublicense, and/or sell copies of the Software, and to
\r
16 // permit persons to whom the Software is furnished to do so, subject to
\r
17 // the following conditions:
\r
19 // The above copyright notice and this permission notice shall be
\r
20 // included in all copies or substantial portions of the Software.
\r
22 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
\r
23 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
\r
24 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
\r
25 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
\r
26 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
\r
27 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
\r
28 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
\r
33 using System.Xml.Schema;
\r
34 using System.Xml.XPath;
\r
36 namespace Mono.Xml.XPath
\r
39 internal class XPathNavigatorReader : XmlReader
\r
41 public XPathNavigatorReader (XPathNavigator nav)
\r
43 // It seems that this class have only to support linked
\r
44 // node as its parameter
\r
45 switch (nav.NodeType) {
\r
46 case XPathNodeType.Attribute:
\r
47 case XPathNodeType.Namespace:
\r
48 throw new InvalidOperationException (String.Format ("NodeType {0} is not supported to read as a subtree of an XPathNavigator.", nav.NodeType));
\r
50 root = nav.Clone ();
\r
51 current = nav.Clone ();
\r
54 XPathNavigator root;
\r
55 XPathNavigator current;
\r
59 bool attributeValueConsumed;
\r
60 StringBuilder readStringBuffer = new StringBuilder ();
\r
61 StringBuilder innerXmlBuilder = new StringBuilder ();
\r
64 int attributeCount = 0;
\r
69 public override XmlNodeType NodeType
\r
72 if (ReadState != ReadState.Interactive)
\r
73 return XmlNodeType.None;
\r
75 return XmlNodeType.EndElement;
\r
76 if (attributeValueConsumed)
\r
77 // Is there any way to get other kind of nodes than Text?
\r
78 return XmlNodeType.Text;
\r
80 switch (current.NodeType) {
\r
81 case XPathNodeType.Namespace:
\r
82 case XPathNodeType.Attribute:
\r
83 return XmlNodeType.Attribute;
\r
84 case XPathNodeType.Comment:
\r
85 return XmlNodeType.Comment;
\r
86 case XPathNodeType.Element:
\r
87 return XmlNodeType.Element;
\r
88 case XPathNodeType.ProcessingInstruction:
\r
89 return XmlNodeType.ProcessingInstruction;
\r
90 case XPathNodeType.Root:
\r
91 // It is actually Document, but in XmlReader there is no such situation to return Document.
\r
92 return XmlNodeType.None;
\r
93 case XPathNodeType.SignificantWhitespace:
\r
94 return XmlNodeType.SignificantWhitespace;
\r
95 case XPathNodeType.Text:
\r
96 return XmlNodeType.Text;
\r
97 case XPathNodeType.Whitespace:
\r
98 return XmlNodeType.Whitespace;
\r
100 throw new InvalidOperationException (String.Format ("Current XPathNavigator status is {0} which is not acceptable to XmlReader.", current.NodeType));
\r
105 public override string Name {
\r
108 return String.Empty;
\r
109 else if (current.NodeType == XPathNodeType.Namespace)
\r
110 return current.Name == String.Empty ? "xmlns" : "xmlns:" + current.Name;
\r
112 return current.Name;
\r
116 public override string LocalName {
\r
119 return String.Empty;
\r
120 else if (current.NodeType == XPathNodeType.Namespace && current.LocalName == String.Empty)
\r
123 return current.LocalName;
\r
127 public override string NamespaceURI {
\r
130 return String.Empty;
\r
131 else if (current.NodeType == XPathNodeType.Namespace)
\r
132 return XmlNamespaceManager.XmlnsXmlns;
\r
134 return current.NamespaceURI;
\r
138 public override string Prefix {
\r
141 return String.Empty;
\r
142 else if (current.NodeType == XPathNodeType.Namespace && current.LocalName != String.Empty)
\r
145 return current.Prefix;
\r
149 public override bool HasValue {
\r
151 switch (current.NodeType) {
\r
152 case XPathNodeType.Namespace:
\r
153 case XPathNodeType.Attribute:
\r
154 case XPathNodeType.Comment:
\r
155 case XPathNodeType.ProcessingInstruction:
\r
156 case XPathNodeType.SignificantWhitespace:
\r
157 case XPathNodeType.Text:
\r
158 case XPathNodeType.Whitespace:
\r
165 public override int Depth {
\r
167 switch (ReadState) {
\r
168 case ReadState.EndOfFile:
\r
169 case ReadState.Initial:
\r
170 case ReadState.Closed:
\r
177 public override string Value {
\r
179 switch (current.NodeType) {
\r
180 case XPathNodeType.Namespace:
\r
181 case XPathNodeType.Attribute:
\r
182 case XPathNodeType.Comment:
\r
183 case XPathNodeType.ProcessingInstruction:
\r
184 case XPathNodeType.SignificantWhitespace:
\r
185 case XPathNodeType.Text:
\r
186 case XPathNodeType.Whitespace:
\r
187 return current.Value;
\r
188 case XPathNodeType.Element:
\r
189 case XPathNodeType.Root:
\r
190 return String.Empty;
\r
192 throw new InvalidOperationException ("Current XPathNavigator status is {0} which is not acceptable to XmlReader.");
\r
197 public override string BaseURI {
\r
198 get { return current.BaseURI; }
\r
201 public override bool IsEmptyElement {
\r
202 get { return current.IsEmptyElement; }
\r
205 public override bool IsDefault {
\r
207 IXmlSchemaInfo si = current as IXmlSchemaInfo;
\r
208 return si != null && si.IsDefault;
\r
212 // It makes no sense.
\r
213 public override char QuoteChar {
\r
214 get { return '\"'; }
\r
217 public override string XmlLang {
\r
218 get { return current.XmlLang; }
\r
221 // It is meaningless.
\r
222 public override XmlSpace XmlSpace {
\r
223 get { return XmlSpace.None; }
\r
226 public override int AttributeCount {
\r
227 get { return attributeCount; }
\r
230 private int GetAttributeCount ()
\r
233 if (current.MoveToFirstAttribute ()) {
\r
236 } while (current.MoveToNextAttribute ());
\r
237 current.MoveToParent ();
\r
239 if (current.MoveToFirstNamespace (XPathNamespaceScope.Local)) {
\r
242 } while (current.MoveToNextNamespace (XPathNamespaceScope.Local));
\r
243 current.MoveToParent ();
\r
248 private bool MoveToAttributeNavigator (int i)
\r
250 switch (current.NodeType) {
\r
251 case XPathNodeType.Namespace:
\r
252 case XPathNodeType.Attribute:
\r
253 this.MoveToElement ();
\r
254 goto case XPathNodeType.Element;
\r
255 case XPathNodeType.Element:
\r
257 if (MoveToFirstAttribute ()) {
\r
261 for (count++; this.MoveToNextAttribute (); count++) {
\r
270 public override string this [int i] {
\r
272 XPathNavigator backup = current.Clone ();
\r
274 if (MoveToAttributeNavigator (i))
\r
277 throw new ArgumentOutOfRangeException ();
\r
279 current.MoveTo (backup);
\r
284 private void SplitName (string name, out string localName, out string ns)
\r
288 int colon = name.IndexOf (':');
\r
290 localName = name.Substring (colon + 1, name.Length - colon - 1);
\r
291 ns = this.LookupNamespace (name.Substring (0, colon));
\r
295 public override string this [string name] {
\r
299 SplitName (name, out localName, out ns);
\r
300 return this [localName, ns];
\r
304 public override string this [string localName, string namespaceURI] {
\r
306 string v = current.GetAttribute (localName, namespaceURI);
\r
307 if (v != String.Empty)
\r
309 XPathNavigator tmp = current.Clone ();
\r
310 return tmp.MoveToAttribute (localName, namespaceURI) ? String.Empty : null;
\r
314 public override bool EOF {
\r
315 get { return ReadState == ReadState.EndOfFile; }
\r
318 public override ReadState ReadState {
\r
321 return ReadState.EndOfFile;
\r
323 return ReadState.Closed;
\r
325 return ReadState.Initial;
\r
326 return ReadState.Interactive;
\r
330 public override XmlNameTable NameTable {
\r
331 get { return current.NameTable; }
\r
337 public override string GetAttribute (string name)
\r
341 SplitName (name, out localName, out ns);
\r
342 return this [localName, ns];
\r
345 public override string GetAttribute (string localName, string namespaceURI)
\r
347 return this [localName, namespaceURI];
\r
350 public override string GetAttribute (int i)
\r
355 private bool CheckAttributeMove (bool b)
\r
358 attributeValueConsumed = false;
\r
362 public override bool MoveToAttribute (string name)
\r
366 SplitName (name, out localName, out ns);
\r
367 return CheckAttributeMove (MoveToAttribute (localName, ns));
\r
370 public override bool MoveToAttribute (string localName, string namespaceURI)
\r
372 XPathNavigator backup = null;
\r
373 switch (current.NodeType) {
\r
374 case XPathNodeType.Attribute:
\r
375 backup = current.Clone ();
\r
376 this.MoveToElement ();
\r
377 goto case XPathNodeType.Element;
\r
378 case XPathNodeType.Element:
\r
379 while (MoveToNextAttribute ())
\r
380 if (current.LocalName == localName && current.NamespaceURI == namespaceURI) {
\r
381 attributeValueConsumed = false;
\r
386 if (backup != null)
\r
391 public override void MoveToAttribute (int i)
\r
393 if (!MoveToAttributeNavigator (i))
\r
394 throw new ArgumentOutOfRangeException ();
\r
397 public override bool MoveToFirstAttribute ()
\r
399 bool b = CheckAttributeMove (current.MoveToFirstNamespace (XPathNamespaceScope.Local));
\r
402 return CheckAttributeMove (current.MoveToFirstAttribute ());
\r
405 public override bool MoveToNextAttribute ()
\r
407 if (current.NodeType == XPathNodeType.Namespace) {
\r
408 bool b = CheckAttributeMove (current.MoveToNextNamespace (XPathNamespaceScope.Local));
\r
411 current.MoveToParent ();
\r
412 b = CheckAttributeMove (current.MoveToFirstAttribute ());
\r
416 return CheckAttributeMove (current.MoveToNextAttribute ());
\r
419 public override bool MoveToElement ()
\r
421 if (current.NodeType == XPathNodeType.Attribute ||
\r
422 current.NodeType == XPathNodeType.Namespace) {
\r
423 attributeValueConsumed = false;
\r
424 return current.MoveToParent ();
\r
429 public override void Close ()
\r
435 public override bool Read ()
\r
437 switch (ReadState) {
\r
438 case ReadState.EndOfFile:
\r
439 case ReadState.Closed:
\r
440 case ReadState.Error:
\r
442 case ReadState.Initial:
\r
444 switch (current.NodeType) {
\r
445 case XPathNodeType.Root:
\r
446 // recurse, but as Interactive
\r
448 case XPathNodeType.Element:
\r
449 if (current.IsEmptyElement)
\r
451 attributeCount = GetAttributeCount ();
\r
468 if (endElement || !current.MoveToFirstChild ()) {
\r
469 if (current.IsSamePosition (root)) { // It should happen only when the root node was empty.
\r
473 if (!current.MoveToNext ()) {
\r
474 current.MoveToParent ();
\r
476 endElement = (current.NodeType == XPathNodeType.Element);
\r
477 if (current.IsSamePosition (root)) {
\r
478 if (current.NodeType == XPathNodeType.Element)
\r
481 endElement = false;
\r
487 endElement = false;
\r
489 else if (!endElement)
\r
491 attributeCount = GetAttributeCount ();
\r
495 public override string ReadString ()
\r
497 readStringBuffer.Length = 0;
\r
499 switch (NodeType) {
\r
501 return String.Empty;
\r
502 case XmlNodeType.Element:
\r
503 if (IsEmptyElement)
\r
504 return String.Empty;
\r
507 switch (NodeType) {
\r
508 case XmlNodeType.Text:
\r
509 case XmlNodeType.CDATA:
\r
510 case XmlNodeType.Whitespace:
\r
511 case XmlNodeType.SignificantWhitespace:
\r
512 readStringBuffer.Append (Value);
\r
518 case XmlNodeType.Text:
\r
519 case XmlNodeType.CDATA:
\r
520 case XmlNodeType.Whitespace:
\r
521 case XmlNodeType.SignificantWhitespace:
\r
523 switch (NodeType) {
\r
524 case XmlNodeType.Text:
\r
525 case XmlNodeType.CDATA:
\r
526 case XmlNodeType.Whitespace:
\r
527 case XmlNodeType.SignificantWhitespace:
\r
528 readStringBuffer.Append (Value);
\r
536 string ret = readStringBuffer.ToString ();
\r
537 readStringBuffer.Length = 0;
\r
543 public override string ReadInnerXml ()
\r
545 if (ReadState != ReadState.Interactive)
\r
546 return String.Empty;
\r
548 switch (NodeType) {
\r
549 case XmlNodeType.Attribute:
\r
551 case XmlNodeType.Element:
\r
552 if (IsEmptyElement)
\r
553 return String.Empty;
\r
555 int startDepth = Depth;
\r
557 innerXmlBuilder.Length = 0;
\r
561 if (NodeType ==XmlNodeType.None)
\r
562 throw new InvalidOperationException ("unexpected end of xml.");
\r
563 else if (NodeType == XmlNodeType.EndElement && Depth == startDepth) {
\r
568 innerXmlBuilder.Append (GetCurrentTagMarkup ());
\r
570 string xml = innerXmlBuilder.ToString ();
\r
571 innerXmlBuilder.Length = 0;
\r
573 case XmlNodeType.None:
\r
574 // MS document is incorrect. Seems not to progress.
\r
575 return String.Empty;
\r
578 return String.Empty;
\r
582 StringBuilder atts = new StringBuilder ();
\r
583 private string GetCurrentTagMarkup ()
\r
585 switch (NodeType) {
\r
586 case XmlNodeType.CDATA:
\r
587 return String.Format ("<![CDATA[{0}]]>", Value.Replace ("]]>", "]]>"));
\r
588 case XmlNodeType.Text:
\r
589 return Value.Replace ("<", "<");
\r
590 case XmlNodeType.Comment:
\r
591 return String.Format ("<!--{0}-->", Value);
\r
592 case XmlNodeType.SignificantWhitespace:
\r
593 case XmlNodeType.Whitespace:
\r
595 case XmlNodeType.EndElement:
\r
596 return String.Format ("</{0}>", Name);
\r
599 bool isEmpty = IsEmptyElement;
\r
600 string name = Name;
\r
602 XPathNavigator temp = current.Clone ();
\r
603 while (temp.MoveToNextAttribute ())
\r
604 atts.AppendFormat (" {0}='{1}'", temp.Name, temp.Value.Replace ("'", "'"));
\r
605 if (!IsEmptyElement)
\r
606 return String.Format ("<{0}{1}>", name, atts);
\r
608 return String.Format ("<{0}{1} />", name, atts);
\r
611 // Arranged copy of XmlTextReader.ReadOuterXml()
\r
612 public override string ReadOuterXml ()
\r
614 if (ReadState != ReadState.Interactive)
\r
615 return String.Empty;
\r
617 switch (NodeType) {
\r
618 case XmlNodeType.Attribute:
\r
619 // strictly incompatible with MS... (it holds spaces attribute between name, value and "=" char (very trivial).
\r
620 return String.Format ("{0}={1}{2}{1}", Name, QuoteChar, ReadInnerXml ());
\r
621 case XmlNodeType.Element:
\r
622 bool isEmpty = IsEmptyElement;
\r
623 string name = Name;
\r
624 StringBuilder atts = new StringBuilder ();
\r
625 XPathNavigator temp = current.Clone ();
\r
626 while (temp.MoveToNextAttribute ())
\r
627 atts.AppendFormat (" {0}='{1}'", temp.Name, temp.Value.Replace ("'", "'"));
\r
630 return String.Format ("{0}{1}</{2}>", GetCurrentTagMarkup (), atts, ReadInnerXml (), name);
\r
632 return String.Format ("{0}", GetCurrentTagMarkup ());
\r
633 case XmlNodeType.None:
\r
634 // MS document is incorrect. Seems not to progress.
\r
635 return String.Empty;
\r
638 return String.Empty;
\r
643 public override string LookupNamespace (string prefix)
\r
645 XPathNavigator backup = current.Clone ();
\r
647 this.MoveToElement ();
\r
648 if (current.MoveToFirstNamespace ()) {
\r
650 if (current.LocalName == prefix)
\r
651 return current.Value;
\r
652 } while (current.MoveToNextNamespace ());
\r
660 // It does not support entity resolution.
\r
661 public override void ResolveEntity ()
\r
663 throw new InvalidOperationException ();
\r
666 public override bool ReadAttributeValue () {
\r
667 if (NodeType != XmlNodeType.Attribute)
\r
669 if (attributeValueConsumed)
\r
671 attributeValueConsumed = true;
\r