**** Merged from MCS ****
[mono-project.git] / mcs / class / System.XML / Mono.Xml.XPath / XPathNavigatorReader.cs
blobed1f7b14f917310e70c98bf066a24e5eb96f55e9
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.
30 #if NET_2_0
32 using System;
33 using System.Text;
34 using System.Xml;
35 using System.Xml.Schema;
36 using System.Xml.XPath;
38 namespace Mono.Xml.XPath
41 internal class XPathNavigatorReader : XmlReader
43 public XPathNavigatorReader (XPathNavigator nav)
45 // It seems that this class have only to support linked
46 // node as its parameter
47 switch (nav.NodeType) {
48 case XPathNodeType.Attribute:
49 case XPathNodeType.Namespace:
50 throw new InvalidOperationException (String.Format ("NodeType {0} is not supported to read as a subtree of an XPathNavigator.", nav.NodeType));
52 root = nav.Clone ();
53 current = nav.Clone ();
56 XPathNavigator root;
57 XPathNavigator current;
58 bool started;
59 bool closed;
60 bool endElement;
61 bool attributeValueConsumed;
62 StringBuilder readStringBuffer = new StringBuilder ();
63 StringBuilder innerXmlBuilder = new StringBuilder ();
65 int depth = 0;
66 int attributeCount = 0;
67 bool eof;
68 bool nextIsEOF;
70 #region Properties
71 public override XmlNodeType NodeType
73 get {
74 if (ReadState != ReadState.Interactive)
75 return XmlNodeType.None;
76 if (endElement)
77 return XmlNodeType.EndElement;
78 if (attributeValueConsumed)
79 // Is there any way to get other kind of nodes than Text?
80 return XmlNodeType.Text;
82 switch (current.NodeType) {
83 case XPathNodeType.Namespace:
84 case XPathNodeType.Attribute:
85 return XmlNodeType.Attribute;
86 case XPathNodeType.Comment:
87 return XmlNodeType.Comment;
88 case XPathNodeType.Element:
89 return XmlNodeType.Element;
90 case XPathNodeType.ProcessingInstruction:
91 return XmlNodeType.ProcessingInstruction;
92 case XPathNodeType.Root:
93 // It is actually Document, but in XmlReader there is no such situation to return Document.
94 return XmlNodeType.None;
95 case XPathNodeType.SignificantWhitespace:
96 return XmlNodeType.SignificantWhitespace;
97 case XPathNodeType.Text:
98 return XmlNodeType.Text;
99 case XPathNodeType.Whitespace:
100 return XmlNodeType.Whitespace;
101 default:
102 throw new InvalidOperationException (String.Format ("Current XPathNavigator status is {0} which is not acceptable to XmlReader.", current.NodeType));
107 public override string Name {
108 get {
109 if (eof)
110 return String.Empty;
111 else if (current.NodeType == XPathNodeType.Namespace)
112 return current.Name == String.Empty ? "xmlns" : "xmlns:" + current.Name;
113 else
114 return current.Name;
118 public override string LocalName {
119 get {
120 if (eof)
121 return String.Empty;
122 else if (current.NodeType == XPathNodeType.Namespace && current.LocalName == String.Empty)
123 return "xmlns";
124 else
125 return current.LocalName;
129 public override string NamespaceURI {
130 get {
131 if (eof)
132 return String.Empty;
133 else if (current.NodeType == XPathNodeType.Namespace)
134 return XmlNamespaceManager.XmlnsXmlns;
135 else
136 return current.NamespaceURI;
140 public override string Prefix {
141 get {
142 if (eof)
143 return String.Empty;
144 else if (current.NodeType == XPathNodeType.Namespace && current.LocalName != String.Empty)
145 return "xmlns";
146 else
147 return current.Prefix;
151 public override bool HasValue {
152 get {
153 switch (current.NodeType) {
154 case XPathNodeType.Namespace:
155 case XPathNodeType.Attribute:
156 case XPathNodeType.Comment:
157 case XPathNodeType.ProcessingInstruction:
158 case XPathNodeType.SignificantWhitespace:
159 case XPathNodeType.Text:
160 case XPathNodeType.Whitespace:
161 return true;
163 return false;
167 public override int Depth {
168 get {
169 switch (ReadState) {
170 case ReadState.EndOfFile:
171 case ReadState.Initial:
172 case ReadState.Closed:
173 return 0;
175 return depth;
179 public override string Value {
180 get {
181 switch (current.NodeType) {
182 case XPathNodeType.Namespace:
183 case XPathNodeType.Attribute:
184 case XPathNodeType.Comment:
185 case XPathNodeType.ProcessingInstruction:
186 case XPathNodeType.SignificantWhitespace:
187 case XPathNodeType.Text:
188 case XPathNodeType.Whitespace:
189 return current.Value;
190 case XPathNodeType.Element:
191 case XPathNodeType.Root:
192 return String.Empty;
193 default:
194 throw new InvalidOperationException ("Current XPathNavigator status is {0} which is not acceptable to XmlReader.");
199 public override string BaseURI {
200 get { return current.BaseURI; }
203 public override bool IsEmptyElement {
204 get { return current.IsEmptyElement; }
207 public override bool IsDefault {
208 get {
209 IXmlSchemaInfo si = current as IXmlSchemaInfo;
210 return si != null && si.IsDefault;
214 // It makes no sense.
215 public override char QuoteChar {
216 get { return '\"'; }
219 public override string XmlLang {
220 get { return current.XmlLang; }
223 // It is meaningless.
224 public override XmlSpace XmlSpace {
225 get { return XmlSpace.None; }
228 public override int AttributeCount {
229 get { return attributeCount; }
232 private int GetAttributeCount ()
234 int count = 0;
235 if (current.MoveToFirstAttribute ()) {
236 do {
237 count++;
238 } while (current.MoveToNextAttribute ());
239 current.MoveToParent ();
241 if (current.MoveToFirstNamespace (XPathNamespaceScope.Local)) {
242 do {
243 count++;
244 } while (current.MoveToNextNamespace (XPathNamespaceScope.Local));
245 current.MoveToParent ();
247 return count;
250 private bool MoveToAttributeNavigator (int i)
252 switch (current.NodeType) {
253 case XPathNodeType.Namespace:
254 case XPathNodeType.Attribute:
255 this.MoveToElement ();
256 goto case XPathNodeType.Element;
257 case XPathNodeType.Element:
258 int count = 0;
259 if (MoveToFirstAttribute ()) {
260 if (i == 0)
261 return true;
263 for (count++; this.MoveToNextAttribute (); count++) {
264 if (count == i)
265 return true;
267 break;
269 return false;
272 public override string this [int i] {
273 get {
274 XPathNavigator backup = current.Clone ();
275 try {
276 if (MoveToAttributeNavigator (i))
277 return Value;
278 else
279 throw new ArgumentOutOfRangeException ();
280 } finally {
281 current.MoveTo (backup);
286 private void SplitName (string name, out string localName, out string ns)
288 localName = name;
289 ns = String.Empty;
290 int colon = name.IndexOf (':');
291 if (colon > 0) {
292 localName = name.Substring (colon + 1, name.Length - colon - 1);
293 ns = this.LookupNamespace (name.Substring (0, colon));
297 public override string this [string name] {
298 get {
299 string localName;
300 string ns;
301 SplitName (name, out localName, out ns);
302 return this [localName, ns];
306 public override string this [string localName, string namespaceURI] {
307 get {
308 string v = current.GetAttribute (localName, namespaceURI);
309 if (v != String.Empty)
310 return v;
311 XPathNavigator tmp = current.Clone ();
312 return tmp.MoveToAttribute (localName, namespaceURI) ? String.Empty : null;
316 public override bool EOF {
317 get { return ReadState == ReadState.EndOfFile; }
320 public override ReadState ReadState {
321 get {
322 if (eof)
323 return ReadState.EndOfFile;
324 if (closed)
325 return ReadState.Closed;
326 else if (!started)
327 return ReadState.Initial;
328 return ReadState.Interactive;
332 public override XmlNameTable NameTable {
333 get { return current.NameTable; }
335 #endregion
337 #region Methods
339 public override string GetAttribute (string name)
341 string localName;
342 string ns;
343 SplitName (name, out localName, out ns);
344 return this [localName, ns];
347 public override string GetAttribute (string localName, string namespaceURI)
349 return this [localName, namespaceURI];
352 public override string GetAttribute (int i)
354 return this [i];
357 private bool CheckAttributeMove (bool b)
359 if (b)
360 attributeValueConsumed = false;
361 return b;
364 public override bool MoveToAttribute (string name)
366 string localName;
367 string ns;
368 SplitName (name, out localName, out ns);
369 return CheckAttributeMove (MoveToAttribute (localName, ns));
372 public override bool MoveToAttribute (string localName, string namespaceURI)
374 XPathNavigator backup = null;
375 switch (current.NodeType) {
376 case XPathNodeType.Attribute:
377 backup = current.Clone ();
378 this.MoveToElement ();
379 goto case XPathNodeType.Element;
380 case XPathNodeType.Element:
381 while (MoveToNextAttribute ())
382 if (current.LocalName == localName && current.NamespaceURI == namespaceURI) {
383 attributeValueConsumed = false;
384 return true;
386 break;
388 if (backup != null)
389 current = backup;
390 return false;
393 public override void MoveToAttribute (int i)
395 if (!MoveToAttributeNavigator (i))
396 throw new ArgumentOutOfRangeException ();
399 public override bool MoveToFirstAttribute ()
401 bool b = CheckAttributeMove (current.MoveToFirstNamespace (XPathNamespaceScope.Local));
402 if (b)
403 return true;
404 return CheckAttributeMove (current.MoveToFirstAttribute ());
407 public override bool MoveToNextAttribute ()
409 if (current.NodeType == XPathNodeType.Namespace) {
410 bool b = CheckAttributeMove (current.MoveToNextNamespace (XPathNamespaceScope.Local));
411 if (b)
412 return true;
413 current.MoveToParent ();
414 b = CheckAttributeMove (current.MoveToFirstAttribute ());
415 if (b)
416 return true;
418 return CheckAttributeMove (current.MoveToNextAttribute ());
421 public override bool MoveToElement ()
423 if (current.NodeType == XPathNodeType.Attribute ||
424 current.NodeType == XPathNodeType.Namespace) {
425 attributeValueConsumed = false;
426 return current.MoveToParent ();
428 return false;
431 public override void Close ()
433 closed = true;
434 eof = true;
437 public override bool Read ()
439 switch (ReadState) {
440 case ReadState.EndOfFile:
441 case ReadState.Closed:
442 case ReadState.Error:
443 return false;
444 case ReadState.Initial:
445 started = true;
446 switch (current.NodeType) {
447 case XPathNodeType.Root:
448 // recurse, but as Interactive
449 return Read ();
450 case XPathNodeType.Element:
451 if (current.IsEmptyElement)
452 nextIsEOF = true;
453 attributeCount = GetAttributeCount ();
454 return true;
455 default:
456 nextIsEOF = true;
457 return true;
459 break;
462 if (nextIsEOF) {
463 nextIsEOF = false;
464 eof = true;
465 return false;
468 MoveToElement ();
470 if (endElement || !current.MoveToFirstChild ()) {
471 if (current.IsSamePosition (root)) { // It should happen only when the root node was empty.
472 eof = true;
473 return false;
475 if (!current.MoveToNext ()) {
476 current.MoveToParent ();
477 depth--;
478 endElement = (current.NodeType == XPathNodeType.Element);
479 if (current.IsSamePosition (root)) {
480 if (current.NodeType == XPathNodeType.Element)
481 nextIsEOF = true;
482 else {
483 endElement = false;
484 eof = true;
485 return false;
488 } else
489 endElement = false;
491 else if (!endElement)
492 depth++;
493 attributeCount = GetAttributeCount ();
494 return true;
497 public override string ReadString ()
499 readStringBuffer.Length = 0;
501 switch (NodeType) {
502 default:
503 return String.Empty;
504 case XmlNodeType.Element:
505 if (IsEmptyElement)
506 return String.Empty;
507 do {
508 Read ();
509 switch (NodeType) {
510 case XmlNodeType.Text:
511 case XmlNodeType.CDATA:
512 case XmlNodeType.Whitespace:
513 case XmlNodeType.SignificantWhitespace:
514 readStringBuffer.Append (Value);
515 continue;
517 break;
518 } while (true);
519 break;
520 case XmlNodeType.Text:
521 case XmlNodeType.CDATA:
522 case XmlNodeType.Whitespace:
523 case XmlNodeType.SignificantWhitespace:
524 do {
525 switch (NodeType) {
526 case XmlNodeType.Text:
527 case XmlNodeType.CDATA:
528 case XmlNodeType.Whitespace:
529 case XmlNodeType.SignificantWhitespace:
530 readStringBuffer.Append (Value);
531 Read ();
532 continue;
534 break;
535 } while (true);
536 break;
538 string ret = readStringBuffer.ToString ();
539 readStringBuffer.Length = 0;
540 return ret;
543 #if NET_1_1
544 #else
545 public override string ReadInnerXml ()
547 if (ReadState != ReadState.Interactive)
548 return String.Empty;
550 switch (NodeType) {
551 case XmlNodeType.Attribute:
552 return Value;
553 case XmlNodeType.Element:
554 if (IsEmptyElement)
555 return String.Empty;
557 int startDepth = Depth;
559 innerXmlBuilder.Length = 0;
560 bool loop = true;
561 do {
562 Read ();
563 if (NodeType ==XmlNodeType.None)
564 throw new InvalidOperationException ("unexpected end of xml.");
565 else if (NodeType == XmlNodeType.EndElement && Depth == startDepth) {
566 loop = false;
567 Read ();
569 else
570 innerXmlBuilder.Append (GetCurrentTagMarkup ());
571 } while (loop);
572 string xml = innerXmlBuilder.ToString ();
573 innerXmlBuilder.Length = 0;
574 return xml;
575 case XmlNodeType.None:
576 // MS document is incorrect. Seems not to progress.
577 return String.Empty;
578 default:
579 Read ();
580 return String.Empty;
584 StringBuilder atts = new StringBuilder ();
585 private string GetCurrentTagMarkup ()
587 switch (NodeType) {
588 case XmlNodeType.CDATA:
589 return String.Format ("<![CDATA[{0}]]>", Value.Replace ("]]>", "]]&gt;"));
590 case XmlNodeType.Text:
591 return Value.Replace ("<", "&lt;");
592 case XmlNodeType.Comment:
593 return String.Format ("<!--{0}-->", Value);
594 case XmlNodeType.SignificantWhitespace:
595 case XmlNodeType.Whitespace:
596 return Value;
597 case XmlNodeType.EndElement:
598 return String.Format ("</{0}>", Name);
601 bool isEmpty = IsEmptyElement;
602 string name = Name;
603 atts.Length = 0;
604 XPathNavigator temp = current.Clone ();
605 while (temp.MoveToNextAttribute ())
606 atts.AppendFormat (" {0}='{1}'", temp.Name, temp.Value.Replace ("'", "&apos;"));
607 if (!IsEmptyElement)
608 return String.Format ("<{0}{1}>", name, atts);
609 else
610 return String.Format ("<{0}{1} />", name, atts);
613 // Arranged copy of XmlTextReader.ReadOuterXml()
614 public override string ReadOuterXml ()
616 if (ReadState != ReadState.Interactive)
617 return String.Empty;
619 switch (NodeType) {
620 case XmlNodeType.Attribute:
621 // strictly incompatible with MS... (it holds spaces attribute between name, value and "=" char (very trivial).
622 return String.Format ("{0}={1}{2}{1}", Name, QuoteChar, ReadInnerXml ());
623 case XmlNodeType.Element:
624 bool isEmpty = IsEmptyElement;
625 string name = Name;
626 StringBuilder atts = new StringBuilder ();
627 XPathNavigator temp = current.Clone ();
628 while (temp.MoveToNextAttribute ())
629 atts.AppendFormat (" {0}='{1}'", temp.Name, temp.Value.Replace ("'", "&apos;"));
631 if (!isEmpty)
632 return String.Format ("{0}{1}</{2}>", GetCurrentTagMarkup (), atts, ReadInnerXml (), name);
633 else
634 return String.Format ("{0}", GetCurrentTagMarkup ());
635 case XmlNodeType.None:
636 // MS document is incorrect. Seems not to progress.
637 return String.Empty;
638 default:
639 Read ();
640 return String.Empty;
643 #endif
645 public override string LookupNamespace (string prefix)
647 XPathNavigator backup = current.Clone ();
648 try {
649 this.MoveToElement ();
650 if (current.MoveToFirstNamespace ()) {
651 do {
652 if (current.LocalName == prefix)
653 return current.Value;
654 } while (current.MoveToNextNamespace ());
656 return null;
657 } finally {
658 current = backup;
662 // It does not support entity resolution.
663 public override void ResolveEntity ()
665 throw new InvalidOperationException ();
668 public override bool ReadAttributeValue () {
669 if (NodeType != XmlNodeType.Attribute)
670 return false;
671 if (attributeValueConsumed)
672 return false;
673 attributeValueConsumed = true;
674 return true;
676 #endregion
680 #endif