Fix bug #2956: XmlReader.HasValue became virtual (from abstract) in .NET 4.
[mono-project.git] / mcs / class / System.XML / System.Xml / XmlReader.cs
blobf5434ec7cf396e1989a8a4124e24459f2e63a7f4
1 //
2 // XmlReader.cs
3 //
4 // Authors:
5 // Jason Diamond (jason@injektilo.org)
6 // Gonzalo Paniagua Javier (gonzalo@ximian.com)
7 // Atsushi Enomoto (ginga@kit.hi-ho.ne.jp)
8 //
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:
22 //
23 // The above copyright notice and this permission notice shall be
24 // included in all copies or substantial portions of the Software.
25 //
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;
35 using System.Diagnostics;
36 using System.IO;
37 using System.Text;
38 #if !MOONLIGHT
39 using System.Xml.Schema; // only required for NET_2_0 (SchemaInfo)
40 using System.Xml.Serialization; // only required for NET_2_0 (SchemaInfo)
41 using Mono.Xml.Schema; // only required for NET_2_0
42 #endif
43 using Mono.Xml; // only required for NET_2_0
45 namespace System.Xml
47 #if NET_2_0
48 public abstract class XmlReader : IDisposable
49 #else
50 public abstract class XmlReader
51 #endif
53 private StringBuilder readStringBuffer;
54 private XmlReaderBinarySupport binary;
55 #if NET_2_0
56 private XmlReaderSettings settings;
57 #endif
59 #region Constructor
61 protected XmlReader ()
65 #endregion
67 #region Properties
69 public abstract int AttributeCount { get; }
71 public abstract string BaseURI { get; }
73 internal XmlReaderBinarySupport Binary {
74 get { return binary; }
77 internal XmlReaderBinarySupport.CharGetter BinaryCharGetter {
78 get { return binary != null ? binary.Getter : null; }
79 set {
80 if (binary == null)
81 binary = new XmlReaderBinarySupport (this);
82 binary.Getter = value;
86 #if NET_2_0
87 // To enable it internally in sys.xml, just insert these
88 // two lines into Read():
90 // #if NET_2_0
91 // if (Binary != null)
92 // Binary.Reset ();
93 // #endif
95 public virtual bool CanReadBinaryContent {
96 get { return false; }
99 public virtual bool CanReadValueChunk {
100 get { return false; }
102 #else
103 internal virtual bool CanReadBinaryContent {
104 get { return false; }
107 internal virtual bool CanReadValueChunk {
108 get { return false; }
110 #endif
112 public virtual bool CanResolveEntity
114 get { return false; }
117 public abstract int Depth { get; }
119 public abstract bool EOF { get; }
121 public virtual bool HasAttributes
123 get { return AttributeCount > 0; }
126 #if NET_4_0
127 public virtual bool HasValue {
128 get {
129 switch (NodeType) {
130 case XmlNodeType.Attribute:
131 case XmlNodeType.Comment:
132 case XmlNodeType.ProcessingInstruction:
133 case XmlNodeType.SignificantWhitespace:
134 case XmlNodeType.CDATA:
135 case XmlNodeType.Text:
136 case XmlNodeType.Whitespace:
137 case XmlNodeType.XmlDeclaration:
138 return true;
140 return false;
143 #else
144 public abstract bool HasValue { get; }
145 #endif
147 public abstract bool IsEmptyElement { get; }
149 #if NET_2_0
150 public virtual bool IsDefault {
151 get { return false; }
154 public virtual string this [int i] {
155 get { return GetAttribute (i); }
158 public virtual string this [string name] {
159 get { return GetAttribute (name); }
162 public virtual string this [string name, string namespaceURI] {
163 get { return GetAttribute (name, namespaceURI); }
165 #else
166 public abstract bool IsDefault { get; }
168 public abstract string this [int i] { get; }
170 public abstract string this [string name] { get; }
172 public abstract string this [string localName, string namespaceName] { get; }
173 #endif
175 public abstract string LocalName { get; }
177 #if NET_2_0
178 public virtual string Name {
179 get {
180 return Prefix.Length > 0 ?
181 String.Concat (Prefix, ":", LocalName) :
182 LocalName;
185 #else
186 public abstract string Name { get; }
187 #endif
189 public abstract string NamespaceURI { get; }
191 public abstract XmlNameTable NameTable { get; }
193 public abstract XmlNodeType NodeType { get; }
195 public abstract string Prefix { get; }
197 #if NET_2_0
198 public virtual char QuoteChar {
199 get { return '\"'; }
201 #else
202 public abstract char QuoteChar { get; }
203 #endif
205 public abstract ReadState ReadState { get; }
207 #if NET_2_0
208 #if !MOONLIGHT
209 public virtual IXmlSchemaInfo SchemaInfo {
210 get { return null; }
212 #endif
214 public virtual XmlReaderSettings Settings {
215 get { return settings; }
217 #endif
219 public abstract string Value { get; }
221 #if NET_2_0
222 public virtual string XmlLang {
223 get { return String.Empty; }
226 public virtual XmlSpace XmlSpace {
227 get { return XmlSpace.None; }
229 #else
230 public abstract string XmlLang { get; }
232 public abstract XmlSpace XmlSpace { get; }
233 #endif
235 #endregion
237 #region Methods
239 public abstract void Close ();
241 #if NET_2_0
242 private static XmlNameTable PopulateNameTable (
243 XmlReaderSettings settings)
245 XmlNameTable nameTable = settings.NameTable;
246 if (nameTable == null)
247 nameTable = new NameTable ();
248 return nameTable;
251 private static XmlParserContext PopulateParserContext (
252 XmlReaderSettings settings, string baseUri)
254 XmlNameTable nt = PopulateNameTable (settings);
255 return new XmlParserContext (nt,
256 new XmlNamespaceManager (nt),
257 null,
258 null,
259 null,
260 null,
261 baseUri,
262 null,
263 XmlSpace.None,
264 null);
267 private static XmlNodeType GetNodeType (
268 XmlReaderSettings settings)
270 ConformanceLevel level = settings != null ? settings.ConformanceLevel : ConformanceLevel.Auto;
271 return
272 level == ConformanceLevel.Fragment ?
273 XmlNodeType.Element :
274 XmlNodeType.Document;
277 public static XmlReader Create (Stream stream)
279 return Create (stream, null);
282 public static XmlReader Create (string url)
284 return Create (url, null);
287 public static XmlReader Create (TextReader reader)
289 return Create (reader, null);
292 public static XmlReader Create (string url, XmlReaderSettings settings)
294 return Create (url, settings, null);
297 public static XmlReader Create (Stream stream, XmlReaderSettings settings)
299 return Create (stream, settings, String.Empty);
302 public static XmlReader Create (TextReader reader, XmlReaderSettings settings)
304 return Create (reader, settings, String.Empty);
307 static XmlReaderSettings PopulateSettings (XmlReaderSettings src)
309 if (src == null)
310 return new XmlReaderSettings ();
311 else
312 return src.Clone ();
315 public static XmlReader Create (Stream stream, XmlReaderSettings settings, string baseUri)
317 settings = PopulateSettings (settings);
318 return Create (stream, settings,
319 PopulateParserContext (settings, baseUri));
322 public static XmlReader Create (TextReader reader, XmlReaderSettings settings, string baseUri)
324 settings = PopulateSettings (settings);
325 return Create (reader, settings,
326 PopulateParserContext (settings, baseUri));
329 public static XmlReader Create (XmlReader reader, XmlReaderSettings settings)
331 settings = PopulateSettings (settings);
332 XmlReader r = CreateFilteredXmlReader (reader, settings);
333 r.settings = settings;
334 return r;
337 public static XmlReader Create (string url, XmlReaderSettings settings, XmlParserContext context)
339 settings = PopulateSettings (settings);
340 bool closeInputBak = settings.CloseInput;
341 try {
342 settings.CloseInput = true; // forced. See XmlReaderCommonTests.CreateFromUrlClose().
343 if (context == null)
344 context = PopulateParserContext (settings, url);
345 XmlTextReader xtr = new XmlTextReader (false, settings.XmlResolver, url, GetNodeType (settings), context);
346 XmlReader ret = CreateCustomizedTextReader (xtr, settings);
347 return ret;
348 } finally {
349 settings.CloseInput = closeInputBak;
353 public static XmlReader Create (Stream stream, XmlReaderSettings settings, XmlParserContext context)
355 settings = PopulateSettings (settings);
356 if (context == null)
357 context = PopulateParserContext (settings, String.Empty);
358 return CreateCustomizedTextReader (new XmlTextReader (stream, GetNodeType (settings), context), settings);
361 public static XmlReader Create (TextReader reader, XmlReaderSettings settings, XmlParserContext context)
363 settings = PopulateSettings (settings);
364 if (context == null)
365 context = PopulateParserContext (settings, String.Empty);
366 return CreateCustomizedTextReader (new XmlTextReader (context.BaseURI, reader, GetNodeType (settings), context), settings);
369 private static XmlReader CreateCustomizedTextReader (XmlTextReader reader, XmlReaderSettings settings)
371 reader.XmlResolver = settings.XmlResolver;
372 // Normalization is set true by default.
373 reader.Normalization = true;
374 reader.EntityHandling = EntityHandling.ExpandEntities;
376 if (settings.ProhibitDtd)
377 reader.ProhibitDtd = true;
379 if (!settings.CheckCharacters)
380 reader.CharacterChecking = false;
382 // I guess it might be changed in 2.0 RTM to set true
383 // as default, or just disappear. It goes against
384 // XmlTextReader's default usage and users will have
385 // to close input manually (that's annoying). Moreover,
386 // MS XmlTextReader consumes text input more than
387 // actually read and users can acquire those extra
388 // consumption by GetRemainder() that returns different
389 // TextReader.
390 reader.CloseInput = settings.CloseInput;
392 // I would like to support it in detail later;
393 // MSDN description looks source of confusion. We don't
394 // need examples, but precise list of how it works.
395 reader.Conformance = settings.ConformanceLevel;
397 reader.AdjustLineInfoOffset (settings.LineNumberOffset,
398 settings.LinePositionOffset);
400 if (settings.NameTable != null)
401 reader.SetNameTable (settings.NameTable);
403 XmlReader r = CreateFilteredXmlReader (reader, settings);
404 r.settings = settings;
405 return r;
408 private static XmlReader CreateFilteredXmlReader (XmlReader reader, XmlReaderSettings settings)
410 ConformanceLevel conf = ConformanceLevel.Auto;
411 if (reader is XmlTextReader)
412 conf = ((XmlTextReader) reader).Conformance;
413 else if (reader.Settings != null)
414 conf = reader.Settings.ConformanceLevel;
415 else
416 conf = settings.ConformanceLevel;
417 if (settings.ConformanceLevel != ConformanceLevel.Auto &&
418 conf != settings.ConformanceLevel)
419 throw new InvalidOperationException (String.Format ("ConformanceLevel cannot be overwritten by a wrapping XmlReader. The source reader has {0}, while {1} is specified.", conf, settings.ConformanceLevel));
420 settings.ConformanceLevel = conf;
422 reader = CreateValidatingXmlReader (reader, settings);
424 if ( settings.IgnoreComments ||
425 settings.IgnoreProcessingInstructions ||
426 settings.IgnoreWhitespace)
427 return new XmlFilterReader (reader, settings);
428 else {
429 reader.settings = settings;
430 return reader;
434 private static XmlReader CreateValidatingXmlReader (XmlReader reader, XmlReaderSettings settings)
436 #if MOONLIGHT
437 return reader;
438 #else
439 XmlValidatingReader xvr = null;
440 switch (settings.ValidationType) {
441 // Auto and XDR are obsoleted in 2.0 and therefore ignored.
442 default:
443 return reader;
444 case ValidationType.DTD:
445 xvr = new XmlValidatingReader (reader);
446 xvr.XmlResolver = settings.XmlResolver;
447 xvr.ValidationType = ValidationType.DTD;
448 break;
449 case ValidationType.Schema:
450 return new XmlSchemaValidatingReader (reader, settings);
453 // Actually I don't think they are treated in DTD validation though...
454 if ((settings.ValidationFlags & XmlSchemaValidationFlags.ProcessIdentityConstraints) == 0)
455 throw new NotImplementedException ();
456 //if ((settings.ValidationFlags & XmlSchemaValidationFlags.ProcessInlineSchema) != 0)
457 // throw new NotImplementedException ();
458 //if ((settings.ValidationFlags & XmlSchemaValidationFlags.ProcessSchemaLocation) != 0)
459 // throw new NotImplementedException ();
460 //if ((settings.ValidationFlags & XmlSchemaValidationFlags.ReportValidationWarnings) == 0)
461 // throw new NotImplementedException ();
463 return xvr != null ? xvr : reader;
464 #endif
467 void IDisposable.Dispose ()
469 Dispose (false);
472 protected virtual void Dispose (bool disposing)
474 if (ReadState != ReadState.Closed)
475 Close ();
477 #endif
479 public abstract string GetAttribute (int i);
481 public abstract string GetAttribute (string name);
483 public abstract string GetAttribute (
484 string localName,
485 string namespaceName);
487 public static bool IsName (string s)
489 return s != null && XmlChar.IsName (s);
492 public static bool IsNameToken (string s)
494 return s != null && XmlChar.IsNmToken (s);
497 public virtual bool IsStartElement ()
499 return (MoveToContent () == XmlNodeType.Element);
502 public virtual bool IsStartElement (string name)
504 if (!IsStartElement ())
505 return false;
507 return (Name == name);
510 public virtual bool IsStartElement (string localName, string namespaceName)
512 if (!IsStartElement ())
513 return false;
515 return (LocalName == localName && NamespaceURI == namespaceName);
518 public abstract string LookupNamespace (string prefix);
520 #if NET_2_0
521 public virtual void MoveToAttribute (int i)
523 if (i >= AttributeCount)
524 throw new ArgumentOutOfRangeException ();
525 MoveToFirstAttribute ();
526 for (int a = 0; a < i; a++)
527 MoveToNextAttribute ();
529 #else
530 public abstract void MoveToAttribute (int i);
531 #endif
533 public abstract bool MoveToAttribute (string name);
535 public abstract bool MoveToAttribute (
536 string localName,
537 string namespaceName);
539 private bool IsContent (XmlNodeType nodeType)
541 /* MS doc says:
542 * (non-white space text, CDATA, Element, EndElement, EntityReference, or EndEntity)
544 switch (nodeType) {
545 case XmlNodeType.Text:
546 return true;
547 case XmlNodeType.CDATA:
548 return true;
549 case XmlNodeType.Element:
550 return true;
551 case XmlNodeType.EndElement:
552 return true;
553 case XmlNodeType.EntityReference:
554 return true;
555 case XmlNodeType.EndEntity:
556 return true;
559 return false;
562 public virtual XmlNodeType MoveToContent ()
564 switch (ReadState) {
565 case ReadState.Initial:
566 case ReadState.Interactive:
567 break;
568 default:
569 return NodeType;
572 if (NodeType == XmlNodeType.Attribute)
573 MoveToElement ();
575 do {
576 if (IsContent (NodeType))
577 return NodeType;
578 Read ();
579 } while (!EOF);
580 return XmlNodeType.None;
583 public abstract bool MoveToElement ();
585 public abstract bool MoveToFirstAttribute ();
587 public abstract bool MoveToNextAttribute ();
589 public abstract bool Read ();
591 public abstract bool ReadAttributeValue ();
593 public virtual string ReadElementString ()
595 if (MoveToContent () != XmlNodeType.Element) {
596 string error = String.Format ("'{0}' is an invalid node type.",
597 NodeType.ToString ());
598 throw XmlError (error);
601 string result = String.Empty;
602 if (!IsEmptyElement) {
603 Read ();
604 result = ReadString ();
605 if (NodeType != XmlNodeType.EndElement) {
606 string error = String.Format ("'{0}' is an invalid node type.",
607 NodeType.ToString ());
608 throw XmlError (error);
612 Read ();
613 return result;
616 public virtual string ReadElementString (string name)
618 if (MoveToContent () != XmlNodeType.Element) {
619 string error = String.Format ("'{0}' is an invalid node type.",
620 NodeType.ToString ());
621 throw XmlError (error);
624 if (name != Name) {
625 string error = String.Format ("The {0} tag from namespace {1} is expected.",
626 Name, NamespaceURI);
627 throw XmlError (error);
630 string result = String.Empty;
631 if (!IsEmptyElement) {
632 Read ();
633 result = ReadString ();
634 if (NodeType != XmlNodeType.EndElement) {
635 string error = String.Format ("'{0}' is an invalid node type.",
636 NodeType.ToString ());
637 throw XmlError (error);
641 Read ();
642 return result;
645 public virtual string ReadElementString (string localName, string namespaceName)
647 if (MoveToContent () != XmlNodeType.Element) {
648 string error = String.Format ("'{0}' is an invalid node type.",
649 NodeType.ToString ());
650 throw XmlError (error);
653 if (localName != LocalName || NamespaceURI != namespaceName) {
654 string error = String.Format ("The {0} tag from namespace {1} is expected.",
655 LocalName, NamespaceURI);
656 throw XmlError (error);
659 string result = String.Empty;
660 if (!IsEmptyElement) {
661 Read ();
662 result = ReadString ();
663 if (NodeType != XmlNodeType.EndElement) {
664 string error = String.Format ("'{0}' is an invalid node type.",
665 NodeType.ToString ());
666 throw XmlError (error);
670 Read ();
671 return result;
674 public virtual void ReadEndElement ()
676 if (MoveToContent () != XmlNodeType.EndElement) {
677 string error = String.Format ("'{0}' is an invalid node type.",
678 NodeType.ToString ());
679 throw XmlError (error);
682 Read ();
685 public virtual string ReadInnerXml ()
687 if (ReadState != ReadState.Interactive || NodeType == XmlNodeType.EndElement)
688 return String.Empty;
690 if (IsEmptyElement) {
691 Read ();
692 return String.Empty;
694 StringWriter sw = new StringWriter ();
695 XmlTextWriter xtw = new XmlTextWriter (sw);
696 if (NodeType == XmlNodeType.Element) {
697 int startDepth = Depth;
698 Read ();
699 while (startDepth < Depth) {
700 if (ReadState != ReadState.Interactive)
701 throw XmlError ("Unexpected end of the XML reader.");
702 xtw.WriteNode (this, false);
704 // reader is now end element, then proceed once more.
705 Read ();
707 else
708 xtw.WriteNode (this, false);
710 return sw.ToString ();
713 public virtual string ReadOuterXml ()
715 if (ReadState != ReadState.Interactive || NodeType == XmlNodeType.EndElement)
716 return String.Empty;
718 switch (NodeType) {
719 case XmlNodeType.Element:
720 case XmlNodeType.Attribute:
721 StringWriter sw = new StringWriter ();
722 XmlTextWriter xtw = new XmlTextWriter (sw);
723 xtw.WriteNode (this, false);
724 return sw.ToString ();
725 default:
726 Skip ();
727 return String.Empty;
731 public virtual void ReadStartElement ()
733 if (MoveToContent () != XmlNodeType.Element) {
734 string error = String.Format ("'{0}' is an invalid node type.",
735 NodeType.ToString ());
736 throw XmlError (error);
739 Read ();
742 public virtual void ReadStartElement (string name)
744 if (MoveToContent () != XmlNodeType.Element) {
745 string error = String.Format ("'{0}' is an invalid node type.",
746 NodeType.ToString ());
747 throw XmlError (error);
750 if (name != Name) {
751 string error = String.Format ("The {0} tag from namespace {1} is expected.",
752 Name, NamespaceURI);
753 throw XmlError (error);
756 Read ();
759 public virtual void ReadStartElement (string localName, string namespaceName)
761 if (MoveToContent () != XmlNodeType.Element) {
762 string error = String.Format ("'{0}' is an invalid node type.",
763 NodeType.ToString ());
764 throw XmlError (error);
767 if (localName != LocalName || NamespaceURI != namespaceName) {
768 string error = String.Format ("Expecting {0} tag from namespace {1}, got {2} and {3} instead",
769 localName, namespaceName,
770 LocalName, NamespaceURI);
771 throw XmlError (error);
774 Read ();
777 public virtual string ReadString ()
779 if (readStringBuffer == null)
780 readStringBuffer = new StringBuilder ();
781 readStringBuffer.Length = 0;
783 MoveToElement ();
785 switch (NodeType) {
786 default:
787 return String.Empty;
788 case XmlNodeType.Element:
789 if (IsEmptyElement)
790 return String.Empty;
791 do {
792 Read ();
793 switch (NodeType) {
794 case XmlNodeType.Text:
795 case XmlNodeType.CDATA:
796 case XmlNodeType.Whitespace:
797 case XmlNodeType.SignificantWhitespace:
798 readStringBuffer.Append (Value);
799 continue;
801 break;
802 } while (true);
803 break;
804 case XmlNodeType.Text:
805 case XmlNodeType.CDATA:
806 case XmlNodeType.Whitespace:
807 case XmlNodeType.SignificantWhitespace:
808 do {
809 switch (NodeType) {
810 case XmlNodeType.Text:
811 case XmlNodeType.CDATA:
812 case XmlNodeType.Whitespace:
813 case XmlNodeType.SignificantWhitespace:
814 readStringBuffer.Append (Value);
815 Read ();
816 continue;
818 break;
819 } while (true);
820 break;
822 string ret = readStringBuffer.ToString ();
823 readStringBuffer.Length = 0;
824 return ret;
827 #if NET_2_0
828 public virtual Type ValueType {
829 get { return typeof (string); }
832 public virtual bool ReadToDescendant (string name)
834 if (ReadState == ReadState.Initial) {
835 MoveToContent ();
836 if (IsStartElement (name))
837 return true;
839 if (NodeType != XmlNodeType.Element || IsEmptyElement)
840 return false;
841 int depth = Depth;
842 for (Read (); depth < Depth; Read ())
843 if (NodeType == XmlNodeType.Element && name == Name)
844 return true;
845 return false;
848 public virtual bool ReadToDescendant (string localName, string namespaceURI)
850 if (ReadState == ReadState.Initial) {
851 MoveToContent ();
852 if (IsStartElement (localName, namespaceURI))
853 return true;
855 if (NodeType != XmlNodeType.Element || IsEmptyElement)
856 return false;
857 int depth = Depth;
858 for (Read (); depth < Depth; Read ())
859 if (NodeType == XmlNodeType.Element && localName == LocalName && namespaceURI == NamespaceURI)
860 return true;
861 return false;
864 public virtual bool ReadToFollowing (string name)
866 while (Read ())
867 if (NodeType == XmlNodeType.Element && name == Name)
868 return true;
869 return false;
872 public virtual bool ReadToFollowing (string localName, string namespaceURI)
874 while (Read ())
875 if (NodeType == XmlNodeType.Element && localName == LocalName && namespaceURI == NamespaceURI)
876 return true;
877 return false;
880 public virtual bool ReadToNextSibling (string name)
882 if (ReadState != ReadState.Interactive)
883 return false;
884 MoveToElement ();
885 int depth = Depth;
886 Skip ();
887 for (; !EOF && depth <= Depth; Skip ())
888 if (NodeType == XmlNodeType.Element && name == Name)
889 return true;
890 return false;
893 public virtual bool ReadToNextSibling (string localName, string namespaceURI)
895 if (ReadState != ReadState.Interactive)
896 return false;
897 int depth = Depth;
898 Skip ();
899 for (; !EOF && depth <= Depth; Skip ())
900 if (NodeType == XmlNodeType.Element && localName == LocalName && namespaceURI == NamespaceURI)
901 return true;
902 return false;
905 public virtual XmlReader ReadSubtree ()
907 if (NodeType != XmlNodeType.Element)
908 throw new InvalidOperationException (String.Format ("ReadSubtree() can be invoked only when the reader is positioned on an element. Current node is {0}. {1}", NodeType, GetLocation ()));
909 return new SubtreeXmlReader (this);
912 private string ReadContentString ()
914 // The latter condition indicates that this XmlReader is on an attribute value
915 // (HasAttributes is to indicate it is on attribute value).
916 if (NodeType == XmlNodeType.Attribute || NodeType != XmlNodeType.Element && HasAttributes)
917 return Value;
918 return ReadContentString (true);
921 private string ReadContentString (bool isText)
923 if (isText) {
924 switch (NodeType) {
925 case XmlNodeType.Text:
926 case XmlNodeType.SignificantWhitespace:
927 case XmlNodeType.Whitespace:
928 case XmlNodeType.CDATA:
929 break;
930 case XmlNodeType.Element:
931 throw new InvalidOperationException (String.Format ("Node type {0} is not supported in this operation.{1}", NodeType, GetLocation ()));
932 default:
933 return String.Empty;
937 string value = String.Empty;
938 do {
939 switch (NodeType) {
940 case XmlNodeType.Element:
941 if (isText)
942 return value;
943 throw XmlError ("Child element is not expected in this operation.");
944 case XmlNodeType.EndElement:
945 return value;
946 case XmlNodeType.Text:
947 case XmlNodeType.CDATA:
948 case XmlNodeType.SignificantWhitespace:
949 case XmlNodeType.Whitespace:
950 value += Value;
951 break;
953 } while (Read ());
954 throw XmlError ("Unexpected end of document.");
957 string GetLocation ()
959 IXmlLineInfo li = this as IXmlLineInfo;
960 return li != null && li.HasLineInfo () ?
961 String.Format (" {0} (line {1}, column {2})", BaseURI, li.LineNumber, li.LinePosition) : String.Empty;
964 [MonoTODO]
965 public virtual object ReadElementContentAsObject ()
967 return ReadElementContentAs (ValueType, null);
970 [MonoTODO]
971 public virtual object ReadElementContentAsObject (string localName, string namespaceURI)
973 return ReadElementContentAs (ValueType, null, localName, namespaceURI);
976 [MonoTODO]
977 public virtual object ReadContentAsObject ()
979 return ReadContentAs (ValueType, null);
982 public virtual object ReadElementContentAs (Type type, IXmlNamespaceResolver resolver)
984 bool isEmpty = IsEmptyElement;
985 ReadStartElement ();
986 object obj = ValueAs (isEmpty ? String.Empty : ReadContentString (false), type, resolver, false);
987 if (!isEmpty)
988 ReadEndElement ();
989 return obj;
992 public virtual object ReadElementContentAs (Type type, IXmlNamespaceResolver resolver, string localName, string namespaceURI)
994 bool isEmpty = IsEmptyElement;
995 ReadStartElement (localName, namespaceURI);
996 if (isEmpty)
997 return ValueAs (String.Empty, type, resolver, false);
998 object obj = ReadContentAs (type, resolver);
999 ReadEndElement ();
1000 return obj;
1003 public virtual object ReadContentAs (Type type, IXmlNamespaceResolver resolver)
1005 return ValueAs (ReadContentString (), type, resolver, false);
1008 private object ValueAs (string text, Type type, IXmlNamespaceResolver resolver, bool isArrayItem)
1010 try {
1011 if (type == typeof (object))
1012 return text;
1013 if (type.IsArray && !isArrayItem) {
1014 var elemType = type.GetElementType ();
1015 var sarr = text.Split ((string []) null, StringSplitOptions.RemoveEmptyEntries);
1016 var ret = Array.CreateInstance (elemType, sarr.Length);
1017 for (int i = 0; i < ret.Length; i++)
1018 ret.SetValue (ValueAs (sarr [i], elemType, resolver, true), i);
1019 return ret;
1022 if (type == typeof (XmlQualifiedName)) {
1023 if (resolver != null)
1024 return XmlQualifiedName.Parse (text, resolver, true);
1025 else
1026 return XmlQualifiedName.Parse (text, this, true);
1028 if (type == typeof (Uri))
1029 return XmlConvert.ToUri (text);
1030 if (type == typeof (TimeSpan))
1031 return XmlConvert.ToTimeSpan (text);
1032 if (type == typeof (DateTimeOffset))
1033 return XmlConvert.ToDateTimeOffset (text);
1035 switch (Type.GetTypeCode (type)) {
1036 case TypeCode.Boolean:
1037 return XQueryConvert.StringToBoolean (text);
1038 case TypeCode.DateTime:
1039 return XQueryConvert.StringToDateTime (text);
1040 case TypeCode.Decimal:
1041 return XQueryConvert.StringToDecimal (text);
1042 case TypeCode.Double:
1043 return XQueryConvert.StringToDouble (text);
1044 case TypeCode.Int32:
1045 return XQueryConvert.StringToInt (text);
1046 case TypeCode.Int64:
1047 return XQueryConvert.StringToInteger (text);
1048 case TypeCode.Single:
1049 return XQueryConvert.StringToFloat (text);
1050 case TypeCode.String:
1051 return text;
1053 } catch (Exception ex) {
1054 throw XmlError (String.Format ("Current text value '{0}' is not acceptable for specified type '{1}'. {2}", text, type, ex != null ? ex.Message : String.Empty), ex);
1056 throw new ArgumentException (String.Format ("Specified type '{0}' is not supported.", type));
1059 public virtual bool ReadElementContentAsBoolean ()
1061 try {
1062 return XQueryConvert.StringToBoolean (ReadElementContentAsString ());
1063 } catch (FormatException ex) {
1064 throw XmlError ("Typed value is invalid.", ex);
1068 public virtual DateTime ReadElementContentAsDateTime ()
1070 try {
1071 return XQueryConvert.StringToDateTime (ReadElementContentAsString ());
1072 } catch (FormatException ex) {
1073 throw XmlError ("Typed value is invalid.", ex);
1077 public virtual decimal ReadElementContentAsDecimal ()
1079 try {
1080 return XQueryConvert.StringToDecimal (ReadElementContentAsString ());
1081 } catch (FormatException ex) {
1082 throw XmlError ("Typed value is invalid.", ex);
1086 public virtual double ReadElementContentAsDouble ()
1088 try {
1089 return XQueryConvert.StringToDouble (ReadElementContentAsString ());
1090 } catch (FormatException ex) {
1091 throw XmlError ("Typed value is invalid.", ex);
1095 public virtual float ReadElementContentAsFloat ()
1097 try {
1098 return XQueryConvert.StringToFloat (ReadElementContentAsString ());
1099 } catch (FormatException ex) {
1100 throw XmlError ("Typed value is invalid.", ex);
1104 public virtual int ReadElementContentAsInt ()
1106 try {
1107 return XQueryConvert.StringToInt (ReadElementContentAsString ());
1108 } catch (FormatException ex) {
1109 throw XmlError ("Typed value is invalid.", ex);
1113 public virtual long ReadElementContentAsLong ()
1115 try {
1116 return XQueryConvert.StringToInteger (ReadElementContentAsString ());
1117 } catch (FormatException ex) {
1118 throw XmlError ("Typed value is invalid.", ex);
1122 public virtual string ReadElementContentAsString ()
1124 bool isEmpty = IsEmptyElement;
1125 // unlike ReadStartElement() it rejects non-content nodes (this check is done before MoveToContent())
1126 if (NodeType != XmlNodeType.Element)
1127 throw new InvalidOperationException (String.Format ("'{0}' is an element node.", NodeType));
1128 ReadStartElement ();
1129 if (isEmpty)
1130 return String.Empty;
1131 string s = ReadContentString (false);
1132 ReadEndElement ();
1133 return s;
1136 public virtual bool ReadElementContentAsBoolean (string localName, string namespaceURI)
1138 try {
1139 return XQueryConvert.StringToBoolean (ReadElementContentAsString (localName, namespaceURI));
1140 } catch (FormatException ex) {
1141 throw XmlError ("Typed value is invalid.", ex);
1145 public virtual DateTime ReadElementContentAsDateTime (string localName, string namespaceURI)
1147 try {
1148 return XQueryConvert.StringToDateTime (ReadElementContentAsString (localName, namespaceURI));
1149 } catch (FormatException ex) {
1150 throw XmlError ("Typed value is invalid.", ex);
1154 public virtual decimal ReadElementContentAsDecimal (string localName, string namespaceURI)
1156 try {
1157 return XQueryConvert.StringToDecimal (ReadElementContentAsString (localName, namespaceURI));
1158 } catch (FormatException ex) {
1159 throw XmlError ("Typed value is invalid.", ex);
1163 public virtual double ReadElementContentAsDouble (string localName, string namespaceURI)
1165 try {
1166 return XQueryConvert.StringToDouble (ReadElementContentAsString (localName, namespaceURI));
1167 } catch (FormatException ex) {
1168 throw XmlError ("Typed value is invalid.", ex);
1172 public virtual float ReadElementContentAsFloat (string localName, string namespaceURI)
1174 try {
1175 return XQueryConvert.StringToFloat (ReadElementContentAsString (localName, namespaceURI));
1176 } catch (FormatException ex) {
1177 throw XmlError ("Typed value is invalid.", ex);
1181 public virtual int ReadElementContentAsInt (string localName, string namespaceURI)
1183 try {
1184 return XQueryConvert.StringToInt (ReadElementContentAsString (localName, namespaceURI));
1185 } catch (FormatException ex) {
1186 throw XmlError ("Typed value is invalid.", ex);
1190 public virtual long ReadElementContentAsLong (string localName, string namespaceURI)
1192 try {
1193 return XQueryConvert.StringToInteger (ReadElementContentAsString (localName, namespaceURI));
1194 } catch (FormatException ex) {
1195 throw XmlError ("Typed value is invalid.", ex);
1199 public virtual string ReadElementContentAsString (string localName, string namespaceURI)
1201 bool isEmpty = IsEmptyElement;
1202 // unlike ReadStartElement() it rejects non-content nodes (this check is done before MoveToContent())
1203 if (NodeType != XmlNodeType.Element)
1204 throw new InvalidOperationException (String.Format ("'{0}' is an element node.", NodeType));
1205 ReadStartElement (localName, namespaceURI);
1206 if (isEmpty)
1207 return String.Empty;
1208 string s = ReadContentString (false);
1209 ReadEndElement ();
1210 return s;
1213 public virtual bool ReadContentAsBoolean ()
1215 try {
1216 return XQueryConvert.StringToBoolean (ReadContentString ());
1217 } catch (FormatException ex) {
1218 throw XmlError ("Typed value is invalid.", ex);
1222 public virtual DateTime ReadContentAsDateTime ()
1224 try {
1225 return XQueryConvert.StringToDateTime (ReadContentString ());
1226 } catch (FormatException ex) {
1227 throw XmlError ("Typed value is invalid.", ex);
1231 public virtual decimal ReadContentAsDecimal ()
1233 try {
1234 return XQueryConvert.StringToDecimal (ReadContentString ());
1235 } catch (FormatException ex) {
1236 throw XmlError ("Typed value is invalid.", ex);
1240 public virtual double ReadContentAsDouble ()
1242 try {
1243 return XQueryConvert.StringToDouble (ReadContentString ());
1244 } catch (FormatException ex) {
1245 throw XmlError ("Typed value is invalid.", ex);
1249 public virtual float ReadContentAsFloat ()
1251 try {
1252 return XQueryConvert.StringToFloat (ReadContentString ());
1253 } catch (FormatException ex) {
1254 throw XmlError ("Typed value is invalid.", ex);
1258 public virtual int ReadContentAsInt ()
1260 try {
1261 return XQueryConvert.StringToInt (ReadContentString ());
1262 } catch (FormatException ex) {
1263 throw XmlError ("Typed value is invalid.", ex);
1267 public virtual long ReadContentAsLong ()
1269 try {
1270 return XQueryConvert.StringToInteger (ReadContentString ());
1271 } catch (FormatException ex) {
1272 throw XmlError ("Typed value is invalid.", ex);
1276 public virtual string ReadContentAsString ()
1278 return ReadContentString ();
1281 public virtual int ReadContentAsBase64 (
1282 byte [] buffer, int offset, int length)
1284 CheckSupport ();
1285 return binary.ReadContentAsBase64 (
1286 buffer, offset, length);
1289 public virtual int ReadContentAsBinHex (
1290 byte [] buffer, int offset, int length)
1292 CheckSupport ();
1293 return binary.ReadContentAsBinHex (
1294 buffer, offset, length);
1297 public virtual int ReadElementContentAsBase64 (
1298 byte [] buffer, int offset, int length)
1300 CheckSupport ();
1301 return binary.ReadElementContentAsBase64 (
1302 buffer, offset, length);
1305 public virtual int ReadElementContentAsBinHex (
1306 byte [] buffer, int offset, int length)
1308 CheckSupport ();
1309 return binary.ReadElementContentAsBinHex (
1310 buffer, offset, length);
1313 private void CheckSupport ()
1315 // Default implementation expects both.
1316 if (!CanReadBinaryContent || !CanReadValueChunk)
1317 throw new NotSupportedException ();
1318 if (binary == null)
1319 binary = new XmlReaderBinarySupport (this);
1322 #endif
1324 #if NET_2_0
1325 public virtual int ReadValueChunk (
1326 char [] buffer, int offset, int length)
1327 #else
1328 internal virtual int ReadValueChunk (
1329 char [] buffer, int offset, int length)
1330 #endif
1332 if (!CanReadValueChunk)
1333 throw new NotSupportedException ();
1334 if (binary == null)
1335 binary = new XmlReaderBinarySupport (this);
1336 return binary.ReadValueChunk (buffer, offset, length);
1339 public abstract void ResolveEntity ();
1341 public virtual void Skip ()
1343 if (ReadState != ReadState.Interactive)
1344 return;
1346 MoveToElement ();
1347 if (NodeType != XmlNodeType.Element || IsEmptyElement) {
1348 Read ();
1349 return;
1352 int depth = Depth;
1353 while (Read () && depth < Depth)
1355 if (NodeType == XmlNodeType.EndElement)
1356 Read ();
1359 private XmlException XmlError (string message)
1361 return new XmlException (this as IXmlLineInfo, BaseURI, message);
1363 #if NET_2_0
1364 private XmlException XmlError (string message, Exception innerException)
1366 return new XmlException (this as IXmlLineInfo, BaseURI, message);
1368 #endif
1369 #endregion