5 // Jason Diamond (jason@injektilo.org)
6 // Gonzalo Paniagua Javier (gonzalo@ximian.com)
7 // Atsushi Enomoto (ginga@kit.hi-ho.ne.jp)
9 // (C) 2001, 2002 Jason Diamond http://injektilo.org/
10 // (c) 2002 Ximian, Inc. (http://www.ximian.com)
11 // (C) 2003 Atsushi Enomoto
15 // Permission is hereby granted, free of charge, to any person obtaining
16 // a copy of this software and associated documentation files (the
17 // "Software"), to deal in the Software without restriction, including
18 // without limitation the rights to use, copy, modify, merge, publish,
19 // distribute, sublicense, and/or sell copies of the Software, and to
20 // permit persons to whom the Software is furnished to do so, subject to
21 // the following conditions:
23 // The above copyright notice and this permission notice shall be
24 // included in all copies or substantial portions of the Software.
26 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
27 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
28 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
29 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
30 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
31 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
32 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
34 using System
.Collections
;
37 using System
.Xml
.Schema
; // only required for NET_2_0 (SchemaInfo)
38 using System
.Xml
.Serialization
; // only required for NET_2_0 (SchemaInfo)
39 using Mono
.Xml
; // only required for NET_2_0
44 public abstract class XmlReader
: IDisposable
46 public abstract class XmlReader
49 private StringBuilder readStringBuffer
;
51 private XmlReaderSettings settings
;
56 protected XmlReader ()
64 public abstract int AttributeCount { get; }
66 public abstract string BaseURI { get; }
68 public virtual bool CanResolveEntity
73 public abstract int Depth { get; }
75 public abstract bool EOF { get; }
77 public virtual bool HasAttributes
79 get { return AttributeCount > 0; }
82 public abstract bool HasValue { get; }
85 public virtual bool IsDefault
{
89 public virtual bool IsEmptyElement
{
93 public virtual string this [int i
] {
94 get { return GetAttribute (i); }
97 public virtual string this [string name
] {
98 get { return GetAttribute (name); }
101 public virtual string this [string name
, string namespaceURI
] {
102 get { return GetAttribute (name, namespaceURI); }
105 public abstract bool IsDefault { get; }
107 public abstract bool IsEmptyElement { get; }
109 public abstract string this [int i
] { get; }
111 public abstract string this [string name
] { get; }
113 public abstract string this [string localName
, string namespaceName
] { get; }
116 public abstract string LocalName { get; }
118 public abstract string Name { get; }
120 public abstract string NamespaceURI { get; }
122 public abstract XmlNameTable NameTable { get; }
124 public abstract XmlNodeType NodeType { get; }
126 public abstract string Prefix { get; }
129 public virtual char QuoteChar
{
133 public abstract char QuoteChar { get; }
136 public abstract ReadState ReadState { get; }
139 public virtual IXmlSchemaInfo SchemaInfo
{
143 public virtual XmlReaderSettings Settings
{
144 get { return settings; }
148 public abstract string Value { get; }
151 public virtual string XmlLang
{
152 get { return String.Empty; }
155 public virtual XmlSpace XmlSpace
{
156 get { return XmlSpace.None; }
159 public abstract string XmlLang { get; }
161 public abstract XmlSpace XmlSpace { get; }
168 public abstract void Close ();
171 public static XmlReader
Create (Stream stream
)
173 return Create (stream
, null, null, new XmlUrlResolver (), null);
176 public static XmlReader
Create (string url
)
178 return Create (url
, null);
181 public static XmlReader
Create (TextReader reader
)
183 return Create (reader
, null, new XmlUrlResolver (), null);
186 public static XmlReader
Create (string url
, XmlReaderSettings settings
)
188 return Create (url
, null, new XmlUrlResolver (), settings
);
191 public static XmlReader
Create (XmlReader reader
, XmlReaderSettings settings
)
193 return Create (reader
, new XmlUrlResolver (), settings
);
196 [MonoTODO ("ConformanceLevel, IgnoreSchemaXXX etc.")]
197 public static XmlReader
Create (XmlReader reader
, XmlResolver resolver
, XmlReaderSettings settings
)
199 return CreateFilteredXmlReader (reader
, resolver
, settings
);
202 [MonoTODO ("ConformanceLevel, IgnoreSchemaXXX etc.; Encoding")]
203 public static XmlReader
Create (string url
, Encoding encoding
, XmlResolver resolver
, XmlReaderSettings settings
)
205 return CreateCustomizedTextReader (new XmlTextReader (url
, settings
!= null ? settings
.NameTable
: null), resolver
, settings
);
208 [MonoTODO ("ConformanceLevel, IgnoreSchemaXXX etc.")]
209 public static XmlReader
Create (TextReader reader
, string baseUri
, XmlResolver resolver
, XmlReaderSettings settings
)
211 return CreateCustomizedTextReader (new XmlTextReader (baseUri
, reader
, settings
!= null ? settings
.NameTable
: null), resolver
, settings
);
214 [MonoTODO ("ConformanceLevel, IgnoreSchemaXXX etc.")]
215 public static XmlReader
Create (Stream stream
, string baseUri
, Encoding encoding
, XmlResolver resolver
, XmlReaderSettings settings
)
217 XmlNameTable nameTable
= settings
!= null ? settings
.NameTable
: null;
218 return CreateCustomizedTextReader (
220 new XmlTextReader (baseUri
, stream
, nameTable
) :
221 new XmlTextReader (baseUri
, new StreamReader (stream
, encoding
), nameTable
),
226 private static XmlReader
CreateCustomizedTextReader (XmlTextReader reader
, XmlResolver resolver
, XmlReaderSettings settings
)
228 reader
.XmlResolver
= resolver
;
229 // Normalization is set true by default.
230 reader
.Normalization
= true;
232 if (settings
== null)
233 settings
= new XmlReaderSettings ();
235 if (settings
.ProhibitDtd
)
236 reader
.ProhibitDtd
= true;
238 if (!settings
.CheckCharacters
)
239 reader
.CharacterChecking
= false;
241 // I guess it might be changed in 2.0 RTM to set true
242 // as default, or just disappear. It goes against
243 // XmlTextReader's default usage and users will have
244 // to close input manually (that's annoying). Moreover,
245 // MS XmlTextReader consumes text input more than
246 // actually read and users can acquire those extra
247 // consumption by GetRemainder() that returns different
249 reader
.CloseInput
= settings
.CloseInput
;
251 // I would like to support it in detail later;
252 // MSDN description looks source of confusion. We don't
253 // need examples, but precise list of how it works.
254 reader
.Conformance
= settings
.ConformanceLevel
;
256 reader
.AdjustLineInfoOffset (settings
.LineNumberOffset
,
257 settings
.LinePositionOffset
);
259 // FIXME: maybe we had better create XmlParserContext.
260 if (settings
.NameTable
!= null)
261 reader
.SetNameTable (settings
.NameTable
);
263 return CreateFilteredXmlReader (reader
, resolver
, settings
);
266 private static XmlReader
CreateFilteredXmlReader (XmlReader reader
, XmlResolver resolver
, XmlReaderSettings settings
)
268 reader
= CreateValidatingXmlReader (reader
, settings
);
270 if (reader
.Settings
!= null ||
271 settings
.IgnoreComments
||
272 settings
.IgnoreProcessingInstructions
||
273 settings
.IgnoreWhitespace
)
274 return new XmlFilterReader (reader
, settings
);
276 reader
.settings
= settings
;
281 private static XmlReader
CreateValidatingXmlReader (XmlReader reader
, XmlReaderSettings settings
)
283 XmlValidatingReader xvr
= null;
284 if (settings
.DtdValidate
) {
285 xvr
= new XmlValidatingReader (reader
);
286 if (!settings
.XsdValidate
)
287 xvr
.ValidationType
= ValidationType
.DTD
;
288 // otherwise .Auto by default.
289 } else if (settings
.XsdValidate
) {
290 xvr
= new XmlValidatingReader (reader
);
291 xvr
.ValidationType
= ValidationType
.Schema
;
294 xvr
.SetSchemas (settings
.Schemas
);
296 if (settings
.IgnoreIdentityConstraints
)
297 throw new NotImplementedException ();
298 if (!settings
.IgnoreInlineSchema
)
299 throw new NotImplementedException ();
300 if (!settings
.IgnoreSchemaLocation
)
301 throw new NotImplementedException ();
302 if (!settings
.IgnoreValidationWarnings
)
303 throw new NotImplementedException ();
305 return xvr
!= null ? xvr
: reader
;
310 public virtual void Dispose ()
312 if (ReadState
!= ReadState
.Closed
)
317 public abstract string GetAttribute (int i
);
319 public abstract string GetAttribute (string name
);
321 public abstract string GetAttribute (
323 string namespaceName
);
325 public static bool IsName (string s
)
327 return s
!= null && XmlChar
.IsName (s
);
330 public static bool IsNameToken (string s
)
332 return s
!= null && XmlChar
.IsNmToken (s
);
335 public virtual bool IsStartElement ()
337 return (MoveToContent () == XmlNodeType
.Element
);
340 public virtual bool IsStartElement (string name
)
342 if (!IsStartElement ())
345 return (Name
== name
);
348 public virtual bool IsStartElement (string localName
, string namespaceName
)
350 if (!IsStartElement ())
353 return (LocalName
== localName
&& NamespaceURI
== namespaceName
);
356 public abstract string LookupNamespace (string prefix
);
359 public virtual string LookupNamespace (string prefix
, bool atomizedNames
)
361 internal virtual string LookupNamespace (string prefix
, bool atomizedNames
)
364 return LookupNamespace (prefix
);
367 public abstract void MoveToAttribute (int i
);
369 public abstract bool MoveToAttribute (string name
);
371 public abstract bool MoveToAttribute (
373 string namespaceName
);
375 private bool IsContent (XmlNodeType nodeType
)
378 * (non-white space text, CDATA, Element, EndElement, EntityReference, or EndEntity)
381 case XmlNodeType
.Text
:
383 case XmlNodeType
.CDATA
:
385 case XmlNodeType
.Element
:
387 case XmlNodeType
.EndElement
:
389 case XmlNodeType
.EntityReference
:
391 case XmlNodeType
.EndEntity
:
398 public virtual XmlNodeType
MoveToContent ()
400 if (NodeType
== XmlNodeType
.Attribute
)
404 if (IsContent (NodeType
))
408 return XmlNodeType
.None
;
411 public abstract bool MoveToElement ();
413 public abstract bool MoveToFirstAttribute ();
415 public abstract bool MoveToNextAttribute ();
417 public abstract bool Read ();
419 public abstract bool ReadAttributeValue ();
421 public virtual string ReadElementString ()
423 if (MoveToContent () != XmlNodeType
.Element
) {
424 string error
= String
.Format ("'{0}' is an invalid node type.",
425 NodeType
.ToString ());
426 throw XmlError (error
);
429 string result
= String
.Empty
;
430 if (!IsEmptyElement
) {
432 result
= ReadString ();
433 if (NodeType
!= XmlNodeType
.EndElement
) {
434 string error
= String
.Format ("'{0}' is an invalid node type.",
435 NodeType
.ToString ());
436 throw XmlError (error
);
444 public virtual string ReadElementString (string name
)
446 if (MoveToContent () != XmlNodeType
.Element
) {
447 string error
= String
.Format ("'{0}' is an invalid node type.",
448 NodeType
.ToString ());
449 throw XmlError (error
);
453 string error
= String
.Format ("The {0} tag from namespace {1} is expected.",
455 throw XmlError (error
);
458 string result
= String
.Empty
;
459 if (!IsEmptyElement
) {
461 result
= ReadString ();
462 if (NodeType
!= XmlNodeType
.EndElement
) {
463 string error
= String
.Format ("'{0}' is an invalid node type.",
464 NodeType
.ToString ());
465 throw XmlError (error
);
473 public virtual string ReadElementString (string localName
, string namespaceName
)
475 if (MoveToContent () != XmlNodeType
.Element
) {
476 string error
= String
.Format ("'{0}' is an invalid node type.",
477 NodeType
.ToString ());
478 throw XmlError (error
);
481 if (localName
!= LocalName
|| NamespaceURI
!= namespaceName
) {
482 string error
= String
.Format ("The {0} tag from namespace {1} is expected.",
483 LocalName
, NamespaceURI
);
484 throw XmlError (error
);
487 string result
= String
.Empty
;
488 if (!IsEmptyElement
) {
490 result
= ReadString ();
491 if (NodeType
!= XmlNodeType
.EndElement
) {
492 string error
= String
.Format ("'{0}' is an invalid node type.",
493 NodeType
.ToString ());
494 throw XmlError (error
);
502 public virtual void ReadEndElement ()
504 if (MoveToContent () != XmlNodeType
.EndElement
) {
505 string error
= String
.Format ("'{0}' is an invalid node type.",
506 NodeType
.ToString ());
507 throw XmlError (error
);
514 public abstract string ReadInnerXml ();
516 public abstract string ReadOuterXml ();
519 public virtual string ReadInnerXml ()
521 return ReadInnerXmlInternal ();
524 public virtual string ReadOuterXml ()
526 return ReadOuterXmlInternal ();
530 internal string ReadInnerXmlInternal ()
532 if (ReadState
!= ReadState
.Interactive
|| NodeType
== XmlNodeType
.EndElement
)
535 StringWriter sw
= new StringWriter ();
536 XmlTextWriter xtw
= new XmlTextWriter (sw
);
537 if (NodeType
== XmlNodeType
.Element
) {
538 if (IsEmptyElement
) {
542 int startDepth
= Depth
;
544 while (startDepth
< Depth
) {
545 if (ReadState
!= ReadState
.Interactive
)
546 throw XmlError ("Unexpected end of the XML reader.");
547 xtw
.WriteNode (this, false);
549 // reader is now end element, then proceed once more.
553 xtw
.WriteNode (this, false);
555 return sw
.ToString ();
558 internal string ReadOuterXmlInternal ()
560 if (ReadState
!= ReadState
.Interactive
|| NodeType
== XmlNodeType
.EndElement
)
563 StringWriter sw
= new StringWriter ();
564 XmlTextWriter xtw
= new XmlTextWriter (sw
);
565 xtw
.WriteNode (this, false);
566 return sw
.ToString ();
569 public virtual void ReadStartElement ()
571 if (MoveToContent () != XmlNodeType
.Element
) {
572 string error
= String
.Format ("'{0}' is an invalid node type.",
573 NodeType
.ToString ());
574 throw XmlError (error
);
580 public virtual void ReadStartElement (string name
)
582 if (MoveToContent () != XmlNodeType
.Element
) {
583 string error
= String
.Format ("'{0}' is an invalid node type.",
584 NodeType
.ToString ());
585 throw XmlError (error
);
589 string error
= String
.Format ("The {0} tag from namespace {1} is expected.",
591 throw XmlError (error
);
597 public virtual void ReadStartElement (string localName
, string namespaceName
)
599 if (MoveToContent () != XmlNodeType
.Element
) {
600 string error
= String
.Format ("'{0}' is an invalid node type.",
601 NodeType
.ToString ());
602 throw XmlError (error
);
605 if (localName
!= LocalName
|| NamespaceURI
!= namespaceName
) {
606 string error
= String
.Format ("Expecting {0} tag from namespace {1}, got {2} and {3} instead",
607 localName
, namespaceName
,
608 LocalName
, NamespaceURI
);
609 throw XmlError (error
);
616 public abstract string ReadString ();
618 public virtual string ReadString ()
620 return ReadStringInternal ();
624 internal string ReadStringInternal ()
626 if (readStringBuffer
== null)
627 readStringBuffer
= new StringBuilder ();
628 readStringBuffer
.Length
= 0;
635 case XmlNodeType
.Element
:
641 case XmlNodeType
.Text
:
642 case XmlNodeType
.CDATA
:
643 case XmlNodeType
.Whitespace
:
644 case XmlNodeType
.SignificantWhitespace
:
645 readStringBuffer
.Append (Value
);
651 case XmlNodeType
.Text
:
652 case XmlNodeType
.CDATA
:
653 case XmlNodeType
.Whitespace
:
654 case XmlNodeType
.SignificantWhitespace
:
657 case XmlNodeType
.Text
:
658 case XmlNodeType
.CDATA
:
659 case XmlNodeType
.Whitespace
:
660 case XmlNodeType
.SignificantWhitespace
:
661 readStringBuffer
.Append (Value
);
669 string ret
= readStringBuffer
.ToString ();
670 readStringBuffer
.Length
= 0;
675 public virtual Type ValueType
{
676 get { return typeof (string); }
680 public virtual bool ReadToDescendant (string name
)
682 if (NodeType
!= XmlNodeType
.Element
|| IsEmptyElement
)
685 for (Read (); depth
< Depth
; Read ())
686 if (NodeType
== XmlNodeType
.Element
&& name
== Name
)
692 public virtual bool ReadToDescendant (string localName
, string namespaceURI
)
694 if (NodeType
!= XmlNodeType
.Element
|| IsEmptyElement
)
697 for (Read (); depth
< Depth
; Read ())
698 if (NodeType
== XmlNodeType
.Element
&& localName
== LocalName
&& namespaceURI
== NamespaceURI
)
704 public virtual bool ReadToNextSibling (string name
)
706 if (NodeType
!= XmlNodeType
.Element
|| IsEmptyElement
)
709 for (Skip (); depth
< Depth
; Skip ())
710 if (NodeType
== XmlNodeType
.Element
&& name
== Name
)
716 public virtual bool ReadToNextSibling (string localName
, string namespaceURI
)
718 if (NodeType
!= XmlNodeType
.Element
|| IsEmptyElement
)
721 for (Skip (); depth
< Depth
; Skip ())
722 if (NodeType
== XmlNodeType
.Element
&& localName
== LocalName
&& namespaceURI
== NamespaceURI
)
728 public virtual XmlReader
ReadSubtree ()
730 return new SubtreeXmlReader (this);
734 public virtual object ReadTypedValue ()
736 return ReadValueAs (ValueType
);
740 public virtual object ReadValueAs (Type type
)
742 return ReadValueAs (type
, this as IXmlNamespaceResolver
);
746 public virtual object ReadValueAs (Type type
, IXmlNamespaceResolver resolver
)
748 string text
= ReadString ();
750 if (type
== typeof (XmlQualifiedName
))
751 return XmlQualifiedName
.Parse (text
, resolver
);
753 switch (Type
.GetTypeCode (type
)) {
754 case TypeCode
.Boolean
:
755 return ReadValueAsBoolean ();
756 case TypeCode
.DateTime
:
757 return ReadValueAsDateTime ();
758 case TypeCode
.Decimal
:
759 return ReadValueAsDecimal ();
760 case TypeCode
.Double
:
761 return ReadValueAsDouble ();
763 return ReadValueAsInt32 ();
765 return ReadValueAsInt64 ();
766 case TypeCode
.Single
:
767 return ReadValueAsSingle ();
768 case TypeCode
.String
:
769 return ReadValueAsString ();
771 } catch (Exception ex
) {
772 return new FormatException (String
.Format ("Current text value '{0}' is not acceptable for specified type '{1}'.", text
, type
));
774 throw new ArgumentException (String
.Format ("Specified type '{0}' is not supported.", type
));
778 public virtual bool ReadValueAsBoolean ()
780 return XQueryConvert
.StringToBoolean (ReadString ());
784 public virtual DateTime
ReadValueAsDateTime ()
786 return XQueryConvert
.StringToDateTime (ReadString ());
790 public virtual decimal ReadValueAsDecimal ()
792 return XQueryConvert
.StringToDecimal (ReadString ());
796 public virtual double ReadValueAsDouble ()
798 return XQueryConvert
.StringToDouble (ReadString ());
802 public virtual int ReadValueAsInt32 ()
804 return XQueryConvert
.StringToInt (ReadString ());
808 public virtual long ReadValueAsInt64 ()
810 return XQueryConvert
.StringToInteger (ReadString ());
814 public virtual ICollection
ReadValueAsList ()
816 throw new NotImplementedException ();
820 public virtual float ReadValueAsSingle ()
822 return XQueryConvert
.StringToFloat (ReadString ());
826 public virtual string ReadValueAsString ()
828 return ReadString ();
832 public abstract void ResolveEntity ();
834 public virtual void Skip ()
836 if (ReadState
!= ReadState
.Interactive
)
840 if (NodeType
!= XmlNodeType
.Element
|| IsEmptyElement
) {
846 while (Read() && depth
< Depth
);
847 if (NodeType
== XmlNodeType
.EndElement
)
851 private XmlException
XmlError (string message
)
853 return new XmlException (this as IXmlLineInfo
, BaseURI
, message
);