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.
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
));
53 current
= nav
.Clone ();
57 XPathNavigator current
;
61 bool attributeValueConsumed
;
62 StringBuilder readStringBuffer
= new StringBuilder ();
63 StringBuilder innerXmlBuilder
= new StringBuilder ();
66 int attributeCount
= 0;
71 public override XmlNodeType NodeType
74 if (ReadState
!= ReadState
.Interactive
)
75 return XmlNodeType
.None
;
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
;
102 throw new InvalidOperationException (String
.Format ("Current XPathNavigator status is {0} which is not acceptable to XmlReader.", current
.NodeType
));
107 public override string Name
{
111 else if (current
.NodeType
== XPathNodeType
.Namespace
)
112 return current
.Name
== String
.Empty
? "xmlns" : "xmlns:" + current
.Name
;
118 public override string LocalName
{
122 else if (current
.NodeType
== XPathNodeType
.Namespace
&& current
.LocalName
== String
.Empty
)
125 return current
.LocalName
;
129 public override string NamespaceURI
{
133 else if (current
.NodeType
== XPathNodeType
.Namespace
)
134 return XmlNamespaceManager
.XmlnsXmlns
;
136 return current
.NamespaceURI
;
140 public override string Prefix
{
144 else if (current
.NodeType
== XPathNodeType
.Namespace
&& current
.LocalName
!= String
.Empty
)
147 return current
.Prefix
;
151 public override bool HasValue
{
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
:
167 public override int Depth
{
170 case ReadState
.EndOfFile
:
171 case ReadState
.Initial
:
172 case ReadState
.Closed
:
179 public override string Value
{
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
:
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
{
209 IXmlSchemaInfo si
= current
as IXmlSchemaInfo
;
210 return si
!= null && si
.IsDefault
;
214 // It makes no sense.
215 public override char QuoteChar
{
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 ()
235 if (current
.MoveToFirstAttribute ()) {
238 } while (current
.MoveToNextAttribute ());
239 current
.MoveToParent ();
241 if (current
.MoveToFirstNamespace (XPathNamespaceScope
.Local
)) {
244 } while (current
.MoveToNextNamespace (XPathNamespaceScope
.Local
));
245 current
.MoveToParent ();
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
:
259 if (MoveToFirstAttribute ()) {
263 for (count
++; this.MoveToNextAttribute (); count
++) {
272 public override string this [int i
] {
274 XPathNavigator backup
= current
.Clone ();
276 if (MoveToAttributeNavigator (i
))
279 throw new ArgumentOutOfRangeException ();
281 current
.MoveTo (backup
);
286 private void SplitName (string name
, out string localName
, out string ns
)
290 int colon
= name
.IndexOf (':');
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
] {
301 SplitName (name
, out localName
, out ns
);
302 return this [localName
, ns
];
306 public override string this [string localName
, string namespaceURI
] {
308 string v
= current
.GetAttribute (localName
, namespaceURI
);
309 if (v
!= String
.Empty
)
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
{
323 return ReadState
.EndOfFile
;
325 return ReadState
.Closed
;
327 return ReadState
.Initial
;
328 return ReadState
.Interactive
;
332 public override XmlNameTable NameTable
{
333 get { return current.NameTable; }
339 public override string GetAttribute (string name
)
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
)
357 private bool CheckAttributeMove (bool b
)
360 attributeValueConsumed
= false;
364 public override bool MoveToAttribute (string name
)
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;
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
));
404 return CheckAttributeMove (current
.MoveToFirstAttribute ());
407 public override bool MoveToNextAttribute ()
409 if (current
.NodeType
== XPathNodeType
.Namespace
) {
410 bool b
= CheckAttributeMove (current
.MoveToNextNamespace (XPathNamespaceScope
.Local
));
413 current
.MoveToParent ();
414 b
= CheckAttributeMove (current
.MoveToFirstAttribute ());
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 ();
431 public override void Close ()
437 public override bool Read ()
440 case ReadState
.EndOfFile
:
441 case ReadState
.Closed
:
442 case ReadState
.Error
:
444 case ReadState
.Initial
:
446 switch (current
.NodeType
) {
447 case XPathNodeType
.Root
:
448 // recurse, but as Interactive
450 case XPathNodeType
.Element
:
451 if (current
.IsEmptyElement
)
453 attributeCount
= GetAttributeCount ();
470 if (endElement
|| !current
.MoveToFirstChild ()) {
471 if (current
.IsSamePosition (root
)) { // It should happen only when the root node was empty.
475 if (!current
.MoveToNext ()) {
476 current
.MoveToParent ();
478 endElement
= (current
.NodeType
== XPathNodeType
.Element
);
479 if (current
.IsSamePosition (root
)) {
480 if (current
.NodeType
== XPathNodeType
.Element
)
491 else if (!endElement
)
493 attributeCount
= GetAttributeCount ();
497 public override string ReadString ()
499 readStringBuffer
.Length
= 0;
504 case XmlNodeType
.Element
:
510 case XmlNodeType
.Text
:
511 case XmlNodeType
.CDATA
:
512 case XmlNodeType
.Whitespace
:
513 case XmlNodeType
.SignificantWhitespace
:
514 readStringBuffer
.Append (Value
);
520 case XmlNodeType
.Text
:
521 case XmlNodeType
.CDATA
:
522 case XmlNodeType
.Whitespace
:
523 case XmlNodeType
.SignificantWhitespace
:
526 case XmlNodeType
.Text
:
527 case XmlNodeType
.CDATA
:
528 case XmlNodeType
.Whitespace
:
529 case XmlNodeType
.SignificantWhitespace
:
530 readStringBuffer
.Append (Value
);
538 string ret
= readStringBuffer
.ToString ();
539 readStringBuffer
.Length
= 0;
545 public override string ReadInnerXml ()
547 if (ReadState
!= ReadState
.Interactive
)
551 case XmlNodeType
.Attribute
:
553 case XmlNodeType
.Element
:
557 int startDepth
= Depth
;
559 innerXmlBuilder
.Length
= 0;
563 if (NodeType
==XmlNodeType
.None
)
564 throw new InvalidOperationException ("unexpected end of xml.");
565 else if (NodeType
== XmlNodeType
.EndElement
&& Depth
== startDepth
) {
570 innerXmlBuilder
.Append (GetCurrentTagMarkup ());
572 string xml
= innerXmlBuilder
.ToString ();
573 innerXmlBuilder
.Length
= 0;
575 case XmlNodeType
.None
:
576 // MS document is incorrect. Seems not to progress.
584 StringBuilder atts
= new StringBuilder ();
585 private string GetCurrentTagMarkup ()
588 case XmlNodeType
.CDATA
:
589 return String
.Format ("<![CDATA[{0}]]>", Value
.Replace ("]]>", "]]>"));
590 case XmlNodeType
.Text
:
591 return Value
.Replace ("<", "<");
592 case XmlNodeType
.Comment
:
593 return String
.Format ("<!--{0}-->", Value
);
594 case XmlNodeType
.SignificantWhitespace
:
595 case XmlNodeType
.Whitespace
:
597 case XmlNodeType
.EndElement
:
598 return String
.Format ("</{0}>", Name
);
601 bool isEmpty
= IsEmptyElement
;
604 XPathNavigator temp
= current
.Clone ();
605 while (temp
.MoveToNextAttribute ())
606 atts
.AppendFormat (" {0}='{1}'", temp
.Name
, temp
.Value
.Replace ("'", "'"));
608 return String
.Format ("<{0}{1}>", name
, atts
);
610 return String
.Format ("<{0}{1} />", name
, atts
);
613 // Arranged copy of XmlTextReader.ReadOuterXml()
614 public override string ReadOuterXml ()
616 if (ReadState
!= ReadState
.Interactive
)
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
;
626 StringBuilder atts
= new StringBuilder ();
627 XPathNavigator temp
= current
.Clone ();
628 while (temp
.MoveToNextAttribute ())
629 atts
.AppendFormat (" {0}='{1}'", temp
.Name
, temp
.Value
.Replace ("'", "'"));
632 return String
.Format ("{0}{1}</{2}>", GetCurrentTagMarkup (), atts
, ReadInnerXml (), name
);
634 return String
.Format ("{0}", GetCurrentTagMarkup ());
635 case XmlNodeType
.None
:
636 // MS document is incorrect. Seems not to progress.
645 public override string LookupNamespace (string prefix
)
647 XPathNavigator backup
= current
.Clone ();
649 this.MoveToElement ();
650 if (current
.MoveToFirstNamespace ()) {
652 if (current
.LocalName
== prefix
)
653 return current
.Value
;
654 } while (current
.MoveToNextNamespace ());
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
)
671 if (attributeValueConsumed
)
673 attributeValueConsumed
= true;