2009-01-08 Atsushi Enomoto <atsushi@ximian.com>
[mono.git] / mcs / class / System.XML / System.Xml / XmlReader.cs
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
12 //
13
14 //
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.
33 //
34 using System.Collections;
35 using System.Diagnostics;
36 using System.IO;
37 using System.Text;
38 #if !NET_2_1
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
44
45 namespace System.Xml
46 {
47 #if NET_2_0
48         public abstract class XmlReader : IDisposable
49 #else
50         public abstract class XmlReader
51 #endif
52         {
53                 private StringBuilder readStringBuffer;
54                 private XmlReaderBinarySupport binary;
55 #if NET_2_0
56                 private XmlReaderSettings settings;
57 #endif
58
59                 #region Constructor
60
61                 protected XmlReader ()
62                 {
63                 }
64
65                 #endregion
66
67                 #region Properties
68
69                 public abstract int AttributeCount { get; }
70
71                 public abstract string BaseURI { get; }
72
73                 internal XmlReaderBinarySupport Binary {
74                         get { return binary; }
75                 }
76
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;
83                         }
84                 }
85
86 #if NET_2_0
87                 // To enable it internally in sys.xml, just insert these
88                 // two lines into Read():
89                 //
90                 //      #if NET_2_0
91                 //      if (Binary != null)
92                 //              Binary.Reset ();
93                 //      #endif
94                 //
95                 public virtual bool CanReadBinaryContent {
96                         get { return false; }
97                 }
98
99                 public virtual bool CanReadValueChunk {
100                         get { return false; }
101                 }
102 #else
103                 internal virtual bool CanReadBinaryContent {
104                         get { return false; }
105                 }
106
107                 internal virtual bool CanReadValueChunk {
108                         get { return false; }
109                 }
110 #endif
111
112                 public virtual bool CanResolveEntity
113                 {
114                         get     { return false; }
115                 }
116
117                 public abstract int Depth { get; }
118
119                 public abstract bool EOF { get; }
120
121                 public virtual bool HasAttributes
122                 {
123                         get { return AttributeCount > 0; }
124                 }
125
126 #if !NET_2_1
127                 public abstract bool HasValue { get; }
128 #endif
129
130                 public abstract bool IsEmptyElement { get; }
131
132 #if NET_2_0
133                 public virtual bool IsDefault {
134                         get { return false; }
135                 }
136
137                 public virtual string this [int i] {
138                         get { return GetAttribute (i); }
139                 }
140
141                 public virtual string this [string name] {
142                         get { return GetAttribute (name); }
143                 }
144
145                 public virtual string this [string name, string namespaceURI] {
146                         get { return GetAttribute (name, namespaceURI); }
147                 }
148 #else
149                 public abstract bool IsDefault { get; }
150
151                 public abstract string this [int i] { get; }
152
153                 public abstract string this [string name] { get; }
154
155                 public abstract string this [string localName, string namespaceName] { get; }
156 #endif
157
158                 public abstract string LocalName { get; }
159
160 #if NET_2_0
161                 public virtual string Name {
162                         get {
163                                 return Prefix.Length > 0 ?
164                                         String.Concat (Prefix, ":", LocalName) :
165                                         LocalName;
166                         }
167                 }
168 #else
169                 public abstract string Name { get; }
170 #endif
171
172                 public abstract string NamespaceURI { get; }
173
174                 public abstract XmlNameTable NameTable { get; }
175
176                 public abstract XmlNodeType NodeType { get; }
177
178                 public abstract string Prefix { get; }
179
180 #if NET_2_0
181                 public virtual char QuoteChar {
182                         get { return '\"'; }
183                 }
184 #else
185                 public abstract char QuoteChar { get; }
186 #endif
187
188                 public abstract ReadState ReadState { get; }
189
190 #if NET_2_0
191 #if !NET_2_1
192                 public virtual IXmlSchemaInfo SchemaInfo {
193                         get { return null; }
194                 }
195 #endif
196
197                 public virtual XmlReaderSettings Settings {
198                         get { return settings; }
199                 }
200 #endif
201
202                 public abstract string Value { get; }
203
204 #if NET_2_0
205                 public virtual string XmlLang {
206                         get { return String.Empty; }
207                 }
208
209                 public virtual XmlSpace XmlSpace {
210                         get { return XmlSpace.None; }
211                 }
212 #else
213                 public abstract string XmlLang { get; }
214
215                 public abstract XmlSpace XmlSpace { get; }
216 #endif
217
218                 #endregion
219
220                 #region Methods
221
222                 public abstract void Close ();
223
224 #if NET_2_0
225                 private static XmlNameTable PopulateNameTable (
226                         XmlReaderSettings settings)
227                 {
228                         XmlNameTable nameTable = settings.NameTable;
229                         if (nameTable == null)
230                                 nameTable = new NameTable ();
231                         return nameTable;
232                 }
233
234                 private static XmlParserContext PopulateParserContext (
235                         XmlReaderSettings settings, string baseUri)
236                 {
237                         XmlNameTable nt = PopulateNameTable (settings);
238                         return new XmlParserContext (nt,
239                                 new XmlNamespaceManager (nt),
240                                 null,
241                                 null,
242                                 null,
243                                 null,
244                                 baseUri,
245                                 null,
246                                 XmlSpace.None,
247                                 null);
248                 }
249
250                 private static XmlNodeType GetNodeType (
251                         XmlReaderSettings settings)
252                 {
253                         ConformanceLevel level = settings != null ? settings.ConformanceLevel : ConformanceLevel.Auto;
254                         return
255                                 level == ConformanceLevel.Fragment ?
256                                 XmlNodeType.Element :
257                                 XmlNodeType.Document;
258                 }
259
260                 public static XmlReader Create (Stream stream)
261                 {
262                         return Create (stream, null);
263                 }
264
265                 public static XmlReader Create (string url)
266                 {
267                         return Create (url, null);
268                 }
269
270                 public static XmlReader Create (TextReader reader)
271                 {
272                         return Create (reader, null);
273                 }
274
275                 public static XmlReader Create (string url, XmlReaderSettings settings)
276                 {
277                         return Create (url, settings, null);
278                 }
279
280                 public static XmlReader Create (Stream stream, XmlReaderSettings settings)
281                 {
282                         return Create (stream, settings, String.Empty);
283                 }
284
285                 public static XmlReader Create (TextReader reader, XmlReaderSettings settings)
286                 {
287                         return Create (reader, settings, String.Empty);
288                 }
289
290                 static XmlReaderSettings PopulateSettings (XmlReaderSettings src)
291                 {
292                         if (src == null)
293                                 return new XmlReaderSettings ();
294                         else
295                                 return src.Clone ();
296                 }
297
298                 public static XmlReader Create (Stream stream, XmlReaderSettings settings, string baseUri)
299                 {
300                         settings = PopulateSettings (settings);
301                         return Create (stream, settings,
302                                 PopulateParserContext (settings, baseUri));
303                 }
304
305                 public static XmlReader Create (TextReader reader, XmlReaderSettings settings, string baseUri)
306                 {
307                         settings = PopulateSettings (settings);
308                         return Create (reader, settings,
309                                 PopulateParserContext (settings, baseUri));
310                 }
311
312                 public static XmlReader Create (XmlReader reader, XmlReaderSettings settings)
313                 {
314                         settings = PopulateSettings (settings);
315                         XmlReader r = CreateFilteredXmlReader (reader, settings);
316                         r.settings = settings;
317                         return r;
318                 }
319
320                 public static XmlReader Create (string url, XmlReaderSettings settings, XmlParserContext context)
321                 {
322                         settings = PopulateSettings (settings);
323                         bool closeInputBak = settings.CloseInput;
324                         try {
325                                 settings.CloseInput = true; // forced. See XmlReaderCommonTests.CreateFromUrlClose().
326                                 if (context == null)
327                                         context = PopulateParserContext (settings, url);
328                                 XmlTextReader xtr = new XmlTextReader (false, settings.XmlResolver, url, GetNodeType (settings), context);
329                                 XmlReader ret = CreateCustomizedTextReader (xtr, settings);
330                                 return ret;
331                         } finally {
332                                 settings.CloseInput = closeInputBak;
333                         }
334                 }
335
336                 public static XmlReader Create (Stream stream, XmlReaderSettings settings, XmlParserContext context)
337                 {
338                         settings = PopulateSettings (settings);
339                         if (context == null)
340                                 context = PopulateParserContext (settings, String.Empty);
341                         return CreateCustomizedTextReader (new XmlTextReader (stream, GetNodeType (settings), context), settings);
342                 }
343
344                 public static XmlReader Create (TextReader reader, XmlReaderSettings settings, XmlParserContext context)
345                 {
346                         settings = PopulateSettings (settings);
347                         if (context == null)
348                                 context = PopulateParserContext (settings, String.Empty);
349                         return CreateCustomizedTextReader (new XmlTextReader (context.BaseURI, reader, GetNodeType (settings), context), settings);
350                 }
351
352                 private static XmlReader CreateCustomizedTextReader (XmlTextReader reader, XmlReaderSettings settings)
353                 {
354                         reader.XmlResolver = settings.XmlResolver;
355                         // Normalization is set true by default.
356                         reader.Normalization = true;
357                         reader.EntityHandling = EntityHandling.ExpandEntities;
358
359                         if (settings.ProhibitDtd)
360                                 reader.ProhibitDtd = true;
361
362                         if (!settings.CheckCharacters)
363                                 reader.CharacterChecking = false;
364
365                         // I guess it might be changed in 2.0 RTM to set true
366                         // as default, or just disappear. It goes against
367                         // XmlTextReader's default usage and users will have 
368                         // to close input manually (that's annoying). Moreover,
369                         // MS XmlTextReader consumes text input more than 
370                         // actually read and users can acquire those extra
371                         // consumption by GetRemainder() that returns different
372                         // TextReader.
373                         reader.CloseInput = settings.CloseInput;
374
375                         // I would like to support it in detail later;
376                         // MSDN description looks source of confusion. We don't
377                         // need examples, but precise list of how it works.
378                         reader.Conformance = settings.ConformanceLevel;
379
380                         reader.AdjustLineInfoOffset (settings.LineNumberOffset,
381                                 settings.LinePositionOffset);
382
383                         if (settings.NameTable != null)
384                                 reader.SetNameTable (settings.NameTable);
385
386                         XmlReader r = CreateFilteredXmlReader (reader, settings);
387                         r.settings = settings;
388                         return r;
389                 }
390
391                 private static XmlReader CreateFilteredXmlReader (XmlReader reader, XmlReaderSettings settings)
392                 {
393                         ConformanceLevel conf = ConformanceLevel.Auto;
394                         if (reader is XmlTextReader)
395                                 conf = ((XmlTextReader) reader).Conformance;
396                         else if (reader.Settings != null)
397                                 conf = reader.Settings.ConformanceLevel;
398                         else
399                                 conf = settings.ConformanceLevel;
400                         if (settings.ConformanceLevel != ConformanceLevel.Auto &&
401                                 conf != settings.ConformanceLevel)
402                                 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));
403                         settings.ConformanceLevel = conf;
404
405                         reader = CreateValidatingXmlReader (reader, settings);
406
407                         if ( settings.IgnoreComments ||
408                              settings.IgnoreProcessingInstructions ||
409                              settings.IgnoreWhitespace)
410                                 return new XmlFilterReader (reader, settings);
411                         else {
412                                 reader.settings = settings;
413                                 return reader;
414                         }
415                 }
416
417                 private static XmlReader CreateValidatingXmlReader (XmlReader reader, XmlReaderSettings settings)
418                 {
419 #if NET_2_1
420                         return reader;
421 #else
422                         XmlValidatingReader xvr = null;
423                         switch (settings.ValidationType) {
424                         // Auto and XDR are obsoleted in 2.0 and therefore ignored.
425                         default:
426                                 return reader;
427                         case ValidationType.DTD:
428                                 xvr = new XmlValidatingReader (reader);
429                                 xvr.XmlResolver = settings.XmlResolver;
430                                 xvr.ValidationType = ValidationType.DTD;
431                                 break;
432                         case ValidationType.Schema:
433                                 return new XmlSchemaValidatingReader (reader, settings);
434                         }
435
436                         // Actually I don't think they are treated in DTD validation though...
437                         if ((settings.ValidationFlags & XmlSchemaValidationFlags.ProcessIdentityConstraints) == 0)
438                                 throw new NotImplementedException ();
439                         //if ((settings.ValidationFlags & XmlSchemaValidationFlags.ProcessInlineSchema) != 0)
440                         //      throw new NotImplementedException ();
441                         //if ((settings.ValidationFlags & XmlSchemaValidationFlags.ProcessSchemaLocation) != 0)
442                         //      throw new NotImplementedException ();
443                         //if ((settings.ValidationFlags & XmlSchemaValidationFlags.ReportValidationWarnings) == 0)
444                         //      throw new NotImplementedException ();
445
446                         return xvr != null ? xvr : reader;
447 #endif
448                 }
449
450                 void IDisposable.Dispose ()
451                 {
452                         Dispose (false);
453                 }
454
455                 protected virtual void Dispose (bool disposing)
456                 {
457                         if (ReadState != ReadState.Closed)
458                                 Close ();
459                 }
460 #endif
461
462                 public abstract string GetAttribute (int i);
463
464                 public abstract string GetAttribute (string name);
465
466                 public abstract string GetAttribute (
467                         string localName,
468                         string namespaceName);
469
470                 public static bool IsName (string s)
471                 {
472                         return s != null && XmlChar.IsName (s);
473                 }
474
475                 public static bool IsNameToken (string s)
476                 {
477                         return s != null && XmlChar.IsNmToken (s);
478                 }
479
480                 public virtual bool IsStartElement ()
481                 {
482                         return (MoveToContent () == XmlNodeType.Element);
483                 }
484
485                 public virtual bool IsStartElement (string name)
486                 {
487                         if (!IsStartElement ())
488                                 return false;
489
490                         return (Name == name);
491                 }
492
493                 public virtual bool IsStartElement (string localName, string namespaceName)
494                 {
495                         if (!IsStartElement ())
496                                 return false;
497
498                         return (LocalName == localName && NamespaceURI == namespaceName);
499                 }
500
501                 public abstract string LookupNamespace (string prefix);
502
503 #if NET_2_0
504                 public virtual void MoveToAttribute (int i)
505                 {
506                         if (i >= AttributeCount)
507                                 throw new ArgumentOutOfRangeException ();
508                         MoveToFirstAttribute ();
509                         for (int a = 0; a < i; a++)
510                                 MoveToNextAttribute ();
511                 }
512 #else
513                 public abstract void MoveToAttribute (int i);
514 #endif
515
516                 public abstract bool MoveToAttribute (string name);
517
518                 public abstract bool MoveToAttribute (
519                         string localName,
520                         string namespaceName);
521
522                 private bool IsContent (XmlNodeType nodeType)
523                 {
524                         /* MS doc says:
525                          * (non-white space text, CDATA, Element, EndElement, EntityReference, or EndEntity)
526                          */
527                         switch (nodeType) {
528                         case XmlNodeType.Text:
529                                 return true;
530                         case XmlNodeType.CDATA:
531                                 return true;
532                         case XmlNodeType.Element:
533                                 return true;
534                         case XmlNodeType.EndElement:
535                                 return true;
536                         case XmlNodeType.EntityReference:
537                                 return true;
538                         case XmlNodeType.EndEntity:
539                                 return true;
540                         }
541
542                         return false;
543                 }
544
545                 public virtual XmlNodeType MoveToContent ()
546                 {
547                         if (NodeType == XmlNodeType.Attribute)
548                                 MoveToElement ();
549
550                         do {
551                                 if (IsContent (NodeType))
552                                         return NodeType;
553                                 Read ();
554                         } while (!EOF);
555                         return XmlNodeType.None;
556                 }
557
558                 public abstract bool MoveToElement ();
559
560                 public abstract bool MoveToFirstAttribute ();
561
562                 public abstract bool MoveToNextAttribute ();
563
564                 public abstract bool Read ();
565
566 #if !NET_2_1
567                 public abstract bool ReadAttributeValue ();
568 #endif
569
570                 public virtual string ReadElementString ()
571                 {
572                         if (MoveToContent () != XmlNodeType.Element) {
573                                 string error = String.Format ("'{0}' is an invalid node type.",
574                                                               NodeType.ToString ());
575                                 throw XmlError (error);
576                         }
577
578                         string result = String.Empty;
579                         if (!IsEmptyElement) {
580                                 Read ();
581                                 result = ReadString ();
582                                 if (NodeType != XmlNodeType.EndElement) {
583                                         string error = String.Format ("'{0}' is an invalid node type.",
584                                                                       NodeType.ToString ());
585                                         throw XmlError (error);
586                                 }
587                         }
588
589                         Read ();
590                         return result;
591                 }
592
593                 public virtual string ReadElementString (string name)
594                 {
595                         if (MoveToContent () != XmlNodeType.Element) {
596                                 string error = String.Format ("'{0}' is an invalid node type.",
597                                                               NodeType.ToString ());
598                                 throw XmlError (error);
599                         }
600
601                         if (name != Name) {
602                                 string error = String.Format ("The {0} tag from namespace {1} is expected.",
603                                                               Name, NamespaceURI);
604                                 throw XmlError (error);
605                         }
606
607                         string result = String.Empty;
608                         if (!IsEmptyElement) {
609                                 Read ();
610                                 result = ReadString ();
611                                 if (NodeType != XmlNodeType.EndElement) {
612                                         string error = String.Format ("'{0}' is an invalid node type.",
613                                                                       NodeType.ToString ());
614                                         throw XmlError (error);
615                                 }
616                         }
617
618                         Read ();
619                         return result;
620                 }
621
622                 public virtual string ReadElementString (string localName, string namespaceName)
623                 {
624                         if (MoveToContent () != XmlNodeType.Element) {
625                                 string error = String.Format ("'{0}' is an invalid node type.",
626                                                               NodeType.ToString ());
627                                 throw XmlError (error);
628                         }
629
630                         if (localName != LocalName || NamespaceURI != namespaceName) {
631                                 string error = String.Format ("The {0} tag from namespace {1} is expected.",
632                                                               LocalName, NamespaceURI);
633                                 throw XmlError (error);
634                         }
635
636                         string result = String.Empty;
637                         if (!IsEmptyElement) {
638                                 Read ();
639                                 result = ReadString ();
640                                 if (NodeType != XmlNodeType.EndElement) {
641                                         string error = String.Format ("'{0}' is an invalid node type.",
642                                                                       NodeType.ToString ());
643                                         throw XmlError (error);
644                                 }
645                         }
646
647                         Read ();
648                         return result;
649                 }
650
651                 public virtual void ReadEndElement ()
652                 {
653                         if (MoveToContent () != XmlNodeType.EndElement) {
654                                 string error = String.Format ("'{0}' is an invalid node type.",
655                                                               NodeType.ToString ());
656                                 throw XmlError (error);
657                         }
658
659                         Read ();
660                 }
661
662                 public virtual string ReadInnerXml ()
663                 {
664                         if (ReadState != ReadState.Interactive || NodeType == XmlNodeType.EndElement)
665                                 return String.Empty;
666
667                         if (IsEmptyElement) {
668                                 Read ();
669                                 return String.Empty;
670                         }
671                         StringWriter sw = new StringWriter ();
672                         XmlTextWriter xtw = new XmlTextWriter (sw);
673                         if (NodeType == XmlNodeType.Element) {
674                                 int startDepth = Depth;
675                                 Read ();
676                                 while (startDepth < Depth) {
677                                         if (ReadState != ReadState.Interactive)
678                                                 throw XmlError ("Unexpected end of the XML reader.");
679                                         xtw.WriteNode (this, false);
680                                 }
681                                 // reader is now end element, then proceed once more.
682                                 Read ();
683                         }
684                         else
685                                 xtw.WriteNode (this, false);
686
687                         return sw.ToString ();
688                 }
689
690                 public virtual string ReadOuterXml ()
691                 {
692                         if (ReadState != ReadState.Interactive || NodeType == XmlNodeType.EndElement)
693                                 return String.Empty;
694
695                         switch (NodeType) {
696                         case XmlNodeType.Element:
697                         case XmlNodeType.Attribute:
698                                 StringWriter sw = new StringWriter ();
699                                 XmlTextWriter xtw = new XmlTextWriter (sw);
700                                 xtw.WriteNode (this, false);
701                                 return sw.ToString ();
702                         default:
703                                 Skip ();
704                                 return String.Empty;
705                         }
706                 }
707
708                 public virtual void ReadStartElement ()
709                 {
710                         if (MoveToContent () != XmlNodeType.Element) {
711                                 string error = String.Format ("'{0}' is an invalid node type.",
712                                                               NodeType.ToString ());
713                                 throw XmlError (error);
714                         }
715
716                         Read ();
717                 }
718
719                 public virtual void ReadStartElement (string name)
720                 {
721                         if (MoveToContent () != XmlNodeType.Element) {
722                                 string error = String.Format ("'{0}' is an invalid node type.",
723                                                               NodeType.ToString ());
724                                 throw XmlError (error);
725                         }
726
727                         if (name != Name) {
728                                 string error = String.Format ("The {0} tag from namespace {1} is expected.",
729                                                               Name, NamespaceURI);
730                                 throw XmlError (error);
731                         }
732
733                         Read ();
734                 }
735
736                 public virtual void ReadStartElement (string localName, string namespaceName)
737                 {
738                         if (MoveToContent () != XmlNodeType.Element) {
739                                 string error = String.Format ("'{0}' is an invalid node type.",
740                                                               NodeType.ToString ());
741                                 throw XmlError (error);
742                         }
743
744                         if (localName != LocalName || NamespaceURI != namespaceName) {
745                                 string error = String.Format ("Expecting {0} tag from namespace {1}, got {2} and {3} instead",
746                                                               localName, namespaceName,
747                                                               LocalName, NamespaceURI);
748                                 throw XmlError (error);
749                         }
750
751                         Read ();
752                 }
753
754                 public virtual string ReadString ()
755                 {
756                         if (readStringBuffer == null)
757                                 readStringBuffer = new StringBuilder ();
758                         readStringBuffer.Length = 0;
759
760                         MoveToElement ();
761
762                         switch (NodeType) {
763                         default:
764                                 return String.Empty;
765                         case XmlNodeType.Element:
766                                 if (IsEmptyElement)
767                                         return String.Empty;
768                                 do {
769                                         Read ();
770                                         switch (NodeType) {
771                                         case XmlNodeType.Text:
772                                         case XmlNodeType.CDATA:
773                                         case XmlNodeType.Whitespace:
774                                         case XmlNodeType.SignificantWhitespace:
775                                                 readStringBuffer.Append (Value);
776                                                 continue;
777                                         }
778                                         break;
779                                 } while (true);
780                                 break;
781                         case XmlNodeType.Text:
782                         case XmlNodeType.CDATA:
783                         case XmlNodeType.Whitespace:
784                         case XmlNodeType.SignificantWhitespace:
785                                 do {
786                                         switch (NodeType) {
787                                         case XmlNodeType.Text:
788                                         case XmlNodeType.CDATA:
789                                         case XmlNodeType.Whitespace:
790                                         case XmlNodeType.SignificantWhitespace:
791                                                 readStringBuffer.Append (Value);
792                                                 Read ();
793                                                 continue;
794                                         }
795                                         break;
796                                 } while (true);
797                                 break;
798                         }
799                         string ret = readStringBuffer.ToString ();
800                         readStringBuffer.Length = 0;
801                         return ret;
802                 }
803
804 #if NET_2_0
805                 public virtual Type ValueType {
806                         get { return typeof (string); }
807                 }
808
809                 public virtual bool ReadToDescendant (string name)
810                 {
811                         if (ReadState == ReadState.Initial) {
812                                 MoveToContent ();
813                                 if (IsStartElement (name))
814                                         return true;
815                         }
816                         if (NodeType != XmlNodeType.Element || IsEmptyElement)
817                                 return false;
818                         int depth = Depth;
819                         for (Read (); depth < Depth; Read ())
820                                 if (NodeType == XmlNodeType.Element && name == Name)
821                                         return true;
822                         return false;
823                 }
824
825                 public virtual bool ReadToDescendant (string localName, string namespaceURI)
826                 {
827                         if (ReadState == ReadState.Initial) {
828                                 MoveToContent ();
829                                 if (IsStartElement (localName, namespaceURI))
830                                         return true;
831                         }
832                         if (NodeType != XmlNodeType.Element || IsEmptyElement)
833                                 return false;
834                         int depth = Depth;
835                         for (Read (); depth < Depth; Read ())
836                                 if (NodeType == XmlNodeType.Element && localName == LocalName && namespaceURI == NamespaceURI)
837                                         return true;
838                         return false;
839                 }
840
841                 public virtual bool ReadToFollowing (string name)
842                 {
843                         while (Read ())
844                                 if (NodeType == XmlNodeType.Element && name == Name)
845                                         return true;
846                         return false;
847                 }
848
849                 public virtual bool ReadToFollowing (string localName, string namespaceURI)
850                 {
851                         while (Read ())
852                                 if (NodeType == XmlNodeType.Element && localName == Name && namespaceURI == NamespaceURI)
853                                         return true;
854                         return false;
855                 }
856
857                 public virtual bool ReadToNextSibling (string name)
858                 {
859                         if (ReadState != ReadState.Interactive)
860                                 return false;
861                         int depth = Depth;
862                         Skip ();
863                         for (; !EOF && depth <= Depth; Skip ())
864                                 if (NodeType == XmlNodeType.Element && name == Name)
865                                         return true;
866                         return false;
867                 }
868
869                 public virtual bool ReadToNextSibling (string localName, string namespaceURI)
870                 {
871                         if (ReadState != ReadState.Interactive)
872                                 return false;
873                         int depth = Depth;
874                         Skip ();
875                         for (; !EOF && depth <= Depth; Skip ())
876                                 if (NodeType == XmlNodeType.Element && localName == LocalName && namespaceURI == NamespaceURI)
877                                         return true;
878                         return false;
879                 }
880
881                 public virtual XmlReader ReadSubtree ()
882                 {
883                         if (NodeType != XmlNodeType.Element)
884                                 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 ()));
885                         return new SubtreeXmlReader (this);
886                 }
887
888                 private string ReadContentString ()
889                 {
890                         if (NodeType == XmlNodeType.Attribute)
891                                 return Value;
892                         return ReadContentString (true);
893                 }
894
895                 private string ReadContentString (bool isText)
896                 {
897                         if (isText) {
898                                 switch (NodeType) {
899                                 case XmlNodeType.Text:
900                                 case XmlNodeType.SignificantWhitespace:
901                                 case XmlNodeType.Whitespace:
902                                 case XmlNodeType.CDATA:
903                                         break;
904                                 case XmlNodeType.Element:
905                                         throw new InvalidOperationException (String.Format ("Node type {0} is not supported in this operation.{1}", NodeType, GetLocation ()));
906                                 default:
907                                         return String.Empty;
908                                 }
909                         }
910
911                         string value = String.Empty;
912                         do {
913                                 switch (NodeType) {
914                                 case XmlNodeType.Element:
915                                         if (isText)
916                                                 return value;
917                                         throw XmlError ("Child element is not expected in this operation.");
918                                 case XmlNodeType.EndElement:
919                                         return value;
920                                 case XmlNodeType.Text:
921                                 case XmlNodeType.CDATA:
922                                 case XmlNodeType.SignificantWhitespace:
923                                 case XmlNodeType.Whitespace:
924                                         value += Value;
925                                         break;
926                                 }
927                         } while (Read ());
928                         throw XmlError ("Unexpected end of document.");
929                 }
930
931                 string GetLocation ()
932                 {
933                         IXmlLineInfo li = this as IXmlLineInfo;
934                         return li != null && li.HasLineInfo () ?
935                                 String.Format (" {0} (line {1}, column {2})", BaseURI, li.LineNumber, li.LinePosition) : String.Empty;
936                 }
937
938                 [MonoTODO]
939                 public virtual object ReadElementContentAsObject ()
940                 {
941                         return ReadElementContentAs (ValueType, null);
942                 }
943
944                 [MonoTODO]
945                 public virtual object ReadElementContentAsObject (string localName, string namespaceURI)
946                 {
947                         return ReadElementContentAs (ValueType, null, localName, namespaceURI);
948                 }
949
950                 [MonoTODO]
951                 public virtual object ReadContentAsObject ()
952                 {
953                         return ReadContentAs (ValueType, null);
954                 }
955
956                 public virtual object ReadElementContentAs (Type type, IXmlNamespaceResolver resolver)
957                 {
958                         bool isEmpty = IsEmptyElement;
959                         ReadStartElement ();
960                         object obj = ValueAs (isEmpty ? String.Empty : ReadContentString (false), type, resolver);
961                         if (!isEmpty)
962                                 ReadEndElement ();
963                         return obj;
964                 }
965
966                 public virtual object ReadElementContentAs (Type type, IXmlNamespaceResolver resolver, string localName, string namespaceURI)
967                 {
968                         ReadStartElement (localName, namespaceURI);
969                         object obj = ReadContentAs (type, resolver);
970                         ReadEndElement ();
971                         return obj;
972                 }
973
974                 public virtual object ReadContentAs (Type type, IXmlNamespaceResolver resolver)
975                 {
976                         return ValueAs (ReadContentString (), type, resolver);
977                 }
978
979                 private object ValueAs (string text, Type type, IXmlNamespaceResolver resolver)
980                 {
981                         try {
982                                 if (type == typeof (object))
983                                         return text;
984                                 if (type == typeof (XmlQualifiedName)) {
985                                         if (resolver != null)
986                                                 return XmlQualifiedName.Parse (text, resolver);
987                                         else
988                                                 return XmlQualifiedName.Parse (text, this);
989                                 }
990
991                                 switch (Type.GetTypeCode (type)) {
992                                 case TypeCode.Boolean:
993                                         return XQueryConvert.StringToBoolean (text);
994                                 case TypeCode.DateTime:
995                                         return XQueryConvert.StringToDateTime (text);
996                                 case TypeCode.Decimal:
997                                         return XQueryConvert.StringToDecimal (text);
998                                 case TypeCode.Double:
999                                         return XQueryConvert.StringToDouble (text);
1000                                 case TypeCode.Int32:
1001                                         return XQueryConvert.StringToInt (text);
1002                                 case TypeCode.Int64:
1003                                         return XQueryConvert.StringToInteger (text);
1004                                 case TypeCode.Single:
1005                                         return XQueryConvert.StringToFloat (text);
1006                                 case TypeCode.String:
1007                                         return text;
1008                                 }
1009                         } catch (Exception ex) {
1010                                 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);
1011                         }
1012                         throw new ArgumentException (String.Format ("Specified type '{0}' is not supported.", type));
1013                 }
1014
1015                 public virtual bool ReadElementContentAsBoolean ()
1016                 {
1017                         try {
1018                                 return XQueryConvert.StringToBoolean (ReadElementContentAsString ());
1019                         } catch (FormatException ex) {
1020                                 throw XmlError ("Typed value is invalid.", ex);
1021                         }
1022                 }
1023
1024                 public virtual DateTime ReadElementContentAsDateTime ()
1025                 {
1026                         try {
1027                                 return XQueryConvert.StringToDateTime (ReadElementContentAsString ());
1028                         } catch (FormatException ex) {
1029                                 throw XmlError ("Typed value is invalid.", ex);
1030                         }
1031                 }
1032
1033                 public virtual decimal ReadElementContentAsDecimal ()
1034                 {
1035                         try {
1036                                 return XQueryConvert.StringToDecimal (ReadElementContentAsString ());
1037                         } catch (FormatException ex) {
1038                                 throw XmlError ("Typed value is invalid.", ex);
1039                         }
1040                 }
1041
1042                 public virtual double ReadElementContentAsDouble ()
1043                 {
1044                         try {
1045                                 return XQueryConvert.StringToDouble (ReadElementContentAsString ());
1046                         } catch (FormatException ex) {
1047                                 throw XmlError ("Typed value is invalid.", ex);
1048                         }
1049                 }
1050
1051                 public virtual float ReadElementContentAsFloat ()
1052                 {
1053                         try {
1054                                 return XQueryConvert.StringToFloat (ReadElementContentAsString ());
1055                         } catch (FormatException ex) {
1056                                 throw XmlError ("Typed value is invalid.", ex);
1057                         }
1058                 }
1059
1060                 public virtual int ReadElementContentAsInt ()
1061                 {
1062                         try {
1063                                 return XQueryConvert.StringToInt (ReadElementContentAsString ());
1064                         } catch (FormatException ex) {
1065                                 throw XmlError ("Typed value is invalid.", ex);
1066                         }
1067                 }
1068
1069                 public virtual long ReadElementContentAsLong ()
1070                 {
1071                         try {
1072                                 return XQueryConvert.StringToInteger (ReadElementContentAsString ());
1073                         } catch (FormatException ex) {
1074                                 throw XmlError ("Typed value is invalid.", ex);
1075                         }
1076                 }
1077
1078                 public virtual string ReadElementContentAsString ()
1079                 {
1080                         bool isEmpty = IsEmptyElement;
1081                         // unlike ReadStartElement() it rejects non-content nodes (this check is done before MoveToContent())
1082                         if (NodeType != XmlNodeType.Element)
1083                                 throw new InvalidOperationException (String.Format ("'{0}' is an element node.", NodeType));
1084                         ReadStartElement ();
1085                         if (isEmpty)
1086                                 return String.Empty;
1087                         string s = ReadContentString (false);
1088                         ReadEndElement ();
1089                         return s;
1090                 }
1091
1092                 public virtual bool ReadElementContentAsBoolean (string localName, string namespaceURI)
1093                 {
1094                         try {
1095                                 return XQueryConvert.StringToBoolean (ReadElementContentAsString (localName, namespaceURI));
1096                         } catch (FormatException ex) {
1097                                 throw XmlError ("Typed value is invalid.", ex);
1098                         }
1099                 }
1100
1101                 public virtual DateTime ReadElementContentAsDateTime (string localName, string namespaceURI)
1102                 {
1103                         try {
1104                                 return XQueryConvert.StringToDateTime (ReadElementContentAsString (localName, namespaceURI));
1105                         } catch (FormatException ex) {
1106                                 throw XmlError ("Typed value is invalid.", ex);
1107                         }
1108                 }
1109
1110                 public virtual decimal ReadElementContentAsDecimal (string localName, string namespaceURI)
1111                 {
1112                         try {
1113                                 return XQueryConvert.StringToDecimal (ReadElementContentAsString (localName, namespaceURI));
1114                         } catch (FormatException ex) {
1115                                 throw XmlError ("Typed value is invalid.", ex);
1116                         }
1117                 }
1118
1119                 public virtual double ReadElementContentAsDouble (string localName, string namespaceURI)
1120                 {
1121                         try {
1122                                 return XQueryConvert.StringToDouble (ReadElementContentAsString (localName, namespaceURI));
1123                         } catch (FormatException ex) {
1124                                 throw XmlError ("Typed value is invalid.", ex);
1125                         }
1126                 }
1127
1128                 public virtual float ReadElementContentAsFloat (string localName, string namespaceURI)
1129                 {
1130                         try {
1131                                 return XQueryConvert.StringToFloat (ReadElementContentAsString (localName, namespaceURI));
1132                         } catch (FormatException ex) {
1133                                 throw XmlError ("Typed value is invalid.", ex);
1134                         }
1135                 }
1136
1137                 public virtual int ReadElementContentAsInt (string localName, string namespaceURI)
1138                 {
1139                         try {
1140                                 return XQueryConvert.StringToInt (ReadElementContentAsString (localName, namespaceURI));
1141                         } catch (FormatException ex) {
1142                                 throw XmlError ("Typed value is invalid.", ex);
1143                         }
1144                 }
1145
1146                 public virtual long ReadElementContentAsLong (string localName, string namespaceURI)
1147                 {
1148                         try {
1149                                 return XQueryConvert.StringToInteger (ReadElementContentAsString (localName, namespaceURI));
1150                         } catch (FormatException ex) {
1151                                 throw XmlError ("Typed value is invalid.", ex);
1152                         }
1153                 }
1154
1155                 public virtual string ReadElementContentAsString (string localName, string namespaceURI)
1156                 {
1157                         bool isEmpty = IsEmptyElement;
1158                         // unlike ReadStartElement() it rejects non-content nodes (this check is done before MoveToContent())
1159                         if (NodeType != XmlNodeType.Element)
1160                                 throw new InvalidOperationException (String.Format ("'{0}' is an element node.", NodeType));
1161                         ReadStartElement (localName, namespaceURI);
1162                         if (isEmpty)
1163                                 return String.Empty;
1164                         string s = ReadContentString (false);
1165                         ReadEndElement ();
1166                         return s;
1167                 }
1168
1169                 public virtual bool ReadContentAsBoolean ()
1170                 {
1171                         try {
1172                                 return XQueryConvert.StringToBoolean (ReadContentString ());
1173                         } catch (FormatException ex) {
1174                                 throw XmlError ("Typed value is invalid.", ex);
1175                         }
1176                 }
1177
1178                 public virtual DateTime ReadContentAsDateTime ()
1179                 {
1180                         try {
1181                                 return XQueryConvert.StringToDateTime (ReadContentString ());
1182                         } catch (FormatException ex) {
1183                                 throw XmlError ("Typed value is invalid.", ex);
1184                         }
1185                 }
1186
1187                 public virtual decimal ReadContentAsDecimal ()
1188                 {
1189                         try {
1190                                 return XQueryConvert.StringToDecimal (ReadContentString ());
1191                         } catch (FormatException ex) {
1192                                 throw XmlError ("Typed value is invalid.", ex);
1193                         }
1194                 }
1195
1196                 public virtual double ReadContentAsDouble ()
1197                 {
1198                         try {
1199                                 return XQueryConvert.StringToDouble (ReadContentString ());
1200                         } catch (FormatException ex) {
1201                                 throw XmlError ("Typed value is invalid.", ex);
1202                         }
1203                 }
1204
1205                 public virtual float ReadContentAsFloat ()
1206                 {
1207                         try {
1208                                 return XQueryConvert.StringToFloat (ReadContentString ());
1209                         } catch (FormatException ex) {
1210                                 throw XmlError ("Typed value is invalid.", ex);
1211                         }
1212                 }
1213
1214                 public virtual int ReadContentAsInt ()
1215                 {
1216                         try {
1217                                 return XQueryConvert.StringToInt (ReadContentString ());
1218                         } catch (FormatException ex) {
1219                                 throw XmlError ("Typed value is invalid.", ex);
1220                         }
1221                 }
1222
1223                 public virtual long ReadContentAsLong ()
1224                 {
1225                         try {
1226                                 return XQueryConvert.StringToInteger (ReadContentString ());
1227                         } catch (FormatException ex) {
1228                                 throw XmlError ("Typed value is invalid.", ex);
1229                         }
1230                 }
1231
1232                 public virtual string ReadContentAsString ()
1233                 {
1234                         return ReadContentString ();
1235                 }
1236
1237                 public virtual int ReadContentAsBase64 (
1238                         byte [] buffer, int offset, int length)
1239                 {
1240                         CheckSupport ();
1241                         return binary.ReadContentAsBase64 (
1242                                 buffer, offset, length);
1243                 }
1244
1245                 public virtual int ReadContentAsBinHex (
1246                         byte [] buffer, int offset, int length)
1247                 {
1248                         CheckSupport ();
1249                         return binary.ReadContentAsBinHex (
1250                                 buffer, offset, length);
1251                 }
1252
1253                 public virtual int ReadElementContentAsBase64 (
1254                         byte [] buffer, int offset, int length)
1255                 {
1256                         CheckSupport ();
1257                         return binary.ReadElementContentAsBase64 (
1258                                 buffer, offset, length);
1259                 }
1260
1261                 public virtual int ReadElementContentAsBinHex (
1262                         byte [] buffer, int offset, int length)
1263                 {
1264                         CheckSupport ();
1265                         return binary.ReadElementContentAsBinHex (
1266                                 buffer, offset, length);
1267                 }
1268
1269                 private void CheckSupport ()
1270                 {
1271                         // Default implementation expects both.
1272                         if (!CanReadBinaryContent || !CanReadValueChunk)
1273                                 throw new NotSupportedException ();
1274                         if (binary == null)
1275                                 binary = new XmlReaderBinarySupport (this);
1276                 }
1277                 
1278 #endif
1279
1280 #if NET_2_0
1281                 public virtual int ReadValueChunk (
1282                         char [] buffer, int offset, int length)
1283 #else
1284                 internal virtual int ReadValueChunk (
1285                         char [] buffer, int offset, int length)
1286 #endif
1287                 {
1288                         if (!CanReadValueChunk)
1289                                 throw new NotSupportedException ();
1290                         if (binary == null)
1291                                 binary = new XmlReaderBinarySupport (this);
1292                         return binary.ReadValueChunk (buffer, offset, length);
1293                 }
1294
1295                 public abstract void ResolveEntity ();
1296
1297                 public virtual void Skip ()
1298                 {
1299                         if (ReadState != ReadState.Interactive)
1300                                 return;
1301
1302                         MoveToElement ();
1303                         if (NodeType != XmlNodeType.Element || IsEmptyElement) {
1304                                 Read ();
1305                                 return;
1306                         }
1307                                 
1308                         int depth = Depth;
1309                         while (Read () && depth < Depth)
1310                                 ;
1311                         if (NodeType == XmlNodeType.EndElement)
1312                                 Read ();
1313                 }
1314
1315                 private XmlException XmlError (string message)
1316                 {
1317                         return new XmlException (this as IXmlLineInfo, BaseURI, message);
1318                 }
1319 #if NET_2_0
1320                 private XmlException XmlError (string message, Exception innerException)
1321                 {
1322                         return new XmlException (this as IXmlLineInfo, BaseURI, message);
1323                 }
1324 #endif
1325                 #endregion
1326         }
1327 }