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