2010-06-21 Atsushi Enomoto <atsushi@ximian.com>
[mcs.git] / class / Mono.Xml.Ext / Mono.Xml.XPath / XPathNavigatorReader.cs
blob6fa769aa35fe2c179d1255c39f1f72f204f67254
1 //
2 // XPathNavigatorReader.cs
3 //
4 // Author:
5 // Atsushi Enomoto <ginga@kit.hi-ho.ne.jp>
6 //
7 // 2003 Atsushi Enomoto. No rights reserved.
8 // Copyright (C) 2004 Novell Inc.
11 // Permission is hereby granted, free of charge, to any person obtaining
12 // a copy of this software and associated documentation files (the
13 // "Software"), to deal in the Software without restriction, including
14 // without limitation the rights to use, copy, modify, merge, publish,
15 // distribute, sublicense, and/or sell copies of the Software, and to
16 // permit persons to whom the Software is furnished to do so, subject to
17 // the following conditions:
18 //
19 // The above copyright notice and this permission notice shall be
20 // included in all copies or substantial portions of the Software.
21 //
22 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
23 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
24 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
25 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
26 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
27 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
28 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
31 using System;
32 using System.Text;
33 using System.Xml;
34 using System.Xml.Schema;
35 using System.Xml.XPath;
37 namespace Mono.Xml.XPath
40 public class XPathNavigatorReader : XmlReader
42 public XPathNavigatorReader (XPathNavigator nav)
44 // It seems that this class have only to support linked
45 // node as its parameter
46 switch (nav.NodeType) {
47 case XPathNodeType.Attribute:
48 case XPathNodeType.Namespace:
49 throw new InvalidOperationException (String.Format ("NodeType {0} is not supported to read as a subtree of an XPathNavigator.", nav.NodeType));
51 root = nav.Clone ();
52 current = nav.Clone ();
55 XPathNavigator root;
56 XPathNavigator current;
57 bool started;
58 bool closed;
59 bool endElement;
60 bool attributeValueConsumed;
61 StringBuilder readStringBuffer = new StringBuilder ();
62 StringBuilder innerXmlBuilder = new StringBuilder ();
64 int depth = 0;
65 int attributeCount = 0;
66 bool eof;
67 bool nextIsEOF;
69 #region Properties
70 public override XmlNodeType NodeType
72 get {
73 if (ReadState != ReadState.Interactive)
74 return XmlNodeType.None;
75 if (endElement)
76 return XmlNodeType.EndElement;
77 if (attributeValueConsumed)
78 // Is there any way to get other kind of nodes than Text?
79 return XmlNodeType.Text;
81 switch (current.NodeType) {
82 case XPathNodeType.Namespace:
83 case XPathNodeType.Attribute:
84 return XmlNodeType.Attribute;
85 case XPathNodeType.Comment:
86 return XmlNodeType.Comment;
87 case XPathNodeType.Element:
88 return XmlNodeType.Element;
89 case XPathNodeType.ProcessingInstruction:
90 return XmlNodeType.ProcessingInstruction;
91 case XPathNodeType.Root:
92 // It is actually Document, but in XmlReader there is no such situation to return Document.
93 return XmlNodeType.None;
94 case XPathNodeType.SignificantWhitespace:
95 return XmlNodeType.SignificantWhitespace;
96 case XPathNodeType.Text:
97 return XmlNodeType.Text;
98 case XPathNodeType.Whitespace:
99 return XmlNodeType.Whitespace;
100 default:
101 throw new InvalidOperationException (String.Format ("Current XPathNavigator status is {0} which is not acceptable to XmlReader.", current.NodeType));
106 public override string Name {
107 get {
108 if (eof)
109 return String.Empty;
110 else if (current.NodeType == XPathNodeType.Namespace)
111 return current.Name == String.Empty ? "xmlns" : "xmlns:" + current.Name;
112 else
113 return current.Name;
117 public override string LocalName {
118 get {
119 if (eof)
120 return String.Empty;
121 else if (current.NodeType == XPathNodeType.Namespace && current.LocalName == String.Empty)
122 return "xmlns";
123 else
124 return current.LocalName;
128 public override string NamespaceURI {
129 get {
130 if (eof)
131 return String.Empty;
132 else if (current.NodeType == XPathNodeType.Namespace)
133 return "http://www.w3.org/2000/xmlns/";
134 else
135 return current.NamespaceURI;
139 public override string Prefix {
140 get {
141 if (eof)
142 return String.Empty;
143 else if (current.NodeType == XPathNodeType.Namespace && current.LocalName != String.Empty)
144 return "xmlns";
145 else
146 return current.Prefix;
150 public override bool HasValue {
151 get {
152 switch (current.NodeType) {
153 case XPathNodeType.Namespace:
154 case XPathNodeType.Attribute:
155 case XPathNodeType.Comment:
156 case XPathNodeType.ProcessingInstruction:
157 case XPathNodeType.SignificantWhitespace:
158 case XPathNodeType.Text:
159 case XPathNodeType.Whitespace:
160 return true;
162 return false;
166 public override int Depth {
167 get {
168 switch (ReadState) {
169 case ReadState.EndOfFile:
170 case ReadState.Initial:
171 case ReadState.Closed:
172 return 0;
174 return depth;
178 public override string Value {
179 get {
180 switch (current.NodeType) {
181 case XPathNodeType.Namespace:
182 case XPathNodeType.Attribute:
183 case XPathNodeType.Comment:
184 case XPathNodeType.ProcessingInstruction:
185 case XPathNodeType.SignificantWhitespace:
186 case XPathNodeType.Text:
187 case XPathNodeType.Whitespace:
188 return current.Value;
189 case XPathNodeType.Element:
190 case XPathNodeType.Root:
191 return String.Empty;
192 default:
193 throw new InvalidOperationException ("Current XPathNavigator status is {0} which is not acceptable to XmlReader.");
198 public override string BaseURI {
199 get { return current.BaseURI; }
202 public override bool IsEmptyElement {
203 get { return current.IsEmptyElement; }
206 public override bool IsDefault {
207 get {
208 #if NET_2_0
209 IXmlSchemaInfo si = current as IXmlSchemaInfo;
210 return si != null && si.IsDefault;
211 #else
212 return false; // no way to check this.
213 #endif
217 // It makes no sense.
218 public override char QuoteChar {
219 get { return '\"'; }
222 public override string XmlLang {
223 get { return current.XmlLang; }
226 // It is meaningless.
227 public override XmlSpace XmlSpace {
228 get { return XmlSpace.None; }
231 public override int AttributeCount {
232 get { return attributeCount; }
235 private int GetAttributeCount ()
237 int count = 0;
238 if (current.MoveToFirstAttribute ()) {
239 do {
240 count++;
241 } while (current.MoveToNextAttribute ());
242 current.MoveToParent ();
244 if (current.MoveToFirstNamespace (XPathNamespaceScope.Local)) {
245 do {
246 count++;
247 } while (current.MoveToNextNamespace (XPathNamespaceScope.Local));
248 current.MoveToParent ();
250 return count;
253 private bool MoveToAttributeNavigator (int i)
255 switch (current.NodeType) {
256 case XPathNodeType.Namespace:
257 case XPathNodeType.Attribute:
258 this.MoveToElement ();
259 goto case XPathNodeType.Element;
260 case XPathNodeType.Element:
261 int count = 0;
262 if (MoveToFirstAttribute ()) {
263 if (i == 0)
264 return true;
266 for (count++; this.MoveToNextAttribute (); count++) {
267 if (count == i)
268 return true;
270 break;
272 return false;
275 public override string this [int i] {
276 get {
277 XPathNavigator backup = current.Clone ();
278 try {
279 if (MoveToAttributeNavigator (i))
280 return Value;
281 else
282 throw new ArgumentOutOfRangeException ();
283 } finally {
284 current.MoveTo (backup);
289 private void SplitName (string name, out string localName, out string ns)
291 localName = name;
292 ns = String.Empty;
293 int colon = name.IndexOf (':');
294 if (colon > 0) {
295 localName = name.Substring (colon + 1, name.Length - colon - 1);
296 ns = this.LookupNamespace (name.Substring (0, colon));
300 public override string this [string name] {
301 get {
302 string localName;
303 string ns;
304 SplitName (name, out localName, out ns);
305 return this [localName, ns];
309 public override string this [string localName, string namespaceURI] {
310 get {
311 string v = current.GetAttribute (localName, namespaceURI);
312 if (v != String.Empty)
313 return v;
314 XPathNavigator tmp = current.Clone ();
315 return tmp.MoveToAttribute (localName, namespaceURI) ? String.Empty : null;
319 public override bool EOF {
320 get { return ReadState == ReadState.EndOfFile; }
323 public override ReadState ReadState {
324 get {
325 if (eof)
326 return ReadState.EndOfFile;
327 if (closed)
328 return ReadState.Closed;
329 else if (!started)
330 return ReadState.Initial;
331 return ReadState.Interactive;
335 public override XmlNameTable NameTable {
336 get { return current.NameTable; }
338 #endregion
340 #region Methods
342 public override string GetAttribute (string name)
344 string localName;
345 string ns;
346 SplitName (name, out localName, out ns);
347 return this [localName, ns];
350 public override string GetAttribute (string localName, string namespaceURI)
352 return this [localName, namespaceURI];
355 public override string GetAttribute (int i)
357 return this [i];
360 private bool CheckAttributeMove (bool b)
362 if (b)
363 attributeValueConsumed = false;
364 return b;
367 public override bool MoveToAttribute (string name)
369 string localName;
370 string ns;
371 SplitName (name, out localName, out ns);
372 return CheckAttributeMove (MoveToAttribute (localName, ns));
375 public override bool MoveToAttribute (string localName, string namespaceURI)
377 XPathNavigator backup = null;
378 switch (current.NodeType) {
379 case XPathNodeType.Attribute:
380 backup = current.Clone ();
381 this.MoveToElement ();
382 goto case XPathNodeType.Element;
383 case XPathNodeType.Element:
384 while (MoveToNextAttribute ())
385 if (current.LocalName == localName && current.NamespaceURI == namespaceURI) {
386 attributeValueConsumed = false;
387 return true;
389 break;
391 if (backup != null)
392 current = backup;
393 return false;
396 public override void MoveToAttribute (int i)
398 if (!MoveToAttributeNavigator (i))
399 throw new ArgumentOutOfRangeException ();
402 public override bool MoveToFirstAttribute ()
404 bool b = CheckAttributeMove (current.MoveToFirstNamespace (XPathNamespaceScope.Local));
405 if (b)
406 return true;
407 return CheckAttributeMove (current.MoveToFirstAttribute ());
410 public override bool MoveToNextAttribute ()
412 if (current.NodeType == XPathNodeType.Namespace) {
413 bool b = CheckAttributeMove (current.MoveToNextNamespace (XPathNamespaceScope.Local));
414 if (b)
415 return true;
416 current.MoveToParent ();
417 b = CheckAttributeMove (current.MoveToFirstAttribute ());
418 if (b)
419 return true;
421 return CheckAttributeMove (current.MoveToNextAttribute ());
424 public override bool MoveToElement ()
426 if (current.NodeType == XPathNodeType.Attribute ||
427 current.NodeType == XPathNodeType.Namespace) {
428 attributeValueConsumed = false;
429 return current.MoveToParent ();
431 return false;
434 public override void Close ()
436 closed = true;
437 eof = true;
440 public override bool Read ()
442 switch (ReadState) {
443 case ReadState.EndOfFile:
444 case ReadState.Closed:
445 case ReadState.Error:
446 return false;
447 case ReadState.Initial:
448 started = true;
449 switch (current.NodeType) {
450 case XPathNodeType.Root:
451 // recurse, but as Interactive
452 return Read ();
453 case XPathNodeType.Element:
454 if (current.IsEmptyElement)
455 nextIsEOF = true;
456 attributeCount = GetAttributeCount ();
457 return true;
458 default:
459 nextIsEOF = true;
460 return true;
462 break;
465 if (nextIsEOF) {
466 nextIsEOF = false;
467 eof = true;
468 return false;
471 MoveToElement ();
473 if (endElement || !current.MoveToFirstChild ()) {
474 if (current.IsSamePosition (root)) { // It should happen only when the root node was empty.
475 eof = true;
476 return false;
478 if (!current.MoveToNext ()) {
479 current.MoveToParent ();
480 depth--;
481 endElement = (current.NodeType == XPathNodeType.Element);
482 if (current.IsSamePosition (root)) {
483 if (current.NodeType == XPathNodeType.Element)
484 nextIsEOF = true;
485 else {
486 endElement = false;
487 eof = true;
488 return false;
491 } else
492 endElement = false;
494 else if (!endElement)
495 depth++;
496 attributeCount = GetAttributeCount ();
497 return true;
500 public override string ReadString ()
502 readStringBuffer.Length = 0;
504 switch (NodeType) {
505 default:
506 return String.Empty;
507 case XmlNodeType.Element:
508 if (IsEmptyElement)
509 return String.Empty;
510 do {
511 Read ();
512 switch (NodeType) {
513 case XmlNodeType.Text:
514 case XmlNodeType.CDATA:
515 case XmlNodeType.Whitespace:
516 case XmlNodeType.SignificantWhitespace:
517 readStringBuffer.Append (Value);
518 continue;
520 break;
521 } while (true);
522 break;
523 case XmlNodeType.Text:
524 case XmlNodeType.CDATA:
525 case XmlNodeType.Whitespace:
526 case XmlNodeType.SignificantWhitespace:
527 do {
528 switch (NodeType) {
529 case XmlNodeType.Text:
530 case XmlNodeType.CDATA:
531 case XmlNodeType.Whitespace:
532 case XmlNodeType.SignificantWhitespace:
533 readStringBuffer.Append (Value);
534 Read ();
535 continue;
537 break;
538 } while (true);
539 break;
541 string ret = readStringBuffer.ToString ();
542 readStringBuffer.Length = 0;
543 return ret;
546 #if NET_1_1
547 #else
548 public override string ReadInnerXml ()
550 if (ReadState != ReadState.Interactive)
551 return String.Empty;
553 switch (NodeType) {
554 case XmlNodeType.Attribute:
555 return Value;
556 case XmlNodeType.Element:
557 if (IsEmptyElement)
558 return String.Empty;
560 int startDepth = Depth;
562 innerXmlBuilder.Length = 0;
563 bool loop = true;
564 do {
565 Read ();
566 if (NodeType ==XmlNodeType.None)
567 throw new InvalidOperationException ("unexpected end of xml.");
568 else if (NodeType == XmlNodeType.EndElement && Depth == startDepth) {
569 loop = false;
570 Read ();
572 else
573 innerXmlBuilder.Append (GetCurrentTagMarkup ());
574 } while (loop);
575 string xml = innerXmlBuilder.ToString ();
576 innerXmlBuilder.Length = 0;
577 return xml;
578 case XmlNodeType.None:
579 // MS document is incorrect. Seems not to progress.
580 return String.Empty;
581 default:
582 Read ();
583 return String.Empty;
587 StringBuilder atts = new StringBuilder ();
588 private string GetCurrentTagMarkup ()
590 switch (NodeType) {
591 case XmlNodeType.CDATA:
592 return String.Format ("<![CDATA[{0}]]>", Value.Replace ("]]>", "]]&gt;"));
593 case XmlNodeType.Text:
594 return Value.Replace ("<", "&lt;");
595 case XmlNodeType.Comment:
596 return String.Format ("<!--{0}-->", Value);
597 case XmlNodeType.SignificantWhitespace:
598 case XmlNodeType.Whitespace:
599 return Value;
600 case XmlNodeType.EndElement:
601 return String.Format ("</{0}>", Name);
604 bool isEmpty = IsEmptyElement;
605 string name = Name;
606 atts.Length = 0;
607 XPathNavigator temp = current.Clone ();
608 while (temp.MoveToNextAttribute ())
609 atts.AppendFormat (" {0}='{1}'", temp.Name, temp.Value.Replace ("'", "&apos;"));
610 if (!IsEmptyElement)
611 return String.Format ("<{0}{1}>", name, atts);
612 else
613 return String.Format ("<{0}{1} />", name, atts);
616 // Arranged copy of XmlTextReader.ReadOuterXml()
617 public override string ReadOuterXml ()
619 if (ReadState != ReadState.Interactive)
620 return String.Empty;
622 switch (NodeType) {
623 case XmlNodeType.Attribute:
624 // strictly incompatible with MS... (it holds spaces attribute between name, value and "=" char (very trivial).
625 return String.Format ("{0}={1}{2}{1}", Name, QuoteChar, ReadInnerXml ());
626 case XmlNodeType.Element:
627 bool isEmpty = IsEmptyElement;
628 string name = Name;
629 StringBuilder atts = new StringBuilder ();
630 XPathNavigator temp = current.Clone ();
631 while (temp.MoveToNextAttribute ())
632 atts.AppendFormat (" {0}='{1}'", temp.Name, temp.Value.Replace ("'", "&apos;"));
634 if (!isEmpty)
635 return String.Format ("{0}{1}</{2}>", GetCurrentTagMarkup (), atts, ReadInnerXml (), name);
636 else
637 return String.Format ("{0}", GetCurrentTagMarkup ());
638 case XmlNodeType.None:
639 // MS document is incorrect. Seems not to progress.
640 return String.Empty;
641 default:
642 Read ();
643 return String.Empty;
646 #endif
648 public override string LookupNamespace (string prefix)
650 XPathNavigator backup = current.Clone ();
651 try {
652 this.MoveToElement ();
653 if (current.MoveToFirstNamespace ()) {
654 do {
655 if (current.LocalName == prefix)
656 return current.Value;
657 } while (current.MoveToNextNamespace ());
659 return null;
660 } finally {
661 current = backup;
665 // It does not support entity resolution.
666 public override void ResolveEntity ()
668 throw new InvalidOperationException ();
671 public override bool ReadAttributeValue () {
672 if (NodeType != XmlNodeType.Attribute)
673 return false;
674 if (attributeValueConsumed)
675 return false;
676 attributeValueConsumed = true;
677 return true;
679 #endregion