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
34 using System.Xml.Schema;
\r
35 using System.Xml.XPath;
\r
37 namespace Mono.Xml.XPath
\r
40 internal class XPathNavigatorReader : XmlReader
\r
42 public XPathNavigatorReader (XPathNavigator nav)
\r
44 current = nav.Clone ();
\r
47 XPathNavigator root;
\r
48 XPathNavigator current;
\r
52 bool attributeValueConsumed;
\r
53 StringBuilder readStringBuffer = new StringBuilder ();
\r
61 public override bool CanReadBinaryContent {
\r
62 get { return true; }
\r
65 public override bool CanReadValueChunk {
\r
66 get { return true; }
\r
69 public override XmlNodeType NodeType {
\r
71 if (ReadState != ReadState.Interactive)
\r
72 return XmlNodeType.None;
\r
74 return XmlNodeType.EndElement;
\r
75 if (attributeValueConsumed)
\r
76 // Is there any way to get other kind of nodes than Text?
\r
77 return XmlNodeType.Text;
\r
79 switch (current.NodeType) {
\r
80 case XPathNodeType.Namespace:
\r
81 case XPathNodeType.Attribute:
\r
82 return XmlNodeType.Attribute;
\r
83 case XPathNodeType.Comment:
\r
84 return XmlNodeType.Comment;
\r
85 case XPathNodeType.Element:
\r
86 return XmlNodeType.Element;
\r
87 case XPathNodeType.ProcessingInstruction:
\r
88 return XmlNodeType.ProcessingInstruction;
\r
89 case XPathNodeType.Root:
\r
90 // It is actually Document, but in XmlReader there is no such situation to return Document.
\r
91 return XmlNodeType.None;
\r
92 case XPathNodeType.SignificantWhitespace:
\r
93 return XmlNodeType.SignificantWhitespace;
\r
94 case XPathNodeType.Text:
\r
95 return XmlNodeType.Text;
\r
96 case XPathNodeType.Whitespace:
\r
97 return XmlNodeType.Whitespace;
\r
99 throw new InvalidOperationException (String.Format ("Current XPathNavigator status is {0} which is not acceptable to XmlReader.", current.NodeType));
\r
104 public override string Name {
\r
106 if (ReadState != ReadState.Interactive)
\r
107 return String.Empty;
\r
108 if (attributeValueConsumed)
\r
109 return String.Empty;
\r
110 else if (current.NodeType == XPathNodeType.Namespace)
\r
111 return current.Name == String.Empty ? "xmlns" : "xmlns:" + current.Name;
\r
113 return current.Name;
\r
117 public override string LocalName {
\r
119 if (ReadState != ReadState.Interactive)
\r
120 return String.Empty;
\r
121 if (attributeValueConsumed)
\r
122 return String.Empty;
\r
123 else if (current.NodeType == XPathNodeType.Namespace && current.LocalName == String.Empty)
\r
126 return current.LocalName;
\r
130 public override string NamespaceURI {
\r
132 if (ReadState != ReadState.Interactive)
\r
133 return String.Empty;
\r
134 if (attributeValueConsumed)
\r
135 return String.Empty;
\r
136 else if (current.NodeType == XPathNodeType.Namespace)
\r
137 return "http://www.w3.org/2000/xmlns/";
\r
139 return current.NamespaceURI;
\r
143 public override string Prefix {
\r
145 if (ReadState != ReadState.Interactive)
\r
146 return String.Empty;
\r
147 if (attributeValueConsumed)
\r
148 return String.Empty;
\r
149 else if (current.NodeType == XPathNodeType.Namespace && current.LocalName != String.Empty)
\r
152 return current.Prefix;
\r
156 public override bool HasValue {
\r
158 switch (current.NodeType) {
\r
159 case XPathNodeType.Namespace:
\r
160 case XPathNodeType.Attribute:
\r
161 case XPathNodeType.Comment:
\r
162 case XPathNodeType.ProcessingInstruction:
\r
163 case XPathNodeType.SignificantWhitespace:
\r
164 case XPathNodeType.Text:
\r
165 case XPathNodeType.Whitespace:
\r
172 public override int Depth {
\r
174 if (ReadState != ReadState.Interactive)
\r
177 if (NodeType == XmlNodeType.Attribute)
\r
179 if (attributeValueConsumed)
\r
185 public override string Value {
\r
187 if (ReadState != ReadState.Interactive)
\r
188 return String.Empty;
\r
189 switch (current.NodeType) {
\r
190 case XPathNodeType.Namespace:
\r
191 case XPathNodeType.Attribute:
\r
192 case XPathNodeType.Comment:
\r
193 case XPathNodeType.ProcessingInstruction:
\r
194 case XPathNodeType.SignificantWhitespace:
\r
195 case XPathNodeType.Text:
\r
196 case XPathNodeType.Whitespace:
\r
197 return current.Value;
\r
198 case XPathNodeType.Element:
\r
199 case XPathNodeType.Root:
\r
200 return String.Empty;
\r
202 throw new InvalidOperationException ("Current XPathNavigator status is {0} which is not acceptable to XmlReader.");
\r
207 public override string BaseURI {
\r
208 get { return current.BaseURI; }
\r
211 public override bool IsEmptyElement {
\r
213 if (ReadState != ReadState.Interactive)
\r
215 return current.IsEmptyElement;
\r
219 public override bool IsDefault {
\r
222 IXmlSchemaInfo si = current as IXmlSchemaInfo;
\r
223 return si != null && si.IsDefault;
\r
225 return false; // no way to check this.
\r
230 // It makes no sense.
\r
231 public override char QuoteChar {
\r
232 get { return '\"'; }
\r
236 public override IXmlSchemaInfo SchemaInfo {
\r
237 get { return current.SchemaInfo; }
\r
241 public override string XmlLang {
\r
242 get { return current.XmlLang; }
\r
245 // It is meaningless.
\r
246 public override XmlSpace XmlSpace {
\r
247 get { return XmlSpace.None; }
\r
250 public override int AttributeCount {
\r
251 get { return attributeCount; }
\r
254 private int GetAttributeCount ()
\r
256 if (ReadState != ReadState.Interactive)
\r
259 if (current.MoveToFirstAttribute ()) {
\r
262 } while (current.MoveToNextAttribute ());
\r
263 current.MoveToParent ();
\r
265 if (current.MoveToFirstNamespace (XPathNamespaceScope.Local)) {
\r
268 } while (current.MoveToNextNamespace (XPathNamespaceScope.Local));
\r
269 current.MoveToParent ();
\r
275 private bool MoveToAttributeNavigator (int i)
\r
277 if (ReadState != ReadState.Interactive)
\r
279 switch (current.NodeType) {
\r
280 case XPathNodeType.Namespace:
\r
281 case XPathNodeType.Attribute:
\r
282 this.MoveToElement ();
\r
283 goto case XPathNodeType.Element;
\r
284 case XPathNodeType.Element:
\r
286 if (MoveToFirstAttribute ()) {
\r
290 for (count++; this.MoveToNextAttribute (); count++) {
\r
299 public override string this [int i] {
\r
301 XPathNavigator backup = current.Clone ();
\r
303 if (MoveToAttributeNavigator (i))
\r
306 throw new ArgumentOutOfRangeException ();
\r
308 current.MoveTo (backup);
\r
314 private void SplitName (string name, out string localName, out string ns)
\r
316 if (name == "xmlns") {
\r
317 localName = "xmlns";
\r
318 ns = XmlNamespaceManager.XmlnsXmlns;
\r
323 int colon = name.IndexOf (':');
\r
325 localName = name.Substring (colon + 1, name.Length - colon - 1);
\r
326 ns = this.LookupNamespace (name.Substring (0, colon));
\r
327 if (name.Substring (0, colon) == "xmlns")
\r
328 ns = XmlNamespaceManager.XmlnsXmlns;
\r
332 public override string this [string name] {
\r
336 SplitName (name, out localName, out ns);
\r
337 return this [localName, ns];
\r
341 public override string this [string localName, string namespaceURI] {
\r
343 string v = current.GetAttribute (localName, namespaceURI);
\r
344 if (v != String.Empty)
\r
346 XPathNavigator tmp = current.Clone ();
\r
347 return tmp.MoveToAttribute (localName, namespaceURI) ? String.Empty : null;
\r
351 public override bool EOF {
\r
352 get { return ReadState == ReadState.EndOfFile; }
\r
355 public override ReadState ReadState {
\r
358 return ReadState.EndOfFile;
\r
360 return ReadState.Closed;
\r
362 return ReadState.Initial;
\r
363 return ReadState.Interactive;
\r
367 public override XmlNameTable NameTable {
\r
368 get { return current.NameTable; }
\r
374 public override string GetAttribute (string name)
\r
378 SplitName (name, out localName, out ns);
\r
379 return this [localName, ns];
\r
382 public override string GetAttribute (string localName, string namespaceURI)
\r
384 return this [localName, namespaceURI];
\r
387 public override string GetAttribute (int i)
\r
392 private bool CheckAttributeMove (bool b)
\r
395 attributeValueConsumed = false;
\r
399 public override bool MoveToAttribute (string name)
\r
403 SplitName (name, out localName, out ns);
\r
404 return MoveToAttribute (localName, ns);
\r
407 public override bool MoveToAttribute (string localName, string namespaceURI)
\r
409 bool isNS = namespaceURI == "http://www.w3.org/2000/xmlns/";
\r
410 XPathNavigator backup = null;
\r
411 switch (current.NodeType) {
\r
412 case XPathNodeType.Namespace:
\r
413 case XPathNodeType.Attribute:
\r
414 backup = current.Clone ();
\r
415 this.MoveToElement ();
\r
416 goto case XPathNodeType.Element;
\r
417 case XPathNodeType.Element:
\r
418 if (MoveToFirstAttribute ()) {
\r
420 bool match = false;
\r
422 if (localName == "xmlns")
\r
423 match = current.Name == String.Empty;
\r
425 match = localName == current.Name;
\r
428 match = current.LocalName == localName &&
\r
429 current.NamespaceURI == namespaceURI;
\r
431 attributeValueConsumed = false;
\r
434 } while (MoveToNextAttribute ());
\r
439 if (backup != null)
\r
444 public override bool MoveToFirstAttribute ()
\r
446 switch (current.NodeType) {
\r
447 case XPathNodeType.Element:
\r
448 if (CheckAttributeMove (current.MoveToFirstNamespace (XPathNamespaceScope.Local)))
\r
450 return CheckAttributeMove (current.MoveToFirstAttribute ());
\r
451 case XPathNodeType.Namespace:
\r
452 case XPathNodeType.Attribute:
\r
453 current.MoveToParent ();
\r
454 goto case XPathNodeType.Element;
\r
460 public override bool MoveToNextAttribute ()
\r
462 switch (current.NodeType) {
\r
463 case XPathNodeType.Element:
\r
464 return MoveToFirstAttribute ();
\r
465 case XPathNodeType.Namespace:
\r
466 if (CheckAttributeMove (current.MoveToNextNamespace (XPathNamespaceScope.Local)))
\r
468 XPathNavigator bak = current.Clone ();
\r
469 current.MoveToParent ();
\r
470 if (CheckAttributeMove (current.MoveToFirstAttribute ()))
\r
472 current.MoveTo (bak);
\r
474 case XPathNodeType.Attribute:
\r
475 return CheckAttributeMove (current.MoveToNextAttribute ());
\r
481 public override bool MoveToElement ()
\r
483 if (current.NodeType == XPathNodeType.Attribute ||
\r
484 current.NodeType == XPathNodeType.Namespace) {
\r
485 attributeValueConsumed = false;
\r
486 return current.MoveToParent ();
\r
491 public override void Close ()
\r
497 public override bool Read ()
\r
502 if (Binary != null)
\r
506 switch (ReadState) {
\r
507 case ReadState.Interactive:
\r
508 if ((IsEmptyElement || endElement) && root.IsSamePosition (current)) {
\r
513 case ReadState.EndOfFile:
\r
514 case ReadState.Closed:
\r
515 case ReadState.Error:
\r
517 case ReadState.Initial:
\r
519 root = current.Clone ();
\r
520 if (current.NodeType == XPathNodeType.Root &&
\r
521 !current.MoveToFirstChild ()) {
\r
522 endElement = false;
\r
526 attributeCount = GetAttributeCount ();
\r
532 if (endElement || !current.MoveToFirstChild ()) {
\r
533 // EndElement of current element.
\r
534 if (!endElement && !current.IsEmptyElement &&
\r
535 current.NodeType == XPathNodeType.Element)
\r
537 else if (!current.MoveToNext ()) {
\r
538 current.MoveToParent ();
\r
539 if (current.NodeType == XPathNodeType.Root) {
\r
540 endElement = false;
\r
544 endElement = (current.NodeType == XPathNodeType.Element);
\r
549 endElement = false;
\r
554 if (!endElement && current.NodeType == XPathNodeType.Element)
\r
555 attributeCount = GetAttributeCount ();
\r
557 attributeCount = 0;
\r
562 public override string ReadString ()
\r
564 readStringBuffer.Length = 0;
\r
566 switch (NodeType) {
\r
568 return String.Empty;
\r
569 case XmlNodeType.Element:
\r
570 if (IsEmptyElement)
\r
571 return String.Empty;
\r
574 switch (NodeType) {
\r
575 case XmlNodeType.Text:
\r
576 case XmlNodeType.CDATA:
\r
577 case XmlNodeType.Whitespace:
\r
578 case XmlNodeType.SignificantWhitespace:
\r
579 readStringBuffer.Append (Value);
\r
585 case XmlNodeType.Text:
\r
586 case XmlNodeType.CDATA:
\r
587 case XmlNodeType.Whitespace:
\r
588 case XmlNodeType.SignificantWhitespace:
\r
590 switch (NodeType) {
\r
591 case XmlNodeType.Text:
\r
592 case XmlNodeType.CDATA:
\r
593 case XmlNodeType.Whitespace:
\r
594 case XmlNodeType.SignificantWhitespace:
\r
595 readStringBuffer.Append (Value);
\r
603 string ret = readStringBuffer.ToString ();
\r
604 readStringBuffer.Length = 0;
\r
609 public override string LookupNamespace (string prefix)
\r
611 XPathNavigator backup = current.Clone ();
\r
613 this.MoveToElement ();
\r
614 if (current.NodeType != XPathNodeType.Element) // text etc.
\r
615 current.MoveToParent ();
\r
616 if (current.MoveToFirstNamespace ()) {
\r
618 if (current.LocalName == prefix)
\r
619 return current.Value;
\r
620 } while (current.MoveToNextNamespace ());
\r
628 // It does not support entity resolution.
\r
629 public override void ResolveEntity ()
\r
631 throw new InvalidOperationException ();
\r
634 public override bool ReadAttributeValue () {
\r
635 if (NodeType != XmlNodeType.Attribute)
\r
637 if (attributeValueConsumed)
\r
639 attributeValueConsumed = true;
\r