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