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