2007-08-03 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 ( settings.IgnoreComments ||
395                              settings.IgnoreProcessingInstructions ||
396                              settings.IgnoreWhitespace)
397                                 return new XmlFilterReader (reader, settings);
398                         else {
399                                 reader.settings = settings;
400                                 return reader;
401                         }
402                 }
403
404                 private static XmlReader CreateValidatingXmlReader (XmlReader reader, XmlReaderSettings settings)
405                 {
406 #if NET_2_1
407                         return reader;
408 #else
409                         XmlValidatingReader xvr = null;
410                         switch (settings.ValidationType) {
411                         // Auto and XDR are obsoleted in 2.0 and therefore ignored.
412                         default:
413                                 return reader;
414                         case ValidationType.DTD:
415                                 xvr = new XmlValidatingReader (reader);
416                                 xvr.XmlResolver = settings.XmlResolver;
417                                 xvr.ValidationType = ValidationType.DTD;
418                                 break;
419                         case ValidationType.Schema:
420                                 return new XmlSchemaValidatingReader (reader, settings);
421                         }
422
423                         // Actually I don't think they are treated in DTD validation though...
424                         if ((settings.ValidationFlags & XmlSchemaValidationFlags.ProcessIdentityConstraints) == 0)
425                                 throw new NotImplementedException ();
426                         //if ((settings.ValidationFlags & XmlSchemaValidationFlags.ProcessInlineSchema) != 0)
427                         //      throw new NotImplementedException ();
428                         //if ((settings.ValidationFlags & XmlSchemaValidationFlags.ProcessSchemaLocation) != 0)
429                         //      throw new NotImplementedException ();
430                         //if ((settings.ValidationFlags & XmlSchemaValidationFlags.ReportValidationWarnings) == 0)
431                         //      throw new NotImplementedException ();
432
433                         return xvr != null ? xvr : reader;
434 #endif
435                 }
436
437                 void IDisposable.Dispose ()
438                 {
439                         Dispose (false);
440                 }
441
442                 protected virtual void Dispose (bool disposing)
443                 {
444                         if (ReadState != ReadState.Closed)
445                                 Close ();
446                 }
447 #endif
448
449                 public abstract string GetAttribute (int i);
450
451                 public abstract string GetAttribute (string name);
452
453                 public abstract string GetAttribute (
454                         string localName,
455                         string namespaceName);
456
457                 public static bool IsName (string s)
458                 {
459                         return s != null && XmlChar.IsName (s);
460                 }
461
462                 public static bool IsNameToken (string s)
463                 {
464                         return s != null && XmlChar.IsNmToken (s);
465                 }
466
467                 public virtual bool IsStartElement ()
468                 {
469                         return (MoveToContent () == XmlNodeType.Element);
470                 }
471
472                 public virtual bool IsStartElement (string name)
473                 {
474                         if (!IsStartElement ())
475                                 return false;
476
477                         return (Name == name);
478                 }
479
480                 public virtual bool IsStartElement (string localName, string namespaceName)
481                 {
482                         if (!IsStartElement ())
483                                 return false;
484
485                         return (LocalName == localName && NamespaceURI == namespaceName);
486                 }
487
488                 public abstract string LookupNamespace (string prefix);
489
490 #if NET_2_0
491                 public virtual void MoveToAttribute (int i)
492                 {
493                         if (i >= AttributeCount)
494                                 throw new ArgumentOutOfRangeException ();
495                         MoveToFirstAttribute ();
496                         for (int a = 0; a < i; a++)
497                                 MoveToNextAttribute ();
498                 }
499 #else
500                 public abstract void MoveToAttribute (int i);
501 #endif
502
503                 public abstract bool MoveToAttribute (string name);
504
505                 public abstract bool MoveToAttribute (
506                         string localName,
507                         string namespaceName);
508
509                 private bool IsContent (XmlNodeType nodeType)
510                 {
511                         /* MS doc says:
512                          * (non-white space text, CDATA, Element, EndElement, EntityReference, or EndEntity)
513                          */
514                         switch (nodeType) {
515                         case XmlNodeType.Text:
516                                 return true;
517                         case XmlNodeType.CDATA:
518                                 return true;
519                         case XmlNodeType.Element:
520                                 return true;
521                         case XmlNodeType.EndElement:
522                                 return true;
523                         case XmlNodeType.EntityReference:
524                                 return true;
525                         case XmlNodeType.EndEntity:
526                                 return true;
527                         }
528
529                         return false;
530                 }
531
532                 public virtual XmlNodeType MoveToContent ()
533                 {
534                         if (NodeType == XmlNodeType.Attribute)
535                                 MoveToElement ();
536
537                         do {
538                                 if (IsContent (NodeType))
539                                         return NodeType;
540                                 Read ();
541                         } while (!EOF);
542                         return XmlNodeType.None;
543                 }
544
545                 public abstract bool MoveToElement ();
546
547                 public abstract bool MoveToFirstAttribute ();
548
549                 public abstract bool MoveToNextAttribute ();
550
551                 public abstract bool Read ();
552
553                 public abstract bool ReadAttributeValue ();
554
555                 public virtual string ReadElementString ()
556                 {
557                         if (MoveToContent () != XmlNodeType.Element) {
558                                 string error = String.Format ("'{0}' is an invalid node type.",
559                                                               NodeType.ToString ());
560                                 throw XmlError (error);
561                         }
562
563                         string result = String.Empty;
564                         if (!IsEmptyElement) {
565                                 Read ();
566                                 result = ReadString ();
567                                 if (NodeType != XmlNodeType.EndElement) {
568                                         string error = String.Format ("'{0}' is an invalid node type.",
569                                                                       NodeType.ToString ());
570                                         throw XmlError (error);
571                                 }
572                         }
573
574                         Read ();
575                         return result;
576                 }
577
578                 public virtual string ReadElementString (string name)
579                 {
580                         if (MoveToContent () != XmlNodeType.Element) {
581                                 string error = String.Format ("'{0}' is an invalid node type.",
582                                                               NodeType.ToString ());
583                                 throw XmlError (error);
584                         }
585
586                         if (name != Name) {
587                                 string error = String.Format ("The {0} tag from namespace {1} is expected.",
588                                                               Name, NamespaceURI);
589                                 throw XmlError (error);
590                         }
591
592                         string result = String.Empty;
593                         if (!IsEmptyElement) {
594                                 Read ();
595                                 result = ReadString ();
596                                 if (NodeType != XmlNodeType.EndElement) {
597                                         string error = String.Format ("'{0}' is an invalid node type.",
598                                                                       NodeType.ToString ());
599                                         throw XmlError (error);
600                                 }
601                         }
602
603                         Read ();
604                         return result;
605                 }
606
607                 public virtual string ReadElementString (string localName, string namespaceName)
608                 {
609                         if (MoveToContent () != XmlNodeType.Element) {
610                                 string error = String.Format ("'{0}' is an invalid node type.",
611                                                               NodeType.ToString ());
612                                 throw XmlError (error);
613                         }
614
615                         if (localName != LocalName || NamespaceURI != namespaceName) {
616                                 string error = String.Format ("The {0} tag from namespace {1} is expected.",
617                                                               LocalName, NamespaceURI);
618                                 throw XmlError (error);
619                         }
620
621                         string result = String.Empty;
622                         if (!IsEmptyElement) {
623                                 Read ();
624                                 result = ReadString ();
625                                 if (NodeType != XmlNodeType.EndElement) {
626                                         string error = String.Format ("'{0}' is an invalid node type.",
627                                                                       NodeType.ToString ());
628                                         throw XmlError (error);
629                                 }
630                         }
631
632                         Read ();
633                         return result;
634                 }
635
636                 public virtual void ReadEndElement ()
637                 {
638                         if (MoveToContent () != XmlNodeType.EndElement) {
639                                 string error = String.Format ("'{0}' is an invalid node type.",
640                                                               NodeType.ToString ());
641                                 throw XmlError (error);
642                         }
643
644                         Read ();
645                 }
646
647                 public virtual string ReadInnerXml ()
648                 {
649                         if (ReadState != ReadState.Interactive || NodeType == XmlNodeType.EndElement)
650                                 return String.Empty;
651
652                         if (IsEmptyElement) {
653                                 Read ();
654                                 return String.Empty;
655                         }
656                         StringWriter sw = new StringWriter ();
657                         XmlTextWriter xtw = new XmlTextWriter (sw);
658                         if (NodeType == XmlNodeType.Element) {
659                                 int startDepth = Depth;
660                                 Read ();
661                                 while (startDepth < Depth) {
662                                         if (ReadState != ReadState.Interactive)
663                                                 throw XmlError ("Unexpected end of the XML reader.");
664                                         xtw.WriteNode (this, false);
665                                 }
666                                 // reader is now end element, then proceed once more.
667                                 Read ();
668                         }
669                         else
670                                 xtw.WriteNode (this, false);
671
672                         return sw.ToString ();
673                 }
674
675                 public virtual string ReadOuterXml ()
676                 {
677                         if (ReadState != ReadState.Interactive || NodeType == XmlNodeType.EndElement)
678                                 return String.Empty;
679
680                         switch (NodeType) {
681                         case XmlNodeType.Element:
682                         case XmlNodeType.Attribute:
683                                 StringWriter sw = new StringWriter ();
684                                 XmlTextWriter xtw = new XmlTextWriter (sw);
685                                 xtw.WriteNode (this, false);
686                                 return sw.ToString ();
687                         default:
688                                 Skip ();
689                                 return String.Empty;
690                         }
691                 }
692
693                 public virtual void ReadStartElement ()
694                 {
695                         if (MoveToContent () != XmlNodeType.Element) {
696                                 string error = String.Format ("'{0}' is an invalid node type.",
697                                                               NodeType.ToString ());
698                                 throw XmlError (error);
699                         }
700
701                         Read ();
702                 }
703
704                 public virtual void ReadStartElement (string name)
705                 {
706                         if (MoveToContent () != XmlNodeType.Element) {
707                                 string error = String.Format ("'{0}' is an invalid node type.",
708                                                               NodeType.ToString ());
709                                 throw XmlError (error);
710                         }
711
712                         if (name != Name) {
713                                 string error = String.Format ("The {0} tag from namespace {1} is expected.",
714                                                               Name, NamespaceURI);
715                                 throw XmlError (error);
716                         }
717
718                         Read ();
719                 }
720
721                 public virtual void ReadStartElement (string localName, string namespaceName)
722                 {
723                         if (MoveToContent () != XmlNodeType.Element) {
724                                 string error = String.Format ("'{0}' is an invalid node type.",
725                                                               NodeType.ToString ());
726                                 throw XmlError (error);
727                         }
728
729                         if (localName != LocalName || NamespaceURI != namespaceName) {
730                                 string error = String.Format ("Expecting {0} tag from namespace {1}, got {2} and {3} instead",
731                                                               localName, namespaceName,
732                                                               LocalName, NamespaceURI);
733                                 throw XmlError (error);
734                         }
735
736                         Read ();
737                 }
738
739                 public virtual string ReadString ()
740                 {
741                         if (readStringBuffer == null)
742                                 readStringBuffer = new StringBuilder ();
743                         readStringBuffer.Length = 0;
744
745                         MoveToElement ();
746
747                         switch (NodeType) {
748                         default:
749                                 return String.Empty;
750                         case XmlNodeType.Element:
751                                 if (IsEmptyElement)
752                                         return String.Empty;
753                                 do {
754                                         Read ();
755                                         switch (NodeType) {
756                                         case XmlNodeType.Text:
757                                         case XmlNodeType.CDATA:
758                                         case XmlNodeType.Whitespace:
759                                         case XmlNodeType.SignificantWhitespace:
760                                                 readStringBuffer.Append (Value);
761                                                 continue;
762                                         }
763                                         break;
764                                 } while (true);
765                                 break;
766                         case XmlNodeType.Text:
767                         case XmlNodeType.CDATA:
768                         case XmlNodeType.Whitespace:
769                         case XmlNodeType.SignificantWhitespace:
770                                 do {
771                                         switch (NodeType) {
772                                         case XmlNodeType.Text:
773                                         case XmlNodeType.CDATA:
774                                         case XmlNodeType.Whitespace:
775                                         case XmlNodeType.SignificantWhitespace:
776                                                 readStringBuffer.Append (Value);
777                                                 Read ();
778                                                 continue;
779                                         }
780                                         break;
781                                 } while (true);
782                                 break;
783                         }
784                         string ret = readStringBuffer.ToString ();
785                         readStringBuffer.Length = 0;
786                         return ret;
787                 }
788
789 #if NET_2_0
790                 public virtual Type ValueType {
791                         get { return typeof (string); }
792                 }
793
794                 public virtual bool ReadToDescendant (string name)
795                 {
796                         if (ReadState == ReadState.Initial) {
797                                 MoveToContent ();
798                                 if (IsStartElement (name))
799                                         return true;
800                         }
801                         if (NodeType != XmlNodeType.Element || IsEmptyElement)
802                                 return false;
803                         int depth = Depth;
804                         for (Read (); depth < Depth; Read ())
805                                 if (NodeType == XmlNodeType.Element && name == Name)
806                                         return true;
807                         return false;
808                 }
809
810                 public virtual bool ReadToDescendant (string localName, string namespaceURI)
811                 {
812                         if (ReadState == ReadState.Initial) {
813                                 MoveToContent ();
814                                 if (IsStartElement (localName, namespaceURI))
815                                         return true;
816                         }
817                         if (NodeType != XmlNodeType.Element || IsEmptyElement)
818                                 return false;
819                         int depth = Depth;
820                         for (Read (); depth < Depth; Read ())
821                                 if (NodeType == XmlNodeType.Element && localName == LocalName && namespaceURI == NamespaceURI)
822                                         return true;
823                         return false;
824                 }
825
826                 public virtual bool ReadToFollowing (string name)
827                 {
828                         while (Read ())
829                                 if (NodeType == XmlNodeType.Element && name == Name)
830                                         return true;
831                         return false;
832                 }
833
834                 public virtual bool ReadToFollowing (string localName, string namespaceURI)
835                 {
836                         while (Read ())
837                                 if (NodeType == XmlNodeType.Element && localName == Name && namespaceURI == NamespaceURI)
838                                         return true;
839                         return false;
840                 }
841
842                 public virtual bool ReadToNextSibling (string name)
843                 {
844                         if (ReadState != ReadState.Interactive)
845                                 return false;
846                         int depth = Depth;
847                         for (Skip (); depth <= Depth; Skip ())
848                                 if (NodeType == XmlNodeType.Element && name == Name)
849                                         return true;
850                         return false;
851                 }
852
853                 public virtual bool ReadToNextSibling (string localName, string namespaceURI)
854                 {
855                         if (ReadState != ReadState.Interactive)
856                                 return false;
857                         int depth = Depth;
858                         for (Skip (); depth <= Depth; Skip ())
859                                 if (NodeType == XmlNodeType.Element && localName == LocalName && namespaceURI == NamespaceURI)
860                                         return true;
861                         return false;
862                 }
863
864                 public virtual XmlReader ReadSubtree ()
865                 {
866                         return new SubtreeXmlReader (this);
867                 }
868
869                 private string ReadContentString ()
870                 {
871                         if (NodeType == XmlNodeType.Attribute)
872                                 return Value;
873                         return ReadContentString (true);
874                 }
875
876                 private string ReadContentString (bool isText)
877                 {
878                         if (isText) {
879                                 switch (NodeType) {
880                                 case XmlNodeType.Text:
881                                 case XmlNodeType.SignificantWhitespace:
882                                 case XmlNodeType.Whitespace:
883                                 case XmlNodeType.CDATA:
884                                         break;
885                                 default:
886                                         throw new InvalidOperationException (String.Format ("Node type {0} is not supported in this operation.{1}", NodeType, GetLocation ()));
887                                 }
888                         }
889
890                         string value = String.Empty;
891                         do {
892                                 switch (NodeType) {
893                                 case XmlNodeType.Element:
894                                         if (isText)
895                                                 return value;
896                                         throw XmlError ("Child element is not expected in this operation.");
897                                 case XmlNodeType.EndElement:
898                                         return value;
899                                 case XmlNodeType.Text:
900                                 case XmlNodeType.CDATA:
901                                 case XmlNodeType.SignificantWhitespace:
902                                 case XmlNodeType.Whitespace:
903                                         value += Value;
904                                         break;
905                                 }
906                         } while (Read ());
907                         throw XmlError ("Unexpected end of document.");
908                 }
909
910                 string GetLocation ()
911                 {
912                         IXmlLineInfo li = this as IXmlLineInfo;
913                         return li != null && li.HasLineInfo () ?
914                                 String.Format (" {0} (line {1}, column {2})", BaseURI, li.LineNumber, li.LinePosition) : String.Empty;
915                 }
916
917                 [MonoTODO]
918                 public virtual object ReadElementContentAsObject ()
919                 {
920                         return ReadElementContentAs (ValueType, null);
921                 }
922
923                 [MonoTODO]
924                 public virtual object ReadElementContentAsObject (string localName, string namespaceURI)
925                 {
926                         return ReadElementContentAs (ValueType, null, localName, namespaceURI);
927                 }
928
929                 [MonoTODO]
930                 public virtual object ReadContentAsObject ()
931                 {
932                         return ReadContentAs (ValueType, null);
933                 }
934
935                 public virtual object ReadElementContentAs (Type type, IXmlNamespaceResolver resolver)
936                 {
937                         bool isEmpty = IsEmptyElement;
938                         ReadStartElement ();
939                         object obj = ValueAs (isEmpty ? String.Empty : ReadContentString (false), type, resolver);
940                         if (!isEmpty)
941                                 ReadEndElement ();
942                         return obj;
943                 }
944
945                 public virtual object ReadElementContentAs (Type type, IXmlNamespaceResolver resolver, string localName, string namespaceURI)
946                 {
947                         ReadStartElement (localName, namespaceURI);
948                         object obj = ReadContentAs (type, resolver);
949                         ReadEndElement ();
950                         return obj;
951                 }
952
953                 public virtual object ReadContentAs (Type type, IXmlNamespaceResolver resolver)
954                 {
955                         return ValueAs (ReadContentString (), type, resolver);
956                 }
957
958                 private object ValueAs (string text, Type type, IXmlNamespaceResolver resolver)
959                 {
960                         try {
961                                 if (type == typeof (object))
962                                         return text;
963                                 if (type == typeof (XmlQualifiedName)) {
964                                         if (resolver != null)
965                                                 return XmlQualifiedName.Parse (text, resolver);
966                                         else
967                                                 return XmlQualifiedName.Parse (text, this);
968                                 }
969
970                                 switch (Type.GetTypeCode (type)) {
971                                 case TypeCode.Boolean:
972                                         return XQueryConvert.StringToBoolean (text);
973                                 case TypeCode.DateTime:
974                                         return XQueryConvert.StringToDateTime (text);
975                                 case TypeCode.Decimal:
976                                         return XQueryConvert.StringToDecimal (text);
977                                 case TypeCode.Double:
978                                         return XQueryConvert.StringToDouble (text);
979                                 case TypeCode.Int32:
980                                         return XQueryConvert.StringToInt (text);
981                                 case TypeCode.Int64:
982                                         return XQueryConvert.StringToInteger (text);
983                                 case TypeCode.Single:
984                                         return XQueryConvert.StringToFloat (text);
985                                 case TypeCode.String:
986                                         return text;
987                                 }
988                         } catch (Exception ex) {
989                                 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);
990                         }
991                         throw new ArgumentException (String.Format ("Specified type '{0}' is not supported.", type));
992                 }
993
994                 public virtual bool ReadElementContentAsBoolean ()
995                 {
996                         try {
997                                 return XQueryConvert.StringToBoolean (ReadElementContentAsString ());
998                         } catch (FormatException ex) {
999                                 throw XmlError ("Typed value is invalid.", ex);
1000                         }
1001                 }
1002
1003                 public virtual DateTime ReadElementContentAsDateTime ()
1004                 {
1005                         try {
1006                                 return XQueryConvert.StringToDateTime (ReadElementContentAsString ());
1007                         } catch (FormatException ex) {
1008                                 throw XmlError ("Typed value is invalid.", ex);
1009                         }
1010                 }
1011
1012                 public virtual decimal ReadElementContentAsDecimal ()
1013                 {
1014                         try {
1015                                 return XQueryConvert.StringToDecimal (ReadElementContentAsString ());
1016                         } catch (FormatException ex) {
1017                                 throw XmlError ("Typed value is invalid.", ex);
1018                         }
1019                 }
1020
1021                 public virtual double ReadElementContentAsDouble ()
1022                 {
1023                         try {
1024                                 return XQueryConvert.StringToDouble (ReadElementContentAsString ());
1025                         } catch (FormatException ex) {
1026                                 throw XmlError ("Typed value is invalid.", ex);
1027                         }
1028                 }
1029
1030                 public virtual float ReadElementContentAsFloat ()
1031                 {
1032                         try {
1033                                 return XQueryConvert.StringToFloat (ReadElementContentAsString ());
1034                         } catch (FormatException ex) {
1035                                 throw XmlError ("Typed value is invalid.", ex);
1036                         }
1037                 }
1038
1039                 public virtual int ReadElementContentAsInt ()
1040                 {
1041                         try {
1042                                 return XQueryConvert.StringToInt (ReadElementContentAsString ());
1043                         } catch (FormatException ex) {
1044                                 throw XmlError ("Typed value is invalid.", ex);
1045                         }
1046                 }
1047
1048                 public virtual long ReadElementContentAsLong ()
1049                 {
1050                         try {
1051                                 return XQueryConvert.StringToInteger (ReadElementContentAsString ());
1052                         } catch (FormatException ex) {
1053                                 throw XmlError ("Typed value is invalid.", ex);
1054                         }
1055                 }
1056
1057                 public virtual string ReadElementContentAsString ()
1058                 {
1059                         bool isEmpty = IsEmptyElement;
1060                         ReadStartElement ();
1061                         if (isEmpty)
1062                                 return String.Empty;
1063                         string s = ReadContentString (false);
1064                         ReadEndElement ();
1065                         return s;
1066                 }
1067
1068                 public virtual bool ReadElementContentAsBoolean (string localName, string namespaceURI)
1069                 {
1070                         try {
1071                                 return XQueryConvert.StringToBoolean (ReadElementContentAsString (localName, namespaceURI));
1072                         } catch (FormatException ex) {
1073                                 throw XmlError ("Typed value is invalid.", ex);
1074                         }
1075                 }
1076
1077                 public virtual DateTime ReadElementContentAsDateTime (string localName, string namespaceURI)
1078                 {
1079                         try {
1080                                 return XQueryConvert.StringToDateTime (ReadElementContentAsString (localName, namespaceURI));
1081                         } catch (FormatException ex) {
1082                                 throw XmlError ("Typed value is invalid.", ex);
1083                         }
1084                 }
1085
1086                 public virtual decimal ReadElementContentAsDecimal (string localName, string namespaceURI)
1087                 {
1088                         try {
1089                                 return XQueryConvert.StringToDecimal (ReadElementContentAsString (localName, namespaceURI));
1090                         } catch (FormatException ex) {
1091                                 throw XmlError ("Typed value is invalid.", ex);
1092                         }
1093                 }
1094
1095                 public virtual double ReadElementContentAsDouble (string localName, string namespaceURI)
1096                 {
1097                         try {
1098                                 return XQueryConvert.StringToDouble (ReadElementContentAsString (localName, namespaceURI));
1099                         } catch (FormatException ex) {
1100                                 throw XmlError ("Typed value is invalid.", ex);
1101                         }
1102                 }
1103
1104                 public virtual float ReadElementContentAsFloat (string localName, string namespaceURI)
1105                 {
1106                         try {
1107                                 return XQueryConvert.StringToFloat (ReadElementContentAsString (localName, namespaceURI));
1108                         } catch (FormatException ex) {
1109                                 throw XmlError ("Typed value is invalid.", ex);
1110                         }
1111                 }
1112
1113                 public virtual int ReadElementContentAsInt (string localName, string namespaceURI)
1114                 {
1115                         try {
1116                                 return XQueryConvert.StringToInt (ReadElementContentAsString (localName, namespaceURI));
1117                         } catch (FormatException ex) {
1118                                 throw XmlError ("Typed value is invalid.", ex);
1119                         }
1120                 }
1121
1122                 public virtual long ReadElementContentAsLong (string localName, string namespaceURI)
1123                 {
1124                         try {
1125                                 return XQueryConvert.StringToInteger (ReadElementContentAsString (localName, namespaceURI));
1126                         } catch (FormatException ex) {
1127                                 throw XmlError ("Typed value is invalid.", ex);
1128                         }
1129                 }
1130
1131                 public virtual string ReadElementContentAsString (string localName, string namespaceURI)
1132                 {
1133                         bool isEmpty = IsEmptyElement;
1134                         ReadStartElement (localName, namespaceURI);
1135                         if (isEmpty)
1136                                 return String.Empty;
1137                         string s = ReadContentString (false);
1138                         ReadEndElement ();
1139                         return s;
1140                 }
1141
1142                 public virtual bool ReadContentAsBoolean ()
1143                 {
1144                         try {
1145                                 return XQueryConvert.StringToBoolean (ReadContentString ());
1146                         } catch (FormatException ex) {
1147                                 throw XmlError ("Typed value is invalid.", ex);
1148                         }
1149                 }
1150
1151                 public virtual DateTime ReadContentAsDateTime ()
1152                 {
1153                         try {
1154                                 return XQueryConvert.StringToDateTime (ReadContentString ());
1155                         } catch (FormatException ex) {
1156                                 throw XmlError ("Typed value is invalid.", ex);
1157                         }
1158                 }
1159
1160                 public virtual decimal ReadContentAsDecimal ()
1161                 {
1162                         try {
1163                                 return XQueryConvert.StringToDecimal (ReadContentString ());
1164                         } catch (FormatException ex) {
1165                                 throw XmlError ("Typed value is invalid.", ex);
1166                         }
1167                 }
1168
1169                 public virtual double ReadContentAsDouble ()
1170                 {
1171                         try {
1172                                 return XQueryConvert.StringToDouble (ReadContentString ());
1173                         } catch (FormatException ex) {
1174                                 throw XmlError ("Typed value is invalid.", ex);
1175                         }
1176                 }
1177
1178                 public virtual float ReadContentAsFloat ()
1179                 {
1180                         try {
1181                                 return XQueryConvert.StringToFloat (ReadContentString ());
1182                         } catch (FormatException ex) {
1183                                 throw XmlError ("Typed value is invalid.", ex);
1184                         }
1185                 }
1186
1187                 public virtual int ReadContentAsInt ()
1188                 {
1189                         try {
1190                                 return XQueryConvert.StringToInt (ReadContentString ());
1191                         } catch (FormatException ex) {
1192                                 throw XmlError ("Typed value is invalid.", ex);
1193                         }
1194                 }
1195
1196                 public virtual long ReadContentAsLong ()
1197                 {
1198                         try {
1199                                 return XQueryConvert.StringToInteger (ReadContentString ());
1200                         } catch (FormatException ex) {
1201                                 throw XmlError ("Typed value is invalid.", ex);
1202                         }
1203                 }
1204
1205                 public virtual string ReadContentAsString ()
1206                 {
1207                         return ReadContentString ();
1208                 }
1209
1210                 public virtual int ReadContentAsBase64 (
1211                         byte [] buffer, int offset, int length)
1212                 {
1213                         CheckSupport ();
1214                         return binary.ReadContentAsBase64 (
1215                                 buffer, offset, length);
1216                 }
1217
1218                 public virtual int ReadContentAsBinHex (
1219                         byte [] buffer, int offset, int length)
1220                 {
1221                         CheckSupport ();
1222                         return binary.ReadContentAsBinHex (
1223                                 buffer, offset, length);
1224                 }
1225
1226                 public virtual int ReadElementContentAsBase64 (
1227                         byte [] buffer, int offset, int length)
1228                 {
1229                         CheckSupport ();
1230                         return binary.ReadElementContentAsBase64 (
1231                                 buffer, offset, length);
1232                 }
1233
1234                 public virtual int ReadElementContentAsBinHex (
1235                         byte [] buffer, int offset, int length)
1236                 {
1237                         CheckSupport ();
1238                         return binary.ReadElementContentAsBinHex (
1239                                 buffer, offset, length);
1240                 }
1241 #endif
1242
1243 #if NET_2_0
1244                 public virtual int ReadValueChunk (
1245                         char [] buffer, int offset, int length)
1246 #else
1247                 internal virtual int ReadValueChunk (
1248                         char [] buffer, int offset, int length)
1249 #endif
1250                 {
1251                         if (!CanReadValueChunk)
1252                                 throw new NotSupportedException ();
1253                         if (binary == null)
1254                                 binary = new XmlReaderBinarySupport (this);
1255                         return binary.ReadValueChunk (buffer, offset, length);
1256                 }
1257
1258                 private void CheckSupport ()
1259                 {
1260                         // Default implementation expects both.
1261                         if (!CanReadBinaryContent || !CanReadValueChunk)
1262                                 throw new NotSupportedException ();
1263                         if (binary == null)
1264                                 binary = new XmlReaderBinarySupport (this);
1265                 }
1266
1267                 public abstract void ResolveEntity ();
1268
1269                 public virtual void Skip ()
1270                 {
1271                         if (ReadState != ReadState.Interactive)
1272                                 return;
1273
1274                         MoveToElement ();
1275                         if (NodeType != XmlNodeType.Element || IsEmptyElement) {
1276                                 Read ();
1277                                 return;
1278                         }
1279                                 
1280                         int depth = Depth;
1281                         while (Read () && depth < Depth)
1282                                 ;
1283                         if (NodeType == XmlNodeType.EndElement)
1284                                 Read ();
1285                 }
1286
1287                 private XmlException XmlError (string message)
1288                 {
1289                         return new XmlException (this as IXmlLineInfo, BaseURI, message);
1290                 }
1291
1292                 private XmlException XmlError (string message, Exception innerException)
1293                 {
1294                         return new XmlException (this as IXmlLineInfo, BaseURI, message);
1295                 }
1296
1297                 #endregion
1298         }
1299 }