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