2 // XPathNavigatorReader.cs
5 // Atsushi Enomoto <ginga@kit.hi-ho.ne.jp>
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:
19 // The above copyright notice and this permission notice shall be
20 // included in all copies or substantial portions of the Software.
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.
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
));
52 current
= nav
.Clone ();
56 XPathNavigator current
;
60 bool attributeValueConsumed
;
61 StringBuilder readStringBuffer
= new StringBuilder ();
62 StringBuilder innerXmlBuilder
= new StringBuilder ();
65 int attributeCount
= 0;
70 public override XmlNodeType NodeType
73 if (ReadState
!= ReadState
.Interactive
)
74 return XmlNodeType
.None
;
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
;
101 throw new InvalidOperationException (String
.Format ("Current XPathNavigator status is {0} which is not acceptable to XmlReader.", current
.NodeType
));
106 public override string Name
{
110 else if (current
.NodeType
== XPathNodeType
.Namespace
)
111 return current
.Name
== String
.Empty
? "xmlns" : "xmlns:" + current
.Name
;
117 public override string LocalName
{
121 else if (current
.NodeType
== XPathNodeType
.Namespace
&& current
.LocalName
== String
.Empty
)
124 return current
.LocalName
;
128 public override string NamespaceURI
{
132 else if (current
.NodeType
== XPathNodeType
.Namespace
)
133 return "http://www.w3.org/2000/xmlns/";
135 return current
.NamespaceURI
;
139 public override string Prefix
{
143 else if (current
.NodeType
== XPathNodeType
.Namespace
&& current
.LocalName
!= String
.Empty
)
146 return current
.Prefix
;
150 public override bool HasValue
{
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
:
166 public override int Depth
{
169 case ReadState
.EndOfFile
:
170 case ReadState
.Initial
:
171 case ReadState
.Closed
:
178 public override string Value
{
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
:
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
{
209 IXmlSchemaInfo si
= current
as IXmlSchemaInfo
;
210 return si
!= null && si
.IsDefault
;
212 return false; // no way to check this.
217 // It makes no sense.
218 public override char QuoteChar
{
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 ()
238 if (current
.MoveToFirstAttribute ()) {
241 } while (current
.MoveToNextAttribute ());
242 current
.MoveToParent ();
244 if (current
.MoveToFirstNamespace (XPathNamespaceScope
.Local
)) {
247 } while (current
.MoveToNextNamespace (XPathNamespaceScope
.Local
));
248 current
.MoveToParent ();
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
:
262 if (MoveToFirstAttribute ()) {
266 for (count
++; this.MoveToNextAttribute (); count
++) {
275 public override string this [int i
] {
277 XPathNavigator backup
= current
.Clone ();
279 if (MoveToAttributeNavigator (i
))
282 throw new ArgumentOutOfRangeException ();
284 current
.MoveTo (backup
);
289 private void SplitName (string name
, out string localName
, out string ns
)
293 int colon
= name
.IndexOf (':');
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
] {
304 SplitName (name
, out localName
, out ns
);
305 return this [localName
, ns
];
309 public override string this [string localName
, string namespaceURI
] {
311 string v
= current
.GetAttribute (localName
, namespaceURI
);
312 if (v
!= String
.Empty
)
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
{
326 return ReadState
.EndOfFile
;
328 return ReadState
.Closed
;
330 return ReadState
.Initial
;
331 return ReadState
.Interactive
;
335 public override XmlNameTable NameTable
{
336 get { return current.NameTable; }
342 public override string GetAttribute (string name
)
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
)
360 private bool CheckAttributeMove (bool b
)
363 attributeValueConsumed
= false;
367 public override bool MoveToAttribute (string name
)
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;
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
));
407 return CheckAttributeMove (current
.MoveToFirstAttribute ());
410 public override bool MoveToNextAttribute ()
412 if (current
.NodeType
== XPathNodeType
.Namespace
) {
413 bool b
= CheckAttributeMove (current
.MoveToNextNamespace (XPathNamespaceScope
.Local
));
416 current
.MoveToParent ();
417 b
= CheckAttributeMove (current
.MoveToFirstAttribute ());
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 ();
434 public override void Close ()
440 public override bool Read ()
443 case ReadState
.EndOfFile
:
444 case ReadState
.Closed
:
445 case ReadState
.Error
:
447 case ReadState
.Initial
:
449 switch (current
.NodeType
) {
450 case XPathNodeType
.Root
:
451 // recurse, but as Interactive
453 case XPathNodeType
.Element
:
454 if (current
.IsEmptyElement
)
456 attributeCount
= GetAttributeCount ();
473 if (endElement
|| !current
.MoveToFirstChild ()) {
474 if (current
.IsSamePosition (root
)) { // It should happen only when the root node was empty.
478 if (!current
.MoveToNext ()) {
479 current
.MoveToParent ();
481 endElement
= (current
.NodeType
== XPathNodeType
.Element
);
482 if (current
.IsSamePosition (root
)) {
483 if (current
.NodeType
== XPathNodeType
.Element
)
494 else if (!endElement
)
496 attributeCount
= GetAttributeCount ();
500 public override string ReadString ()
502 readStringBuffer
.Length
= 0;
507 case XmlNodeType
.Element
:
513 case XmlNodeType
.Text
:
514 case XmlNodeType
.CDATA
:
515 case XmlNodeType
.Whitespace
:
516 case XmlNodeType
.SignificantWhitespace
:
517 readStringBuffer
.Append (Value
);
523 case XmlNodeType
.Text
:
524 case XmlNodeType
.CDATA
:
525 case XmlNodeType
.Whitespace
:
526 case XmlNodeType
.SignificantWhitespace
:
529 case XmlNodeType
.Text
:
530 case XmlNodeType
.CDATA
:
531 case XmlNodeType
.Whitespace
:
532 case XmlNodeType
.SignificantWhitespace
:
533 readStringBuffer
.Append (Value
);
541 string ret
= readStringBuffer
.ToString ();
542 readStringBuffer
.Length
= 0;
548 public override string ReadInnerXml ()
550 if (ReadState
!= ReadState
.Interactive
)
554 case XmlNodeType
.Attribute
:
556 case XmlNodeType
.Element
:
560 int startDepth
= Depth
;
562 innerXmlBuilder
.Length
= 0;
566 if (NodeType
==XmlNodeType
.None
)
567 throw new InvalidOperationException ("unexpected end of xml.");
568 else if (NodeType
== XmlNodeType
.EndElement
&& Depth
== startDepth
) {
573 innerXmlBuilder
.Append (GetCurrentTagMarkup ());
575 string xml
= innerXmlBuilder
.ToString ();
576 innerXmlBuilder
.Length
= 0;
578 case XmlNodeType
.None
:
579 // MS document is incorrect. Seems not to progress.
587 StringBuilder atts
= new StringBuilder ();
588 private string GetCurrentTagMarkup ()
591 case XmlNodeType
.CDATA
:
592 return String
.Format ("<![CDATA[{0}]]>", Value
.Replace ("]]>", "]]>"));
593 case XmlNodeType
.Text
:
594 return Value
.Replace ("<", "<");
595 case XmlNodeType
.Comment
:
596 return String
.Format ("<!--{0}-->", Value
);
597 case XmlNodeType
.SignificantWhitespace
:
598 case XmlNodeType
.Whitespace
:
600 case XmlNodeType
.EndElement
:
601 return String
.Format ("</{0}>", Name
);
604 bool isEmpty
= IsEmptyElement
;
607 XPathNavigator temp
= current
.Clone ();
608 while (temp
.MoveToNextAttribute ())
609 atts
.AppendFormat (" {0}='{1}'", temp
.Name
, temp
.Value
.Replace ("'", "'"));
611 return String
.Format ("<{0}{1}>", name
, atts
);
613 return String
.Format ("<{0}{1} />", name
, atts
);
616 // Arranged copy of XmlTextReader.ReadOuterXml()
617 public override string ReadOuterXml ()
619 if (ReadState
!= ReadState
.Interactive
)
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
;
629 StringBuilder atts
= new StringBuilder ();
630 XPathNavigator temp
= current
.Clone ();
631 while (temp
.MoveToNextAttribute ())
632 atts
.AppendFormat (" {0}='{1}'", temp
.Name
, temp
.Value
.Replace ("'", "'"));
635 return String
.Format ("{0}{1}</{2}>", GetCurrentTagMarkup (), atts
, ReadInnerXml (), name
);
637 return String
.Format ("{0}", GetCurrentTagMarkup ());
638 case XmlNodeType
.None
:
639 // MS document is incorrect. Seems not to progress.
648 public override string LookupNamespace (string prefix
)
650 XPathNavigator backup
= current
.Clone ();
652 this.MoveToElement ();
653 if (current
.MoveToFirstNamespace ()) {
655 if (current
.LocalName
== prefix
)
656 return current
.Value
;
657 } while (current
.MoveToNextNamespace ());
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
)
674 if (attributeValueConsumed
)
676 attributeValueConsumed
= true;