c23fedd3b8478b9e4c8f5ec349af9f77497accb9
[mono.git] / mcs / class / System.XML / System.Xml / XmlTextReader.cs
1 //
2 // System.Xml.XmlTextReader
3 //
4 // Author:
5 //   Jason Diamond (jason@injektilo.org)
6 //   Adam Treat (manyoso@yahoo.com)
7 //   Atsushi Enomoto  (ginga@kit.hi-ho.ne.jp)
8 //
9 // (C) 2001, 2002 Jason Diamond  http://injektilo.org/
10 //
11
12 // FIXME:
13 //
14 //   Some thought needs to be given to performance.
15 //
16 //   If current node is on an Attribute, Prefix might be null, and
17 //   in several fields which uses XmlReader, it should be considered.
18 //
19
20 using System;
21 using System.Collections;
22 using System.IO;
23 using System.Security.Policy;
24 using System.Text;
25 using System.Xml.Schema;
26 using Mono.Xml;
27
28 namespace System.Xml
29 {
30         public class XmlTextReader : XmlReader, IXmlLineInfo
31         {
32                 #region Constructors
33
34                 protected XmlTextReader ()
35                 {
36                 }
37
38                 public XmlTextReader (Stream input)
39                         : this (new XmlStreamReader (input))
40                 {
41                 }
42
43                 public XmlTextReader (string url)
44                         : this(url, new NameTable ())
45                 {
46                 }
47
48                 public XmlTextReader (TextReader input)
49                         : this (input, new NameTable ())
50                 {
51                 }
52
53                 protected XmlTextReader (XmlNameTable nt)
54                         : this (String.Empty, null, XmlNodeType.None, null)
55                 {
56                 }
57
58                 public XmlTextReader (Stream input, XmlNameTable nt)
59                         : this(new XmlStreamReader (input), nt)
60                 {
61                 }
62
63                 public XmlTextReader (string url, Stream input)
64                         : this (url, new XmlStreamReader (input))
65                 {
66                 }
67
68                 public XmlTextReader (string url, TextReader input)
69                         : this (url, input, new NameTable ())
70                 {
71                 }
72
73                 public XmlTextReader (string url, XmlNameTable nt)
74                 {
75                         Uri uri = resolver.ResolveUri (null, url);
76                         Stream s = resolver.GetEntity (uri, null, typeof (Stream)) as Stream;
77                         XmlParserContext ctx = new XmlParserContext (nt,
78                                 new XmlNamespaceManager (nt),
79                                 String.Empty,
80                                 XmlSpace.None);
81                         this.InitializeContext (uri.ToString(), ctx, new XmlStreamReader (s), XmlNodeType.Document);
82                 }
83
84                 public XmlTextReader (TextReader input, XmlNameTable nt)
85                         : this (String.Empty, input, nt)
86                 {
87                 }
88
89                 public XmlTextReader (Stream xmlFragment, XmlNodeType fragType, XmlParserContext context)
90                         : this (context != null ? context.BaseURI : String.Empty,
91                                 new XmlStreamReader (xmlFragment),
92                         fragType,
93                         context)
94                 {
95                 }
96
97                 public XmlTextReader (string url, Stream input, XmlNameTable nt)
98                         : this (url, new XmlStreamReader (input), nt)
99                 {
100                 }
101
102                 public XmlTextReader (string url, TextReader input, XmlNameTable nt)
103                         : this (url, input, XmlNodeType.Document, null)
104                 {
105                 }
106
107                 public XmlTextReader (string xmlFragment, XmlNodeType fragType, XmlParserContext context)
108                         : this (context != null ? context.BaseURI : String.Empty,
109                                 new StringReader (xmlFragment),
110                                 fragType,
111                                 context)
112                 {
113                 }
114
115                 XmlTextReader (string url, TextReader fragment, XmlNodeType fragType, XmlParserContext context)
116                 {
117                         InitializeContext (url, context, fragment, fragType);
118                 }
119
120                 #endregion
121
122                 #region Properties
123
124                 public override int AttributeCount
125                 {
126                         get { return attributeCount; }
127                 }
128
129                 public override string BaseURI
130                 {
131                         get { return parserContext.BaseURI; }
132                 }
133
134                 public override int Depth
135                 {
136                         get {
137                                 int nodeTypeMod = currentToken.NodeType == XmlNodeType.Element  ? 0 : -1;
138                                 if (currentAttributeValue >= 0)
139                                         return nodeTypeMod + elementDepth + 2; // inside attribute value.
140                                 else if (currentAttribute >= 0)
141                                         return nodeTypeMod + elementDepth + 1;
142                                 return elementDepth;
143                         }
144                 }
145
146                 public Encoding Encoding
147                 {
148                         get { return parserContext.Encoding; }
149                 }
150 #if NET_2_0
151                 [MonoTODO]
152                 public EntityHandling EntityHandling {
153                         get { throw new NotImplementedException (); }
154                 }
155 #endif
156
157                 public override bool EOF
158                 {
159                         get
160                         {
161                                 return
162                                         readState == ReadState.EndOfFile ||
163                                         readState == ReadState.Closed;
164                         }
165                 }
166
167 #if NET_2_0
168                 [MonoTODO]
169                 public override Evidence [] Evidences {
170                         get { return base.Evidences; }
171                 }
172 #endif
173
174                 public override bool HasValue {
175                         get { return cursorToken.Value != null; }
176                 }
177
178                 public override bool IsDefault {
179                         // XmlTextReader does not expand default attributes.
180                         get { return false; }
181                 }
182
183                 public override bool IsEmptyElement {
184                         get { return cursorToken.IsEmptyElement; }
185                 }
186
187                 public override string this [int i] {
188                         get { return GetAttribute (i); }
189                 }
190
191                 public override string this [string name] {
192                         get { return GetAttribute (name); }
193                 }
194
195                 public override string this [string localName, string namespaceName] {
196                         get { return GetAttribute (localName, namespaceName); }
197                 }
198
199                 public int LineNumber {
200                         get {
201                                 if (useProceedingLineInfo)
202                                         return line;
203                                 else
204                                         return cursorToken.LineNumber;
205                         }
206                 }
207
208                 public int LinePosition {
209                         get {
210                                 if (useProceedingLineInfo)
211                                         return column;
212                                 else
213                                         return cursorToken.LinePosition;
214                         }
215                 }
216
217                 public override string LocalName {
218                         get { return cursorToken.LocalName; }
219                 }
220
221                 public override string Name {
222                         get { return cursorToken.Name; }
223                 }
224
225                 public bool Namespaces {
226                         get { return namespaces; }
227                         set { 
228                                 if (readState != ReadState.Initial)
229                                         throw new InvalidOperationException ("Namespaces have to be set before reading.");
230                                 namespaces = value;
231                         }
232                 }
233
234                 public override string NamespaceURI {
235                         get { return cursorToken.NamespaceURI; }
236                 }
237
238                 public override XmlNameTable NameTable {
239                         get { return parserContext.NameTable; }
240                 }
241
242                 public override XmlNodeType NodeType {
243                         get { return cursorToken.NodeType; }
244                 }
245
246                 public bool Normalization {
247                         get { return normalization; }
248                         set { normalization = value; }
249                 }
250
251                 public override string Prefix {
252                         get { return cursorToken.Prefix; }
253                 }
254
255 #if NET_2_0
256
257                 [MonoTODO ("Not documented in .NET SDK")]
258                 public bool ProhibitDtd {
259                         get { return prohibitDtd; }
260                         set { prohibitDtd = value; }
261                 }
262 #endif
263
264                 public override char QuoteChar {
265                         get { return cursorToken.QuoteChar; }
266                 }
267
268                 public override ReadState ReadState {
269                         get { return readState; }
270                 }
271
272                 public override string Value {
273                         get { return cursorToken.Value != null ? cursorToken.Value : String.Empty; }
274                 }
275
276                 public WhitespaceHandling WhitespaceHandling {
277                         get { return whitespaceHandling; }
278                         set { whitespaceHandling = value; }
279                 }
280
281                 public override string XmlLang {
282                         get { return parserContext.XmlLang; }
283                 }
284
285                 public XmlResolver XmlResolver {
286                         set { resolver = value; }
287                 }
288
289                 public override XmlSpace XmlSpace {
290                         get { return parserContext.XmlSpace; }
291                 }
292
293                 #endregion
294
295                 #region Methods
296
297                 public override void Close ()
298                 {
299                         readState = ReadState.Closed;
300
301                         cursorToken.Clear ();
302                         currentToken.Clear ();
303                         attributeCount = 0;
304                         if (reader != null)
305                                 reader.Close ();
306                 }
307
308                 public override string GetAttribute (int i)
309                 {
310                         if (i >= attributeCount)
311                                 throw new ArgumentOutOfRangeException ("i is smaller than AttributeCount");
312                         else {
313                                 return attributeTokens [i].Value;
314                         }
315                 }
316
317                 // MS.NET 1.0 msdn says that this method returns String.Empty
318                 // for absent attribute, but in fact it returns null.
319                 // This description is corrected in MS.NET 1.1 msdn.
320                 public override string GetAttribute (string name)
321                 {
322                         for (int i = 0; i < attributeCount; i++)
323                                 if (attributeTokens [i].Name == name)
324                                         return attributeTokens [i].Value;
325                         return null;
326                 }
327
328                 private int GetIndexOfQualifiedAttribute (string localName, string namespaceURI)
329                 {
330                         for (int i = 0; i < attributeCount; i++) {
331                                 XmlAttributeTokenInfo ti = attributeTokens [i];
332                                 if (ti.LocalName == localName && ti.NamespaceURI == namespaceURI)
333                                         return i;
334                         }
335                         return -1;
336                 }
337
338                 internal XmlParserContext GetInternalParserContext ()
339                 {
340                         return parserContext;
341                 }
342
343                 public override string GetAttribute (string localName, string namespaceURI)
344                 {
345                         int idx = this.GetIndexOfQualifiedAttribute (localName, namespaceURI);
346                         if (idx < 0)
347                                 return null;
348                         return attributeTokens [idx].Value;
349                 }
350
351                 public TextReader GetRemainder ()
352                 {
353                         if (peekCharsIndex == peekCharsLength)
354                                 return reader;
355                         return new StringReader (new string (peekChars, peekCharsIndex, peekCharsLength - peekCharsIndex) + reader.ReadToEnd ());
356                 }
357
358                 bool IXmlLineInfo.HasLineInfo ()
359                 {
360                         return true;
361                 }
362
363                 public override string LookupNamespace (string prefix)
364                 {
365                         return LookupNamespace (prefix, false);
366                 }
367
368                 internal string LookupNamespace (string prefix, bool atomizedName)
369                 {
370                         return parserContext.NamespaceManager.LookupNamespace (prefix, atomizedName);
371                 }
372
373                 public override void MoveToAttribute (int i)
374                 {
375                         if (i >= attributeCount)
376                                 throw new ArgumentOutOfRangeException ("attribute index out of range.");
377
378                         currentAttribute = i;
379                         currentAttributeValue = -1;
380                         cursorToken = attributeTokens [i];
381                 }
382
383                 public override bool MoveToAttribute (string name)
384                 {
385                         for (int i = 0; i < attributeCount; i++) {
386                                 XmlAttributeTokenInfo ti = attributeTokens [i];
387                                 if (ti.Name == name) {
388                                         MoveToAttribute (i);
389                                         return true;
390                                 }
391                         }
392                         return false;
393                 }
394
395                 public override bool MoveToAttribute (string localName, string namespaceName)
396                 {
397                         int idx = GetIndexOfQualifiedAttribute (localName, namespaceName);
398                         if (idx < 0)
399                                 return false;
400                         MoveToAttribute (idx);
401                         return true;
402                 }
403
404                 public override bool MoveToElement ()
405                 {
406                         if (currentToken == null)       // for attribute .ctor()
407                                 return false;
408
409                         if (cursorToken == currentToken)
410                                 return false;
411
412                         if (currentAttribute >= 0) {
413                                 currentAttribute = -1;
414                                 currentAttributeValue = -1;
415                                 cursorToken = currentToken;
416                                 return true;
417                         }
418                         else
419                                 return false;
420                 }
421
422                 public override bool MoveToFirstAttribute ()
423                 {
424                         if (attributeCount == 0)
425                                 return false;
426                         MoveToElement ();
427                         return MoveToNextAttribute ();
428                 }
429
430                 public override bool MoveToNextAttribute ()
431                 {
432                         if (currentAttribute == 0 && attributeCount == 0)
433                                 return false;
434                         if (currentAttribute + 1 < attributeCount) {
435                                 currentAttribute++;
436                                 currentAttributeValue = -1;
437                                 cursorToken = attributeTokens [currentAttribute];
438                                 return true;
439                         }
440                         else
441                                 return false;
442                 }
443
444                 public override bool Read ()
445                 {
446                         if (startNodeType == XmlNodeType.Attribute) {
447                                 if (currentAttribute == 0)
448                                         return false;   // already read.
449                                 ClearAttributes ();
450                                 IncrementAttributeToken ();
451                                 ReadAttributeValueTokens ('"');
452                                 cursorToken = attributeTokens [0];
453                                 currentAttributeValue = -1;
454                                 readState = ReadState.Interactive;
455                                 return true;
456                         }
457
458                         bool more = false;
459                         readState = ReadState.Interactive;
460                         currentLinkedNodeLineNumber = line;
461                         currentLinkedNodeLinePosition = column;
462                         useProceedingLineInfo = true;
463
464                         cursorToken = currentToken;
465                         attributeCount = 0;
466                         currentAttribute = currentAttributeValue = -1;
467                         currentToken.Clear ();
468
469                         // It was moved from end of ReadStartTag ().
470                         if (depthUp) {
471                                 ++depth;
472                                 depthUp = false;
473                         }
474
475                         if (shouldSkipUntilEndTag) {
476                                 shouldSkipUntilEndTag = false;
477                                 return ReadUntilEndTag ();
478                         }
479
480                         base64CacheStartsAt = -1;
481
482                         more = ReadContent ();
483
484                         if (!more && startNodeType == XmlNodeType.Document && currentState != XmlNodeType.EndElement)
485                                 throw new XmlException ("Document element did not appear.");
486
487                         useProceedingLineInfo = false;
488                         return more;
489                 }
490
491                 public override bool ReadAttributeValue ()
492                 {
493                         if (readState == ReadState.Initial && startNodeType == XmlNodeType.Attribute) {
494                                 Read ();
495                         }
496
497                         if (currentAttribute < 0)
498                                 return false;
499                         XmlAttributeTokenInfo ti = attributeTokens [currentAttribute];
500                         if (currentAttributeValue < 0)
501                                 currentAttributeValue = ti.ValueTokenStartIndex - 1;
502
503                         if (currentAttributeValue < ti.ValueTokenEndIndex) {
504                                 currentAttributeValue++;
505                                 cursorToken = attributeValueTokens [currentAttributeValue];
506                                 return true;
507                         }
508                         else
509                                 return false;
510                 }
511
512                 public int ReadBase64 (byte [] buffer, int offset, int length)
513                 {
514                         if (offset < 0)
515                                 throw new ArgumentOutOfRangeException ("offset", offset, "Offset must be non-negative integer.");
516                         else if (length < 0)
517                                 throw new ArgumentOutOfRangeException ("length", length, "Length must be non-negative integer.");
518                         else if (buffer.Length < offset + length)
519                                 throw new ArgumentOutOfRangeException ("buffer length is smaller than the sum of offset and length.");
520
521                         if (length == 0)        // It does not raise an error.
522                                 return 0;
523
524                         int bufIndex = offset;
525                         int bufLast = offset + length;
526
527                         if (base64CacheStartsAt >= 0) {
528                                 for (int i = base64CacheStartsAt; i < 3; i++) {
529                                         buffer [bufIndex++] = base64Cache [base64CacheStartsAt++];
530                                         if (bufIndex == bufLast)
531                                                 return bufLast - offset;
532                                 }
533                         }
534
535                         for (int i = 0; i < 3; i++)
536                                 base64Cache [i] = 0;
537                         base64CacheStartsAt = -1;
538
539                         int max = (int) System.Math.Ceiling (4.0 / 3 * length);
540                         int additional = max % 4;
541                         if (additional > 0)
542                                 max += 4 - additional;
543                         char [] chars = new char [max];
544                         int charsLength = ReadChars (chars, 0, max);
545
546                         byte b = 0;
547                         byte work = 0;
548                         for (int i = 0; i < charsLength - 3; i += 4) {
549                                 b = (byte) (GetBase64Byte (chars [i]) << 2);
550                                 if (bufIndex < bufLast)
551                                         buffer [bufIndex] = b;
552                                 else {
553                                         if (base64CacheStartsAt < 0)
554                                                 base64CacheStartsAt = 0;
555                                         base64Cache [0] = b;
556                                 }
557                                 // charsLength mod 4 might not equals to 0.
558                                 if (i + 1 == charsLength)
559                                         break;
560                                 b = GetBase64Byte (chars [i + 1]);
561                                 work = (byte) (b >> 4);
562                                 if (bufIndex < bufLast) {
563                                         buffer [bufIndex] += work;
564                                         bufIndex++;
565                                 }
566                                 else
567                                         base64Cache [0] += work;
568
569                                 work = (byte) ((b & 0xf) << 4);
570                                 if (bufIndex < bufLast) {
571                                         buffer [bufIndex] = work;
572                                 }
573                                 else {
574                                         if (base64CacheStartsAt < 0)
575                                                 base64CacheStartsAt = 1;
576                                         base64Cache [1] = work;
577                                 }
578
579                                 if (i + 2 == charsLength)
580                                         break;
581                                 b = GetBase64Byte (chars [i + 2]);
582                                 work = (byte) (b >> 2);
583                                 if (bufIndex < bufLast) {
584                                         buffer [bufIndex] += work;
585                                         bufIndex++;
586                                 }
587                                 else
588                                         base64Cache [1] += work;
589
590                                 work = (byte) ((b & 3) << 6);
591                                 if (bufIndex < bufLast)
592                                         buffer [bufIndex] = work;
593                                 else {
594                                         if (base64CacheStartsAt < 0)
595                                                 base64CacheStartsAt = 2;
596                                         base64Cache [2] = work;
597                                 }
598                                 if (i + 3 == charsLength)
599                                         break;
600                                 work = GetBase64Byte (chars [i + 3]);
601                                 if (bufIndex < bufLast) {
602                                         buffer [bufIndex] += work;
603                                         bufIndex++;
604                                 }
605                                 else
606                                         base64Cache [2] += work;
607                         }
608                         return System.Math.Min (bufLast - offset, bufIndex - offset);
609                 }
610
611                 public int ReadBinHex (byte [] buffer, int offset, int length)
612                 {
613                         if (offset < 0)
614                                 throw new ArgumentOutOfRangeException ("offset", offset, "Offset must be non-negative integer.");
615                         else if (length < 0)
616                                 throw new ArgumentOutOfRangeException ("length", length, "Length must be non-negative integer.");
617                         else if (buffer.Length < offset + length)
618                                 throw new ArgumentOutOfRangeException ("buffer length is smaller than the sum of offset and length.");
619
620                         if (length == 0)
621                                 return 0;
622
623                         char [] chars = new char [length * 2];
624                         int charsLength = ReadChars (chars, 0, length * 2);
625                         return XmlConvert.FromBinHexString (chars, offset, charsLength, buffer);
626                 }
627
628                 public int ReadChars (char [] buffer, int offset, int length)
629                 {
630                         return ReadCharsInternal (buffer, offset, length);
631                 }
632
633 #if NET_1_1
634 #else
635                 public override string ReadInnerXml ()
636                 {
637                         return ReadInnerXmlInternal ();
638                 }
639
640                 public override string ReadOuterXml ()
641                 {
642                         return ReadOuterXmlInternal ();
643                 }
644
645                 public override string ReadString ()
646                 {
647                         return ReadStringInternal ();
648                 }
649 #endif
650
651                 public void ResetState ()
652                 {
653                         Init ();
654                 }
655
656                 public override void ResolveEntity ()
657                 {
658                         // XmlTextReader does not resolve entities.
659                         throw new InvalidOperationException ("XmlTextReader cannot resolve external entities.");
660                 }
661
662 #if NET_2_0
663                 [MonoTODO ("Implement for performance reason")]
664                 public override void Skip ()
665                 {
666                         base.Skip ();
667                 }
668 #endif
669                 #endregion
670
671                 #region Internals
672                 // Parsed DTD Objects
673 #if DTD_HANDLE_EVENTS
674                 internal event ValidationEventHandler ValidationEventHandler;
675 #endif
676
677                 internal DTDObjectModel DTD {
678                         get { return parserContext.Dtd; }
679                 }
680
681                 internal XmlResolver Resolver {
682                         get { return resolver; }
683                 }
684                 #endregion
685
686                 #region Privates
687                 internal class XmlTokenInfo
688                 {
689                         public XmlTokenInfo (XmlTextReader xtr, bool isPrimaryToken)
690                         {
691                                 this.isPrimaryToken = isPrimaryToken;
692                                 Reader = xtr;
693                                 Clear ();
694                         }
695
696                         bool isPrimaryToken;
697                         string valueCache;
698
699                         protected XmlTextReader Reader;
700
701                         public string Name;
702                         public string LocalName;
703                         public string Prefix;
704                         public string NamespaceURI;
705                         public bool IsEmptyElement;
706                         public char QuoteChar;
707                         public int LineNumber;
708                         public int LinePosition;
709
710                         public XmlNodeType NodeType;
711
712                         public virtual string Value {
713                                 get {
714                                         if (valueCache != null)
715                                                 return valueCache;
716                                         switch (NodeType) {
717                                         case XmlNodeType.Text:
718                                         case XmlNodeType.SignificantWhitespace:
719                                         case XmlNodeType.Whitespace:
720                                         case XmlNodeType.Comment:
721                                         case XmlNodeType.CDATA:
722                                         case XmlNodeType.ProcessingInstruction:
723                                                 valueCache = Reader.CreateValueString ();
724                                                 return valueCache;
725                                         }
726                                         return null;
727                                 }
728                                 set { valueCache = value; }
729                         }
730
731                         public virtual void Clear ()
732                         {
733                                 valueCache = null;
734                                 NodeType = XmlNodeType.None;
735                                 Name = LocalName = Prefix = NamespaceURI = String.Empty;
736                                 IsEmptyElement = false;
737                                 QuoteChar = '"';
738                                 LineNumber = LinePosition = 0;
739                         }
740
741                         internal virtual void FillNames ()
742                         {
743                                 if (Reader.Namespaces) {
744                                         int indexOfColon = -1;
745                                         switch (NodeType) {
746                                         case XmlNodeType.Attribute:
747                                         case XmlNodeType.Element:
748                                         case XmlNodeType.EndElement:
749                                                 indexOfColon = Name.IndexOf (':');
750                                                 break;
751                                         }
752
753                                         if (indexOfColon == -1) {
754                                                 Prefix = String.Empty;
755                                                 LocalName = Name;
756                                         } else {
757                                                 // This improves speed by at least nearly 5%, but eats more memory at least nearly 0.3%
758                                                 // However, this might be reverted if NameTable is got improved.
759                                                 char [] nameArr = Name.ToCharArray ();
760                                                 Prefix = Reader.NameTable.Add (nameArr, 0, indexOfColon);
761                                                 LocalName = Reader.NameTable.Add (nameArr, indexOfColon + 1, nameArr.Length - indexOfColon - 1);
762 //                                              Prefix = Reader.NameTable.Add (Name.Substring (0, indexOfColon));
763 //                                              LocalName = Reader.NameTable.Add (Name.Substring (indexOfColon + 1));
764                                         }
765
766                                         // NamespaceURI
767                                         switch (NodeType) {
768                                         case XmlNodeType.Attribute:
769                                                 if (Prefix.Length == 0)
770                                                         NamespaceURI = string.Empty;
771                                                 else
772                                                         NamespaceURI = Reader.LookupNamespace (Prefix, true);
773                                                 break;
774
775                                         case XmlNodeType.Element:
776                                         case XmlNodeType.EndElement:
777                                                 NamespaceURI = Reader.LookupNamespace (Prefix, true);
778                                                 break;
779                                         default:
780                                                 NamespaceURI = "";
781                                                 break;
782                                         }
783                                 } else {
784                                         Prefix = String.Empty;
785                                         LocalName = Name;
786                                 }
787                         }
788                 }
789
790                 internal class XmlAttributeTokenInfo : XmlTokenInfo
791                 {
792                         public XmlAttributeTokenInfo (XmlTextReader reader)
793                                 : base (reader, false)
794                         {
795                                 NodeType = XmlNodeType.Attribute;
796                         }
797
798                         public int ValueTokenStartIndex;
799                         public int ValueTokenEndIndex;
800                         string valueCache;
801                         bool cachedNormalization;
802                         StringBuilder tmpBuilder = new StringBuilder ();
803
804                         public override string Value {
805                                 get {
806                                         if (cachedNormalization != Reader.Normalization)
807                                                 valueCache = null;
808                                         if (valueCache != null)
809                                                 return valueCache;
810
811                                         cachedNormalization = Reader.Normalization;
812
813                                         // An empty value should return String.Empty.
814                                         if (ValueTokenStartIndex == ValueTokenEndIndex) {
815                                                 XmlTokenInfo ti = Reader.attributeValueTokens [ValueTokenStartIndex];
816                                                 if (ti.NodeType == XmlNodeType.EntityReference)
817                                                         valueCache = String.Concat ("&", ti.Name, ";");
818                                                 else
819                                                         valueCache = ti.Value;
820                                                 if (cachedNormalization)
821                                                         NormalizeSpaces ();
822                                                 return valueCache;
823                                         }
824
825                                         tmpBuilder.Length = 0;
826                                         for (int i = ValueTokenStartIndex; i <= ValueTokenEndIndex; i++) {
827                                                 XmlTokenInfo ti = Reader.attributeValueTokens [i];
828                                                 if (ti.NodeType == XmlNodeType.Text)
829                                                         tmpBuilder.Append (ti.Value);
830                                                 else {
831                                                         tmpBuilder.Append ('&');
832                                                         tmpBuilder.Append (ti.Name);
833                                                         tmpBuilder.Append (';');
834                                                 }
835                                         }
836
837                                         valueCache = tmpBuilder.ToString ();
838                                         if (cachedNormalization)
839                                                 NormalizeSpaces ();
840                                         return valueCache;
841                                 }
842
843                                 set { valueCache = value; }
844                         }
845
846                         public override void Clear ()
847                         {
848                                 base.Clear ();
849                                 valueCache = null;
850                                 NodeType = XmlNodeType.Attribute;
851                                 ValueTokenStartIndex = ValueTokenEndIndex = 0;
852                         }
853
854                         internal override void FillNames ()
855                         {
856                                 base.FillNames ();
857                                 if (Prefix == "xmlns" || Name == "xmlns")
858                                         NamespaceURI = XmlNamespaceManager.XmlnsXmlns;
859                         }
860
861                         private void NormalizeSpaces ()
862                         {
863                                 tmpBuilder.Length = 0;
864                                 for (int i = 0; i < valueCache.Length; i++)
865                                         switch (valueCache [i]) {
866                                         case '\r':
867                                                 if (i + 1 < valueCache.Length && valueCache [i + 1] == '\n')
868                                                         i++;
869                                                 goto case '\n';
870                                         case '\t':
871                                         case '\n':
872                                                 tmpBuilder.Append (' ');
873                                                 break;
874                                         default:
875                                                 tmpBuilder.Append (valueCache [i]);
876                                                 break;
877                                         }
878                                 valueCache = tmpBuilder.ToString ();
879                         }
880                 }
881
882                 private XmlTokenInfo cursorToken;
883                 private XmlTokenInfo currentToken;
884                 private XmlAttributeTokenInfo currentAttributeToken;
885                 private XmlTokenInfo currentAttributeValueToken;
886                 private XmlAttributeTokenInfo [] attributeTokens = new XmlAttributeTokenInfo [10];
887                 private XmlTokenInfo [] attributeValueTokens = new XmlTokenInfo [10];
888                 private int currentAttribute;
889                 private int currentAttributeValue;
890                 private int attributeCount;
891
892                 private XmlParserContext parserContext;
893
894                 private ReadState readState;
895
896                 private int depth;
897                 private int elementDepth;
898                 private bool depthUp;
899
900                 private bool popScope;
901
902                 private string [] elementNames;
903                 int elementNameStackPos;
904
905                 private bool allowMultipleRoot;
906
907                 private bool isStandalone;
908
909                 private bool returnEntityReference;
910                 private string entityReferenceName;
911
912                 private char [] nameBuffer;
913                 private int nameLength;
914                 private int nameCapacity;
915                 private const int initialNameCapacity = 32;
916
917                 private char [] valueBuffer;
918                 private int valueLength;
919                 private int valueCapacity;
920                 private const int initialValueCapacity = 256;
921
922                 private char [] currentTagBuffer;
923                 private int currentTagLength;
924                 private int currentTagCapacity;
925                 private const int initialCurrentTagCapacity = 256;
926
927                 private TextReader reader;
928                 private char [] peekChars;
929                 private int peekCharsIndex;
930                 private int peekCharsLength;
931                 private const int peekCharCapacity = 1024;
932
933                 private int line;
934                 private int column;
935
936                 private int currentLinkedNodeLineNumber;
937                 private int currentLinkedNodeLinePosition;
938                 private bool useProceedingLineInfo;
939
940                 private XmlNodeType startNodeType;
941                 // State machine attribute.
942                 //      XmlDeclaration: after the first node.
943                 //      DocumentType: after doctypedecl
944                 //      Element: inside document element
945                 //      EndElement: after document element
946                 private XmlNodeType currentState;
947
948                 // For ReadChars()/ReadBase64()/ReadBinHex()
949                 private bool shouldSkipUntilEndTag;
950                 private byte [] base64Cache = new byte [3];
951                 private int base64CacheStartsAt;
952
953                 // These values are never re-initialized.
954                 private bool namespaces = true;
955                 private WhitespaceHandling whitespaceHandling = WhitespaceHandling.All;
956                 private XmlResolver resolver = new XmlUrlResolver ();
957                 private bool normalization = false;
958 #if NET_2_0
959                 private bool prohibitDtd = false;
960 #endif
961
962                 private void Init ()
963                 {
964                         currentToken = new XmlTokenInfo (this, true);
965                         cursorToken = currentToken;
966                         currentAttribute = -1;
967                         currentAttributeValue = -1;
968                         attributeCount = 0;
969
970                         readState = ReadState.Initial;
971                         allowMultipleRoot = false;
972
973                         depth = 0;
974                         elementDepth = 0;
975                         depthUp = false;
976
977                         popScope = allowMultipleRoot = false;
978                         elementNames = new string [10];
979                         elementNameStackPos = 0;
980
981                         isStandalone = false;
982                         returnEntityReference = false;
983                         entityReferenceName = String.Empty;
984
985                         nameBuffer = new char [initialNameCapacity];
986                         nameLength = 0;
987                         nameCapacity = initialNameCapacity;
988
989                         valueBuffer = new char [initialValueCapacity];
990                         valueLength = 0;
991                         valueCapacity = initialValueCapacity;
992
993                         currentTagBuffer = new char [initialCurrentTagCapacity];
994                         currentTagLength = 0;
995                         currentTagCapacity = initialCurrentTagCapacity;
996
997                         peekCharsIndex = 0;
998                         peekCharsLength = 0;
999                         if (peekChars == null)
1000                                 peekChars = new char [peekCharCapacity];
1001
1002                         line = 1;
1003                         column = 0;
1004                         currentTagLength = 0;
1005
1006                         currentLinkedNodeLineNumber = currentLinkedNodeLinePosition = 0;
1007                         useProceedingLineInfo = false;
1008
1009                         currentState = XmlNodeType.None;
1010
1011                         shouldSkipUntilEndTag = false;
1012                         base64CacheStartsAt = -1;
1013                 }
1014
1015                 private void InitializeContext (string url, XmlParserContext context, TextReader fragment, XmlNodeType fragType)
1016                 {
1017                         startNodeType = fragType;
1018                         parserContext = context;
1019                         if (context == null) {
1020                                 XmlNameTable nt = new NameTable ();
1021                                 parserContext = new XmlParserContext (nt,
1022                                         new XmlNamespaceManager (nt),
1023                                         String.Empty,
1024                                         XmlSpace.None);
1025                         }
1026
1027                         if (url != null && url.Length > 0) {
1028                                 Uri uri = null;
1029                                 try {
1030                                         uri = new Uri (url);
1031                                 } catch (Exception) {
1032                                         string path = Path.GetFullPath ("./a");
1033                                         uri = new Uri (new Uri (path), url);
1034                                 }
1035                                 parserContext.BaseURI = uri.ToString ();
1036                         }
1037
1038                         Init ();
1039
1040                         switch (fragType) {
1041                         case XmlNodeType.Attribute:
1042                                 fragment = new StringReader (fragment.ReadToEnd ().Replace ("\"", "&quot;"));
1043                                 break;
1044                         case XmlNodeType.Element:
1045                                 currentState = XmlNodeType.Element;
1046                                 allowMultipleRoot = true;
1047                                 break;
1048                         case XmlNodeType.Document:
1049                                 break;
1050                         default:
1051                                 throw new XmlException (String.Format ("NodeType {0} is not allowed to create XmlTextReader.", fragType));
1052                         }
1053
1054                         reader = fragment;
1055                 }
1056
1057                 // Use this method rather than setting the properties
1058                 // directly so that all the necessary properties can
1059                 // be changed in harmony with each other. Maybe the
1060                 // fields should be in a seperate class to help enforce
1061                 // this.
1062                 private void SetProperties (
1063                         XmlNodeType nodeType,
1064                         string name,
1065                         bool isEmptyElement,
1066                         string value,
1067                         bool clearAttributes)
1068                 {
1069                         SetProperties (currentToken, nodeType, name, isEmptyElement, value, clearAttributes);
1070                         currentToken.LineNumber = this.currentLinkedNodeLineNumber;
1071                         currentToken.LinePosition = this.currentLinkedNodeLinePosition;
1072                 }
1073
1074                 private void SetProperties (
1075                         XmlTokenInfo token,
1076                         XmlNodeType nodeType,
1077                         string name,
1078                         bool isEmptyElement,
1079                         string value,
1080                         bool clearAttributes)
1081                 {
1082                         token.Clear ();
1083                         token.NodeType = nodeType;
1084                         token.Name = name;
1085                         token.IsEmptyElement = isEmptyElement;
1086                         token.Value = value;
1087                         this.elementDepth = depth;
1088
1089                         if (clearAttributes)
1090                                 ClearAttributes ();
1091
1092                         token.FillNames ();
1093                 }
1094
1095                 private void ClearAttributes ()
1096                 {
1097                         for (int i = 0; i < attributeCount; i++)
1098                                 attributeTokens [i].Clear ();
1099                         attributeCount = 0;
1100                         currentAttribute = -1;
1101                         currentAttributeValue = -1;
1102                 }
1103
1104                 private int PeekChar ()
1105                 {
1106                         if (peekCharsLength == peekCharsIndex) {
1107                                 if (!ReadTextReader ())
1108                                         return -1;
1109                                 return PeekChar ();
1110                         }
1111                         else
1112                                 return peekChars [peekCharsIndex];
1113                 }
1114
1115                 private int ReadChar ()
1116                 {
1117                         int ch;
1118
1119                         if (peekCharsLength == peekCharsIndex) {
1120                                 if (!ReadTextReader ())
1121                                         return -1;
1122                                 return ReadChar ();
1123                         }
1124                         ch = peekChars [peekCharsIndex++];
1125
1126                         if (ch == '\n') {
1127                                 line++;
1128                                 column = 1;
1129                         } else {
1130                                 column++;
1131                         }
1132                         if (currentState != XmlNodeType.Element)
1133                                 AppendCurrentTagChar (ch);
1134                         return ch;
1135                 }
1136
1137                 private bool ReadTextReader ()
1138                 {
1139                         peekCharsIndex = 0;
1140                         peekCharsLength = reader.Read (peekChars, 0, peekCharCapacity);
1141                         if (peekCharsLength == 0)
1142                                 return false;
1143                         return true;
1144                 }
1145
1146                 private string ExpandSurrogateChar (int ch)
1147                 {
1148                         if (ch < Char.MaxValue)
1149                                 return ((char) ch).ToString ();
1150                         else {
1151                                 char [] tmp = new char [] {(char) (ch / 0x10000 + 0xD800 - 1), (char) (ch % 0x10000 + 0xDC00)};
1152                                 return new string (tmp);
1153                         }
1154                 }
1155
1156                 // This should really keep track of some state so
1157                 // that it's not possible to have more than one document
1158                 // element or text outside of the document element.
1159                 private bool ReadContent ()
1160                 {
1161                         currentTagLength = 0;
1162                         if (popScope) {
1163                                 parserContext.NamespaceManager.PopScope ();
1164                                 popScope = false;
1165                         }
1166
1167                         if (returnEntityReference)
1168                                 SetEntityReferenceProperties ();
1169                         else {
1170                                 switch (PeekChar ()) {
1171                                 case '<':
1172                                         ReadChar ();
1173                                         ReadTag ();
1174                                         break;
1175                                 case '\r': goto case ' ';
1176                                 case '\n': goto case ' ';
1177                                 case '\t': goto case ' ';
1178                                 case ' ':
1179                                         if (whitespaceHandling == WhitespaceHandling.All ||
1180                                                 whitespaceHandling == WhitespaceHandling.Significant)
1181                                                 ReadWhitespace ();
1182                                         else {
1183                                                 SkipWhitespace ();
1184                                                 return ReadContent ();
1185                                         }
1186                                         break;
1187                                 case -1:
1188                                         readState = ReadState.EndOfFile;
1189                                         ClearValueBuffer ();
1190                                         SetProperties (
1191                                                 XmlNodeType.None, // nodeType
1192                                                 String.Empty, // name
1193                                                 false, // isEmptyElement
1194                                                 null, // value
1195                                                 true // clearAttributes
1196                                         );
1197                                         if (depth > 0)
1198                                                 throw new XmlException ("unexpected end of file. Current depth is " + depth);
1199
1200                                         return false;
1201                                 default:
1202                                         ReadText (true);
1203                                         break;
1204                                 }
1205                         }
1206                         return this.ReadState != ReadState.EndOfFile;
1207                 }
1208
1209                 private void SetEntityReferenceProperties ()
1210                 {
1211                         DTDEntityDeclaration decl = DTD != null ? DTD.EntityDecls [entityReferenceName] : null;
1212                         if (this.isStandalone)
1213                                 if (DTD == null || decl == null || !decl.IsInternalSubset)
1214                                         throw new XmlException (this as IXmlLineInfo,
1215                                                 "Standalone document must not contain any references to an non-internally declared entity.");
1216                         if (decl != null && decl.NotationName != null)
1217                                 throw new XmlException (this as IXmlLineInfo,
1218                                         "Reference to any unparsed entities is not allowed here.");
1219
1220                         ClearValueBuffer ();
1221                         SetProperties (
1222                                 XmlNodeType.EntityReference, // nodeType
1223                                 entityReferenceName, // name
1224                                 false, // isEmptyElement
1225                                 null, // value
1226                                 true // clearAttributes
1227                         );
1228
1229                         returnEntityReference = false;
1230                         entityReferenceName = String.Empty;
1231                 }
1232
1233                 // The leading '<' has already been consumed.
1234                 private void ReadTag ()
1235                 {
1236                         switch (PeekChar ())
1237                         {
1238                         case '/':
1239                                 ReadChar ();
1240                                 ReadEndTag ();
1241                                 break;
1242                         case '?':
1243                                 ReadChar ();
1244                                 ReadProcessingInstruction ();
1245                                 break;
1246                         case '!':
1247                                 ReadChar ();
1248                                 ReadDeclaration ();
1249                                 break;
1250                         default:
1251                                 ReadStartTag ();
1252                                 break;
1253                         }
1254                 }
1255
1256                 // The leading '<' has already been consumed.
1257                 private void ReadStartTag ()
1258                 {
1259                         if (currentState == XmlNodeType.EndElement)
1260                                 throw new XmlException (this as IXmlLineInfo,
1261                                         "Multiple document element was detected.");
1262                         currentState = XmlNodeType.Element;
1263
1264                         parserContext.NamespaceManager.PushScope ();
1265
1266                         string name = ReadName ();
1267                         if (currentState == XmlNodeType.EndElement)
1268                                 throw new XmlException (this as IXmlLineInfo,"document has terminated, cannot open new element");
1269
1270                         bool isEmptyElement = false;
1271
1272                         ClearAttributes ();
1273
1274                         SkipWhitespace ();
1275                         if (XmlChar.IsFirstNameChar (PeekChar ()))
1276                                 ReadAttributes (false);
1277                         cursorToken = this.currentToken;
1278
1279                         // fill namespaces
1280                         for (int i = 0; i < attributeCount; i++)
1281                                 attributeTokens [i].FillNames ();
1282
1283                         // quick name check
1284                         for (int i = 0; i < attributeCount; i++) {
1285                                 for (int j = i + 1; j < attributeCount; j++)
1286                                         if (Object.ReferenceEquals (attributeTokens [i].Name, attributeTokens [j].Name) ||
1287                                                 (Object.ReferenceEquals (attributeTokens [i].LocalName, attributeTokens [j].LocalName) &&
1288                                                 Object.ReferenceEquals (attributeTokens [i].NamespaceURI, attributeTokens [j].NamespaceURI)))
1289                                                 throw new XmlException (this as IXmlLineInfo,
1290                                                         "Attribute name and qualified name must be identical.");
1291                         }
1292
1293                         string baseUri = GetAttribute ("xml:base");
1294                         if (baseUri != null) {
1295                                 if (this.resolver != null)
1296                                         parserContext.BaseURI = resolver.ResolveUri (new Uri (BaseURI), baseUri).ToString ();
1297                                 else
1298                                         parserContext.BaseURI = baseUri;
1299                         }
1300                         string xmlLang = GetAttribute ("xml:lang");
1301                         if (xmlLang != null)
1302                                 parserContext.XmlLang = xmlLang;
1303                         string xmlSpaceAttr = GetAttribute ("xml:space");
1304                         if (xmlSpaceAttr != null) {
1305                                 if (xmlSpaceAttr == "preserve")
1306                                         parserContext.XmlSpace = XmlSpace.Preserve;
1307                                 else if (xmlSpaceAttr == "default")
1308                                         parserContext.XmlSpace = XmlSpace.Default;
1309                                 else
1310                                         throw new XmlException (this as IXmlLineInfo,String.Format ("Invalid xml:space value: {0}", xmlSpaceAttr));
1311                         }
1312                         if (PeekChar () == '/') {
1313                                 ReadChar ();
1314                                 isEmptyElement = true;
1315                                 popScope = true;
1316                         }
1317                         else {
1318                                 depthUp = true;
1319                                 PushElementName (name);
1320                                 parserContext.PushScope ();
1321                         }
1322
1323                         Expect ('>');
1324                         SetProperties (
1325                                 XmlNodeType.Element, // nodeType
1326                                 name, // name
1327                                 isEmptyElement, // isEmptyElement
1328                                 null, // value
1329                                 false // clearAttributes
1330                         );
1331
1332                         if (LookupNamespace (Prefix) == null)
1333                                 throw new XmlException (String.Format ("'{0}' is undeclared namespace.", Prefix));
1334                         try {
1335                                 for (int i = 0; i < attributeCount; i++) {
1336                                         MoveToAttribute (i);
1337                                         if (LookupNamespace (Prefix) == null)
1338                                                 throw new XmlException (String.Format ("'{0}' is undeclared namespace.", Prefix));
1339                                 }
1340                         } finally {
1341                                 MoveToElement ();
1342                         }
1343
1344                         if (IsEmptyElement)
1345                                 CheckCurrentStateUpdate ();
1346                 }
1347
1348                 private void PushElementName (string name)
1349                 {
1350                         if (elementNames.Length == elementNameStackPos) {
1351                                 string [] newArray = new string [elementNames.Length * 2];
1352                                 Array.Copy (elementNames, 0, newArray, 0, elementNameStackPos);
1353                                 elementNames = newArray;
1354                         }
1355                         elementNames [elementNameStackPos++] = name;
1356                 }
1357
1358                 // The reader is positioned on the first character
1359                 // of the element's name.
1360                 private void ReadEndTag ()
1361                 {
1362                         if (currentState != XmlNodeType.Element)
1363                                 throw new XmlException (this as IXmlLineInfo,
1364                                         "End tag cannot appear in this state.");
1365
1366                         string name = ReadName ();
1367                         if (elementNameStackPos == 0)
1368                                 throw new XmlException (this as IXmlLineInfo,"closing element without matching opening element");
1369                         string expected = elementNames [--elementNameStackPos];
1370                         if (expected != name)
1371                                 throw new XmlException (this as IXmlLineInfo,String.Format ("unmatched closing element: expected {0} but found {1}", expected, name));
1372                         parserContext.PopScope ();
1373
1374                         ExpectAfterWhitespace ('>');
1375
1376                         --depth;
1377
1378                         SetProperties (
1379                                 XmlNodeType.EndElement, // nodeType
1380                                 name, // name
1381                                 false, // isEmptyElement
1382                                 null, // value
1383                                 true // clearAttributes
1384                         );
1385
1386                         popScope = true;
1387
1388                         CheckCurrentStateUpdate ();
1389                 }
1390
1391                 private void CheckCurrentStateUpdate ()
1392                 {
1393                         if (depth == 0 && !allowMultipleRoot && (IsEmptyElement || NodeType == XmlNodeType.EndElement))
1394                                 currentState = XmlNodeType.EndElement;
1395                 }
1396
1397                 private void AppendNameChar (int ch)
1398                 {
1399                         if (nameLength == nameCapacity)
1400                                 ExpandNameCapacity ();
1401                         if (ch < Char.MaxValue)
1402                                 nameBuffer [nameLength++] = (char) ch;
1403                         else {
1404                                 nameBuffer [nameLength++] = (char) (ch / 0x10000 + 0xD800 - 1);
1405                                 if (nameLength == nameCapacity)
1406                                         ExpandNameCapacity ();
1407                                 nameBuffer [nameLength++] = (char) (ch % 0x10000 + 0xDC00);
1408                         }
1409                 }
1410
1411                 private void ExpandNameCapacity ()
1412                 {
1413                         nameCapacity = nameCapacity * 2;
1414                         char [] oldNameBuffer = nameBuffer;
1415                         nameBuffer = new char [nameCapacity];
1416                         Array.Copy (oldNameBuffer, nameBuffer, nameLength);
1417                 }
1418
1419                 private string CreateNameString ()
1420                 {
1421                         return parserContext.NameTable.Add (nameBuffer, 0, nameLength);
1422                 }
1423
1424                 private void AppendValueChar (int ch)
1425                 {
1426                         if (valueLength == valueCapacity)
1427                                 ExpandValueCapacity ();
1428                         if (ch < Char.MaxValue)
1429                                 valueBuffer [valueLength++] = (char) ch;
1430                         else {
1431                                 valueBuffer [valueLength++] = (char) (ch / 0x10000 + 0xD800 - 1);
1432                                 if (valueLength == valueCapacity)
1433                                         ExpandValueCapacity ();
1434                                 valueBuffer [valueLength++] = (char) (ch % 0x10000 + 0xDC00);
1435                         }
1436                 }
1437
1438                 private void ExpandValueCapacity ()
1439                 {
1440                         valueCapacity = valueCapacity * 2;
1441                         char [] oldValueBuffer = valueBuffer;
1442                         valueBuffer = new char [valueCapacity];
1443                         Array.Copy (oldValueBuffer, valueBuffer, valueLength);
1444                 }
1445
1446                 private string CreateValueString ()
1447                 {
1448                         return new string (valueBuffer, 0, valueLength);
1449                 }
1450
1451                 private void ClearValueBuffer ()
1452                 {
1453                         valueLength = 0;
1454                 }
1455
1456                 private void AppendCurrentTagChar (int ch)
1457                 {
1458                         if (currentTagLength == currentTagCapacity)
1459                                 ExpandCurrentTagCapacity ();
1460                         if (ch < Char.MaxValue)
1461                                 currentTagBuffer [currentTagLength++] = (char) ch;
1462                         else {
1463                                 currentTagBuffer [currentTagLength++] = (char) (ch / 0x10000 + 0xD800 - 1);
1464                                 if (currentTagLength == currentTagCapacity)
1465                                         ExpandCurrentTagCapacity ();
1466                                 currentTagBuffer [currentTagLength++] = (char) (ch % 0x10000 + 0xDC00);
1467                         }
1468                 }
1469
1470                 private void ExpandCurrentTagCapacity ()
1471                 {
1472                         currentTagCapacity = currentTagCapacity * 2;
1473                         char [] oldCurrentTagBuffer = currentTagBuffer;
1474                         currentTagBuffer = new char [currentTagCapacity];
1475                         Array.Copy (oldCurrentTagBuffer, currentTagBuffer, currentTagLength);
1476                 }
1477
1478                 private string CreateCurrentTagString ()
1479                 {
1480                         return new string (currentTagBuffer, 0, currentTagLength);
1481                 }
1482
1483                 private void ClearCurrentTagBuffer ()
1484                 {
1485                         currentTagLength = 0;
1486                 }
1487
1488                 // The reader is positioned on the first character
1489                 // of the text.
1490                 private void ReadText (bool notWhitespace)
1491                 {
1492                         if (currentState != XmlNodeType.Element)
1493                                 throw new XmlException (this as IXmlLineInfo,
1494                                         "Text node cannot appear in this state.");
1495
1496                         if (notWhitespace)
1497                                 ClearValueBuffer ();
1498
1499                         int ch = PeekChar ();
1500                         bool previousWasCloseBracket = false;
1501
1502                         while (ch != '<' && ch != -1) {
1503                                 if (ch == '&') {
1504                                         ReadChar ();
1505                                         ch = ReadReference (false);
1506                                         if (returnEntityReference) // Returns -1 if char validation should not be done
1507                                                 break;
1508                                 }
1509                                 else {
1510                                         if (XmlChar.IsInvalid (ch))
1511                                                 throw new XmlException (this, "Not allowed character was found.");
1512                                         ch = ReadChar ();
1513                                 }
1514
1515                                 AppendValueChar (ch);
1516
1517                                 // Block "]]>"
1518                                 if (ch == ']') {
1519                                         if (previousWasCloseBracket)
1520                                                 if (PeekChar () == '>')
1521                                                         throw new XmlException (this as IXmlLineInfo,
1522                                                                 "Inside text content, character sequence ']]>' is not allowed.");
1523                                         previousWasCloseBracket = true;
1524                                 }
1525                                 else if (previousWasCloseBracket)
1526                                         previousWasCloseBracket = false;
1527                                 ch = PeekChar ();
1528                                 notWhitespace = true;
1529                         }
1530
1531                         if (returnEntityReference && valueLength == 0) {
1532                                 SetEntityReferenceProperties ();
1533                         } else {
1534                                 XmlNodeType nodeType = notWhitespace ? XmlNodeType.Text :
1535                                         this.XmlSpace == XmlSpace.Preserve ? XmlNodeType.SignificantWhitespace : XmlNodeType.Whitespace;
1536                                 SetProperties (
1537                                         nodeType, // nodeType
1538                                         String.Empty, // name
1539                                         false, // isEmptyElement
1540                                         null, // value: create only when required
1541                                         true // clearAttributes
1542                                 );
1543                         }
1544                 }
1545
1546                 // The leading '&' has already been consumed.
1547                 // Returns true if the entity reference isn't a simple
1548                 // character reference or one of the predefined entities.
1549                 // This allows the ReadText method to break so that the
1550                 // next call to Read will return the EntityReference node.
1551                 private int ReadReference (bool ignoreEntityReferences)
1552                 {
1553                         if (PeekChar () == '#') {
1554                                 ReadChar ();
1555                                 return ReadCharacterReference ();
1556                         } else
1557                                 return ReadEntityReference (ignoreEntityReferences);
1558                 }
1559
1560                 private int ReadCharacterReference ()
1561                 {
1562                         int value = 0;
1563
1564                         if (PeekChar () == 'x') {
1565                                 ReadChar ();
1566
1567                                 while (PeekChar () != ';' && PeekChar () != -1) {
1568                                         int ch = ReadChar ();
1569
1570                                         if (ch >= '0' && ch <= '9')
1571                                                 value = (value << 4) + ch - '0';
1572                                         else if (ch >= 'A' && ch <= 'F')
1573                                                 value = (value << 4) + ch - 'A' + 10;
1574                                         else if (ch >= 'a' && ch <= 'f')
1575                                                 value = (value << 4) + ch - 'a' + 10;
1576                                         else
1577                                                 throw new XmlException (this as IXmlLineInfo,
1578                                                         String.Format (
1579                                                                 "invalid hexadecimal digit: {0} (#x{1:X})",
1580                                                                 (char) ch,
1581                                                                 ch));
1582                                 }
1583                         } else {
1584                                 while (PeekChar () != ';' && PeekChar () != -1) {
1585                                         int ch = ReadChar ();
1586
1587                                         if (ch >= '0' && ch <= '9')
1588                                                 value = value * 10 + ch - '0';
1589                                         else
1590                                                 throw new XmlException (this as IXmlLineInfo,
1591                                                         String.Format (
1592                                                                 "invalid decimal digit: {0} (#x{1:X})",
1593                                                                 (char) ch,
1594                                                                 ch));
1595                                 }
1596                         }
1597
1598                         ReadChar (); // ';'
1599
1600                         // There is no way to save surrogate pairs...
1601                         if (normalization && XmlChar.IsInvalid (value))
1602                                 throw new XmlException (this as IXmlLineInfo,
1603                                         "Referenced character was not allowed in XML.");
1604                         return value;
1605                 }
1606
1607                 // Returns -1 if it should not be validated.
1608                 // Real EOF must not be detected here.
1609                 private int ReadEntityReference (bool ignoreEntityReferences)
1610                 {
1611                         string name = ReadName ();
1612                         Expect (';');
1613
1614                         int predefined = XmlChar.GetPredefinedEntity (name);
1615                         if (predefined >= 0)
1616                                 return predefined;
1617                         else {
1618                                 if (ignoreEntityReferences) {
1619                                         AppendValueChar ('&');
1620                                         for (int i = 0; i < name.Length; i++)
1621                                                 AppendValueChar (name [i]);
1622                                         AppendValueChar (';');
1623                                 } else {
1624                                         returnEntityReference = true;
1625                                         entityReferenceName = name;
1626                                 }
1627                         }
1628                         return -1;
1629                 }
1630
1631                 // The reader is positioned on the first character of
1632                 // the attribute name.
1633                 private void ReadAttributes (bool isXmlDecl)
1634                 {
1635                         int peekChar = -1;
1636                         bool requireWhitespace = false;
1637                         currentAttribute = -1;
1638                         currentAttributeValue = -1;
1639
1640                         do {
1641                                 if (!SkipWhitespace () && requireWhitespace)
1642                                         throw new XmlException ("Unexpected token. Name is required here.");
1643
1644                                 IncrementAttributeToken ();
1645                                 currentAttributeToken.LineNumber = line;
1646                                 currentAttributeToken.LinePosition = column;
1647
1648                                 currentAttributeToken.LocalName = 
1649                                         currentAttributeToken.Name = ReadName ();
1650                                 ExpectAfterWhitespace ('=');
1651                                 SkipWhitespace ();
1652                                 ReadAttributeValueTokens (-1);
1653                                 attributeCount++;
1654
1655                                 if (currentAttributeToken.Name == "xmlns")
1656                                         parserContext.NamespaceManager.AddNamespace (String.Empty, GetAttribute (currentAttribute));
1657                                 else if (currentAttributeToken.Name.StartsWith ("xmlns:")) {
1658                                         string nsPrefix = currentAttributeToken.Name.Substring (6);
1659                                         parserContext.NamespaceManager.AddNamespace (nsPrefix, GetAttribute (currentAttribute));
1660                                 }
1661
1662                                 if (!SkipWhitespace ())
1663                                         requireWhitespace = true;
1664                                 peekChar = PeekChar ();
1665                                 if (isXmlDecl) {
1666                                         if (peekChar == '?')
1667                                                 break;
1668                                 }
1669                                 else if (peekChar == '/' || peekChar == '>')
1670                                         break;
1671                         } while (peekChar != -1);
1672
1673                         currentAttribute = -1;
1674                         currentAttributeValue = -1;
1675                 }
1676
1677                 private void AddAttribute (string name, string value)
1678                 {
1679                         IncrementAttributeToken ();
1680                         XmlAttributeTokenInfo ati = attributeTokens [currentAttribute];
1681                         ati.Name = "SYSTEM";
1682                         ati.FillNames ();
1683                         IncrementAttributeValueToken ();
1684                         XmlTokenInfo vti = attributeValueTokens [currentAttributeValue];
1685                         vti.Value = value;
1686                         SetProperties (vti, XmlNodeType.Text, String.Empty, false, value, false);
1687                         attributeCount++;
1688                 }
1689
1690                 private void IncrementAttributeToken ()
1691                 {
1692                         currentAttribute++;
1693                         if (attributeTokens.Length == currentAttribute) {
1694                                 XmlAttributeTokenInfo [] newArray = 
1695                                         new XmlAttributeTokenInfo [attributeTokens.Length * 2];
1696                                 attributeTokens.CopyTo (newArray, 0);
1697                                 attributeTokens = newArray;
1698                         }
1699                         if (attributeTokens [currentAttribute] == null)
1700                                 attributeTokens [currentAttribute] = new XmlAttributeTokenInfo (this);
1701                         currentAttributeToken = attributeTokens [currentAttribute];
1702                         currentAttributeToken.Clear ();
1703                 }
1704
1705                 private void IncrementAttributeValueToken ()
1706                 {
1707                         ClearValueBuffer ();
1708                         currentAttributeValue++;
1709                         if (attributeValueTokens.Length == currentAttributeValue) {
1710                                 XmlTokenInfo [] newArray = new XmlTokenInfo [attributeValueTokens.Length * 2];
1711                                 attributeValueTokens.CopyTo (newArray, 0);
1712                                 attributeValueTokens = newArray;
1713                         }
1714                         if (attributeValueTokens [currentAttributeValue] == null)
1715                                 attributeValueTokens [currentAttributeValue] = new XmlTokenInfo (this, false);
1716                         currentAttributeValueToken = attributeValueTokens [currentAttributeValue];
1717                         currentAttributeValueToken.Clear ();
1718                 }
1719
1720                 // LAMESPEC: Orthodox XML reader should normalize attribute values
1721                 private void ReadAttributeValueTokens (int dummyQuoteChar)
1722                 {
1723                         int quoteChar = (dummyQuoteChar < 0) ? ReadChar () : dummyQuoteChar;
1724
1725                         if (quoteChar != '\'' && quoteChar != '\"')
1726                                 throw new XmlException (this as IXmlLineInfo,"an attribute value was not quoted");
1727                         currentAttributeToken.QuoteChar = (char) quoteChar;
1728
1729                         IncrementAttributeValueToken ();
1730                         currentAttributeToken.ValueTokenStartIndex = currentAttributeValue;
1731                         currentAttributeValueToken.LineNumber = line;
1732                         currentAttributeValueToken.LinePosition = column;
1733
1734                         bool incrementToken = false;
1735                         bool isNewToken = true;
1736                         bool loop = true;
1737                         int ch = 0;
1738                         while (loop) {
1739                                 ch = ReadChar ();
1740                                 if (ch == quoteChar)
1741                                         break;
1742
1743                                 if (incrementToken) {
1744                                         IncrementAttributeValueToken ();
1745                                         currentAttributeValueToken.LineNumber = line;
1746                                         currentAttributeValueToken.LinePosition = column;
1747                                         incrementToken = false;
1748                                         isNewToken = true;
1749                                 }
1750
1751                                 switch (ch)
1752                                 {
1753                                 case '<':
1754                                         throw new XmlException (this as IXmlLineInfo,"attribute values cannot contain '<'");
1755                                 case -1:
1756                                         if (dummyQuoteChar < 0)
1757                                                 throw new XmlException (this as IXmlLineInfo,"unexpected end of file in an attribute value");
1758                                         else    // Attribute value constructor.
1759                                                 loop = false;
1760                                         break;
1761                                 case '&':
1762                                         int startPosition = currentTagLength - 1;
1763                                         if (PeekChar () == '#') {
1764                                                 ReadChar ();
1765                                                 ch = ReadCharacterReference ();
1766                                                 if (normalization && XmlChar.IsInvalid (ch))
1767                                                         throw new XmlException (this as IXmlLineInfo,
1768                                                                 "Not allowed character was found.");
1769                                                 AppendValueChar (ch);
1770                                                 break;
1771                                         }
1772                                         // Check XML 1.0 section 3.1 WFC.
1773                                         string entName = ReadName ();
1774                                         Expect (';');
1775                                         int predefined = XmlChar.GetPredefinedEntity (entName);
1776                                         if (predefined < 0) {
1777                                                 CheckAttributeEntityReferenceWFC (entName);
1778                                                 currentAttributeValueToken.Value = CreateValueString ();
1779                                                 currentAttributeValueToken.NodeType = XmlNodeType.Text;
1780                                                 if (!isNewToken)
1781                                                         IncrementAttributeValueToken ();
1782                                                 currentAttributeValueToken.Name = entName;
1783                                                 currentAttributeValueToken.Value = String.Empty;
1784                                                 currentAttributeValueToken.NodeType = XmlNodeType.EntityReference;
1785                                                 incrementToken = true;
1786                                         }
1787                                         else
1788                                                 AppendValueChar (predefined);
1789                                         break;
1790                                 default:
1791                                         if (normalization && XmlChar.IsInvalid (ch))
1792                                                 throw new XmlException (this, "Invalid character was found.");
1793                                         AppendValueChar (ch);
1794                                         break;
1795                                 }
1796
1797                                 isNewToken = false;
1798                         }
1799                         if (!incrementToken) {
1800                                 currentAttributeValueToken.Value = CreateValueString ();
1801                                 currentAttributeValueToken.NodeType = XmlNodeType.Text;
1802                         }
1803                         currentAttributeToken.ValueTokenEndIndex = currentAttributeValue;
1804
1805                 }
1806
1807                 private void CheckAttributeEntityReferenceWFC (string entName)
1808                 {
1809                         DTDEntityDeclaration entDecl = 
1810                                 DTD == null ? null : DTD.EntityDecls [entName];
1811                         if (DTD != null && resolver != null && entDecl == null)
1812                                 throw new XmlException (this, "Referenced entity does not exist.");
1813
1814                         if (entDecl == null)
1815                                 return;
1816
1817                         if (entDecl.HasExternalReference)
1818                                 throw new XmlException (this, "Reference to external entities is not allowed in the value of an attribute.");
1819                         if (isStandalone && !entDecl.IsInternalSubset)
1820                                 throw new XmlException (this, "Reference to external entities is not allowed in the internal subset.");
1821                         if (entDecl.EntityValue.IndexOf ('<') >= 0)
1822                                 throw new XmlException (this, "Attribute must not contain character '<' either directly or indirectly by way of entity references.");
1823                 }
1824
1825                 // The reader is positioned on the first character
1826                 // of the target.
1827                 //
1828                 // It may be xml declaration or processing instruction.
1829                 private void ReadProcessingInstruction ()
1830                 {
1831                         string target = ReadName ();
1832                         if (target == "xml") {
1833                                 ReadXmlDeclaration ();
1834                                 return;
1835                         } else if (target.ToLower () == "xml")
1836                                 throw new XmlException (this as IXmlLineInfo,
1837                                         "Not allowed processing instruction name which starts with 'X', 'M', 'L' was found.");
1838
1839                         if (currentState == XmlNodeType.None)
1840                                 currentState = XmlNodeType.XmlDeclaration;
1841
1842                         if (!SkipWhitespace ())
1843                                 if (PeekChar () != '?')
1844                                         throw new XmlException (this as IXmlLineInfo,
1845                                                 "Invalid processing instruction name was found.");
1846
1847                         ClearValueBuffer ();
1848
1849                         while (PeekChar () != -1) {
1850                                 int ch = ReadChar ();
1851
1852                                 if (ch == '?' && PeekChar () == '>') {
1853                                         ReadChar ();
1854                                         break;
1855                                 }
1856
1857                                 if (normalization && XmlChar.IsInvalid (ch))
1858                                         throw new XmlException (this, "Invalid character was found.");
1859                                 AppendValueChar (ch);
1860                         }
1861
1862                         SetProperties (
1863                                 XmlNodeType.ProcessingInstruction, // nodeType
1864                                 target, // name
1865                                 false, // isEmptyElement
1866                                 null, // value: create only when required
1867                                 true // clearAttributes
1868                         );
1869                 }
1870
1871                 // The reader is positioned after "<?xml "
1872                 private void ReadXmlDeclaration ()
1873                 {
1874                         if (currentState != XmlNodeType.None) {
1875                                 throw new XmlException (this as IXmlLineInfo,
1876                                         "XML declaration cannot appear in this state.");
1877                         }
1878                         currentState = XmlNodeType.XmlDeclaration;
1879
1880                         ClearAttributes ();
1881
1882                         ReadAttributes (true);  // They must have "version."
1883                         string version = GetAttribute ("version");
1884
1885                         string message = null;
1886
1887                         if (attributeTokens [0].Name != "version" || version != "1.0")
1888                                 message = "Version 1.0 declaration is required in XML Declaration.";
1889                         else if (attributeCount > 1 &&
1890                                         (attributeTokens [1].Name != "encoding" &&
1891                                         attributeTokens [1].Name != "standalone"))
1892                                 message = "Invalid Xml Declaration markup was found.";
1893                         else if (attributeCount > 2 && attributeTokens [2].Name != "standalone")
1894                                 message = "Invalid Xml Declaration markup was found.";
1895                         string sa = GetAttribute ("standalone");
1896                         if (sa != null && sa != "yes" && sa != "no")
1897                                 message = "Only 'yes' or 'no' is allowed for standalone.";
1898
1899                         this.isStandalone = (sa == "yes");
1900
1901                         if (message != null)
1902                                 throw new XmlException (this as IXmlLineInfo, message);
1903
1904                         SetProperties (
1905                                 XmlNodeType.XmlDeclaration, // nodeType
1906                                 "xml", // name
1907                                 false, // isEmptyElement
1908                                 new string (currentTagBuffer, 6, currentTagLength - 6), // value
1909                                 false // clearAttributes
1910                         );
1911
1912                         Expect ("?>");
1913                 }
1914
1915                 internal void SkipTextDeclaration ()
1916                 {
1917                         this.currentState = XmlNodeType.Element;
1918
1919                         if (PeekChar () != '<')
1920                                 return;
1921
1922                         ReadChar ();
1923
1924                         if (PeekChar () != '?') {
1925                                 peekCharsIndex = 0;
1926                                 return;
1927                         }
1928                         ReadChar ();
1929
1930                         while (peekCharsIndex < 6) {
1931                                 if (PeekChar () < 0)
1932                                         break;
1933                                 else
1934                                         ReadChar ();
1935                         }
1936                         if (new string (peekChars, 2, 4) != "xml ") {
1937                                 if (new string (peekChars, 2, 3).ToLower () == "xml") {
1938                                         throw new XmlException (this as IXmlLineInfo,
1939                                                 "Processing instruction name must not be character sequence 'X' 'M' 'L' with case insensitivity.");
1940                                 }
1941                                 peekCharsIndex = 0;
1942                                 return;
1943                         }
1944
1945                         SkipWhitespace ();
1946
1947                         // version decl
1948                         if (PeekChar () == 'v') {
1949                                 Expect ("version");
1950                                 ExpectAfterWhitespace ('=');
1951                                 SkipWhitespace ();
1952                                 int quoteChar = ReadChar ();
1953                                 char [] expect1_0 = new char [3];
1954                                 int versionLength = 0;
1955                                 switch (quoteChar) {
1956                                 case '\'':
1957                                 case '"':
1958                                         while (PeekChar () != quoteChar) {
1959                                                 if (PeekChar () == -1)
1960                                                         throw new XmlException (this as IXmlLineInfo,
1961                                                                 "Invalid version declaration inside text declaration.");
1962                                                 else if (versionLength == 3)
1963                                                         throw new XmlException (this as IXmlLineInfo,
1964                                                                 "Invalid version number inside text declaration.");
1965                                                 else {
1966                                                         expect1_0 [versionLength] = (char) ReadChar ();
1967                                                         versionLength++;
1968                                                         if (versionLength == 3 && new String (expect1_0) != "1.0")
1969                                                                 throw new XmlException (this as IXmlLineInfo,
1970                                                                         "Invalid version number inside text declaration.");
1971                                                 }
1972                                         }
1973                                         ReadChar ();
1974                                         SkipWhitespace ();
1975                                         break;
1976                                 default:
1977                                         throw new XmlException (this as IXmlLineInfo,
1978                                                 "Invalid version declaration inside text declaration.");
1979                                 }
1980                         }
1981
1982                         if (PeekChar () == 'e') {
1983                                 Expect ("encoding");
1984                                 ExpectAfterWhitespace ('=');
1985                                 SkipWhitespace ();
1986                                 int quoteChar = ReadChar ();
1987                                 switch (quoteChar) {
1988                                 case '\'':
1989                                 case '"':
1990                                         while (PeekChar () != quoteChar)
1991                                                 if (ReadChar () == -1)
1992                                                         throw new XmlException (this as IXmlLineInfo,
1993                                                                 "Invalid encoding declaration inside text declaration.");
1994                                         ReadChar ();
1995                                         SkipWhitespace ();
1996                                         break;
1997                                 default:
1998                                         throw new XmlException (this as IXmlLineInfo,
1999                                                 "Invalid encoding declaration inside text declaration.");
2000                                 }
2001                                 // Encoding value should be checked inside XmlInputStream.
2002                         }
2003                         else
2004                                 throw new XmlException (this as IXmlLineInfo,
2005                                         "Encoding declaration is mandatory in text declaration.");
2006
2007                         Expect ("?>");
2008                 }
2009
2010                 // The reader is positioned on the first character after
2011                 // the leading '<!'.
2012                 private void ReadDeclaration ()
2013                 {
2014                         int ch = PeekChar ();
2015
2016                         switch (ch)
2017                         {
2018                         case '-':
2019                                 Expect ("--");
2020                                 ReadComment ();
2021                                 break;
2022                         case '[':
2023                                 ReadChar ();
2024                                 Expect ("CDATA[");
2025                                 ReadCDATA ();
2026                                 break;
2027                         case 'D':
2028                                 Expect ("DOCTYPE");
2029                                 ReadDoctypeDecl ();
2030                                 break;
2031                         default:
2032                                 throw new XmlException (this as IXmlLineInfo,
2033                                         "Unexpected declaration markup was found.");
2034                         }
2035                 }
2036
2037                 // The reader is positioned on the first character after
2038                 // the leading '<!--'.
2039                 private void ReadComment ()
2040                 {
2041                         if (currentState == XmlNodeType.None)
2042                                 currentState = XmlNodeType.XmlDeclaration;
2043
2044                         ClearValueBuffer ();
2045
2046                         while (PeekChar () != -1) {
2047                                 int ch = ReadChar ();
2048
2049                                 if (ch == '-' && PeekChar () == '-') {
2050                                         ReadChar ();
2051
2052                                         if (PeekChar () != '>')
2053                                                 throw new XmlException (this as IXmlLineInfo,"comments cannot contain '--'");
2054
2055                                         ReadChar ();
2056                                         break;
2057                                 }
2058
2059                                 if (XmlChar.IsInvalid (ch))
2060                                         throw new XmlException (this as IXmlLineInfo,
2061                                                 "Not allowed character was found.");
2062
2063                                 AppendValueChar (ch);
2064                         }
2065
2066                         SetProperties (
2067                                 XmlNodeType.Comment, // nodeType
2068                                 String.Empty, // name
2069                                 false, // isEmptyElement
2070                                 null, // value: create only when required
2071                                 true // clearAttributes
2072                         );
2073                 }
2074
2075                 // The reader is positioned on the first character after
2076                 // the leading '<![CDATA['.
2077                 private void ReadCDATA ()
2078                 {
2079                         if (currentState != XmlNodeType.Element)
2080                                 throw new XmlException (this as IXmlLineInfo,
2081                                         "CDATA section cannot appear in this state.");
2082
2083                         ClearValueBuffer ();
2084
2085                         bool skip = false;
2086                         int ch = 0;
2087                         while (PeekChar () != -1) {
2088                                 if (!skip)
2089                                         ch = ReadChar ();
2090                                 skip = false;
2091
2092                                 if (ch == ']' && PeekChar () == ']') {
2093                                         ch = ReadChar (); // ']'
2094
2095                                         if (PeekChar () == '>') {
2096                                                 ReadChar (); // '>'
2097                                                 break;
2098                                         } else {
2099                                                 skip = true;
2100                                         }
2101                                 }
2102                                 if (normalization && XmlChar.IsInvalid (ch))
2103                                         throw new XmlException (this, "Invalid character was found.");
2104
2105                                 AppendValueChar (ch);
2106                         }
2107
2108                         SetProperties (
2109                                 XmlNodeType.CDATA, // nodeType
2110                                 String.Empty, // name
2111                                 false, // isEmptyElement
2112                                 null, // value: create only when required
2113                                 true // clearAttributes
2114                         );
2115                 }
2116
2117                 // The reader is positioned on the first character after
2118                 // the leading '<!DOCTYPE'.
2119                 private void ReadDoctypeDecl ()
2120                 {
2121                         switch (currentState) {
2122                         case XmlNodeType.DocumentType:
2123                         case XmlNodeType.Element:
2124                         case XmlNodeType.EndElement:
2125                                 throw new XmlException (this as IXmlLineInfo,
2126                                         "Document type cannot appear in this state.");
2127                         }
2128                         currentState = XmlNodeType.DocumentType;
2129
2130                         string doctypeName = null;
2131                         string publicId = null;
2132                         string systemId = null;
2133                         int intSubsetStartLine = 0;
2134                         int intSubsetStartColumn = 0;
2135
2136                         SkipWhitespace ();
2137                         doctypeName = ReadName ();
2138                         SkipWhitespace ();
2139                         switch(PeekChar ())
2140                         {
2141                         case 'S':
2142                                 systemId = ReadSystemLiteral (true);
2143                                 break;
2144                         case 'P':
2145                                 publicId = ReadPubidLiteral ();
2146                                 if (!SkipWhitespace ())
2147                                         throw new XmlException (this as IXmlLineInfo,
2148                                                 "Whitespace is required between PUBLIC id and SYSTEM id.");
2149                                 systemId = ReadSystemLiteral (false);
2150                                 break;
2151                         }
2152                         SkipWhitespace ();
2153
2154
2155                         if(PeekChar () == '[')
2156                         {
2157                                 // read markupdecl etc. or end of decl
2158                                 ReadChar ();
2159                                 intSubsetStartLine = this.LineNumber;
2160                                 intSubsetStartColumn = this.LinePosition;
2161                                 int startPos = currentTagLength;
2162                                 ReadInternalSubset ();
2163                                 int endPos = currentTagLength - 1;
2164                                 parserContext.InternalSubset = new string (currentTagBuffer, startPos, endPos - startPos);
2165                         }
2166                         // end of DOCTYPE decl.
2167                         ExpectAfterWhitespace ('>');
2168
2169                         GenerateDTDObjectModel (doctypeName, publicId,
2170                                 systemId, parserContext.InternalSubset,
2171                                 intSubsetStartLine, intSubsetStartColumn);
2172
2173                         // set properties for <!DOCTYPE> node
2174                         SetProperties (
2175                                 XmlNodeType.DocumentType, // nodeType
2176                                 doctypeName, // name
2177                                 false, // isEmptyElement
2178                                 parserContext.InternalSubset, // value
2179                                 true // clearAttributes
2180                                 );
2181
2182                         if (publicId != null)
2183                                 AddAttribute ("PUBLIC", publicId);
2184                         if (systemId != null)
2185                                 AddAttribute ("SYSTEM", systemId);
2186                         currentAttribute = currentAttributeValue = -1;
2187                 }
2188
2189                 internal DTDObjectModel GenerateDTDObjectModel (string name, string publicId,
2190                         string systemId, string internalSubset)
2191                 {
2192                         return GenerateDTDObjectModel (name, publicId, systemId, internalSubset, 0, 0);
2193                 }
2194
2195                 internal DTDObjectModel GenerateDTDObjectModel (string name, string publicId,
2196                         string systemId, string internalSubset, int intSubsetStartLine, int intSubsetStartColumn)
2197                 {
2198                         // now compile DTD
2199                         parserContext.Dtd = new DTDObjectModel (this.NameTable);        // merges both internal and external subsets in the meantime,
2200                         DTD.BaseURI = BaseURI;
2201                         DTD.Name = name;
2202                         DTD.PublicId = publicId;
2203                         DTD.SystemId = systemId;
2204                         DTD.InternalSubset = internalSubset;
2205                         DTD.XmlResolver = resolver;
2206                         DTD.IsStandalone = isStandalone;
2207                         DTD.LineNumber = line;
2208                         DTD.LinePosition = column;
2209
2210                         DTDReader dr = new DTDReader (DTD, intSubsetStartLine, intSubsetStartColumn);
2211                         dr.Normalization = this.normalization;
2212 #if DTD_HANDLE_EVENTS
2213                         dr.ValidationEventHandler += new ValidationEventHandler (OnValidationEvent);
2214 #endif
2215                         return dr.GenerateDTDObjectModel ();
2216                 }
2217
2218                 private void OnValidationEvent (object o, ValidationEventArgs e)
2219                 {
2220 #if DTD_HANDLE_EVENTS
2221                         if (ValidationEventHandler != null)
2222                                 // Override object as this.
2223                                 ValidationEventHandler (this, e);
2224 #endif
2225                 }
2226
2227                 private enum DtdInputState
2228                 {
2229                         Free = 1,
2230                         ElementDecl,
2231                         AttlistDecl,
2232                         EntityDecl,
2233                         NotationDecl,
2234                         PI,
2235                         Comment,
2236                         InsideSingleQuoted,
2237                         InsideDoubleQuoted,
2238                 }
2239
2240                 private class DtdInputStateStack
2241                 {
2242                         Stack intern = new Stack ();
2243                         public DtdInputStateStack ()
2244                         {
2245                                 Push (DtdInputState.Free);
2246                         }
2247
2248                         public DtdInputState Peek ()
2249                         {
2250                                 return (DtdInputState) intern.Peek ();
2251                         }
2252
2253                         public DtdInputState Pop ()
2254                         {
2255                                 return (DtdInputState) intern.Pop ();
2256                         }
2257
2258                         public void Push (DtdInputState val)
2259                         {
2260                                 intern.Push (val);
2261                         }
2262                 }
2263
2264
2265                 DtdInputStateStack stateStack = new DtdInputStateStack ();
2266                 DtdInputState State {
2267                         get { return stateStack.Peek (); }
2268                 }
2269
2270                 // Simply read but not generate any result.
2271                 private void ReadInternalSubset ()
2272                 {
2273                         bool continueParse = true;
2274
2275                         while (continueParse) {
2276                                 switch (ReadChar ()) {
2277                                 case ']':
2278                                         switch (State) {
2279                                         case DtdInputState.Free:
2280                                                 continueParse = false;
2281                                                 break;
2282                                         case DtdInputState.InsideDoubleQuoted:
2283                                                 continue;
2284                                         case DtdInputState.InsideSingleQuoted:
2285                                                 continue;
2286                                         default:
2287                                                 throw new XmlException (this as IXmlLineInfo,"unexpected end of file at DTD.");
2288                                         }
2289                                         break;
2290                                 case -1:
2291                                         throw new XmlException (this as IXmlLineInfo,"unexpected end of file at DTD.");
2292                                 case '<':
2293                                         if (State == DtdInputState.InsideDoubleQuoted ||
2294                                                 State == DtdInputState.InsideSingleQuoted)
2295                                                 continue;       // well-formed
2296                                         switch (ReadChar ()) {
2297                                         case '?':
2298                                                 stateStack.Push (DtdInputState.PI);
2299                                                 break;
2300                                         case '!':
2301                                                 switch (ReadChar ()) {
2302                                                 case 'E':
2303                                                         switch (ReadChar ()) {
2304                                                         case 'L':
2305                                                                 Expect ("EMENT");
2306                                                                 stateStack.Push (DtdInputState.ElementDecl);
2307                                                                 break;
2308                                                         case 'N':
2309                                                                 Expect ("TITY");
2310                                                                 stateStack.Push (DtdInputState.EntityDecl);
2311                                                                 break;
2312                                                         default:
2313                                                                 throw new XmlException (this as IXmlLineInfo,"unexpected token '<!E'.");
2314                                                         }
2315                                                         break;
2316                                                 case 'A':
2317                                                         Expect ("TTLIST");
2318                                                         stateStack.Push (DtdInputState.AttlistDecl);
2319                                                         break;
2320                                                 case 'N':
2321                                                         Expect ("OTATION");
2322                                                         stateStack.Push (DtdInputState.NotationDecl);
2323                                                         break;
2324                                                 case '-':
2325                                                         Expect ("-");
2326                                                         stateStack.Push (DtdInputState.Comment);
2327                                                         break;
2328                                                 }
2329                                                 break;
2330                                         default:
2331                                                 throw new XmlException (this as IXmlLineInfo,"unexpected '>'.");
2332                                         }
2333                                         break;
2334                                 case '\'':
2335                                         if (State == DtdInputState.InsideSingleQuoted)
2336                                                 stateStack.Pop ();
2337                                         else if (State != DtdInputState.InsideDoubleQuoted && State != DtdInputState.Comment)
2338                                                 stateStack.Push (DtdInputState.InsideSingleQuoted);
2339                                         break;
2340                                 case '"':
2341                                         if (State == DtdInputState.InsideDoubleQuoted)
2342                                                 stateStack.Pop ();
2343                                         else if (State != DtdInputState.InsideSingleQuoted && State != DtdInputState.Comment)
2344                                                 stateStack.Push (DtdInputState.InsideDoubleQuoted);
2345                                         break;
2346                                 case '>':
2347                                         switch (State) {
2348                                         case DtdInputState.ElementDecl:
2349                                                 goto case DtdInputState.NotationDecl;
2350                                         case DtdInputState.AttlistDecl:
2351                                                 goto case DtdInputState.NotationDecl;
2352                                         case DtdInputState.EntityDecl:
2353                                                 goto case DtdInputState.NotationDecl;
2354                                         case DtdInputState.NotationDecl:
2355                                                 stateStack.Pop ();
2356                                                 break;
2357                                         case DtdInputState.InsideDoubleQuoted:
2358                                                 continue;
2359                                         case DtdInputState.InsideSingleQuoted:
2360                                                 continue; // well-formed
2361                                         case DtdInputState.Comment:
2362                                                 continue;
2363                                         default:
2364                                                 throw new XmlException (this as IXmlLineInfo,"unexpected token '>'");
2365                                         }
2366                                         break;
2367                                 case '?':
2368                                         if (State == DtdInputState.PI) {
2369                                                 if (ReadChar () == '>')
2370                                                         stateStack.Pop ();
2371                                         }
2372                                         break;
2373                                 case '-':
2374                                         if (State == DtdInputState.Comment) {
2375                                                 if (PeekChar () == '-') {
2376                                                         ReadChar ();
2377                                                         Expect ('>');
2378                                                         stateStack.Pop ();
2379                                                 }
2380                                         }
2381                                         break;
2382                                 case '%':
2383                                         if (State != DtdInputState.Free && State != DtdInputState.EntityDecl && State != DtdInputState.Comment && State != DtdInputState.InsideDoubleQuoted && State != DtdInputState.InsideSingleQuoted)
2384                                                 throw new XmlException (this as IXmlLineInfo,"Parameter Entity Reference cannot appear as a part of markupdecl (see XML spec 2.8).");
2385                                         break;
2386                                 }
2387                         }
2388                 }
2389
2390                 // The reader is positioned on the first 'S' of "SYSTEM".
2391                 private string ReadSystemLiteral (bool expectSYSTEM)
2392                 {
2393                         if(expectSYSTEM) {
2394                                 Expect ("SYSTEM");
2395                                 if (!SkipWhitespace ())
2396                                         throw new XmlException (this as IXmlLineInfo,
2397                                                 "Whitespace is required after 'SYSTEM'.");
2398                         }
2399                         else
2400                                 SkipWhitespace ();
2401                         int quoteChar = ReadChar ();    // apos or quot
2402                         int startPos = currentTagLength;
2403                         int c = 0;
2404                         ClearValueBuffer ();
2405                         while (c != quoteChar) {
2406                                 c = ReadChar ();
2407                                 if (c < 0)
2408                                         throw new XmlException (this as IXmlLineInfo,"Unexpected end of stream in ExternalID.");
2409                                 if (c != quoteChar)
2410                                         AppendValueChar (c);
2411                         }
2412                         return CreateValueString ();
2413                 }
2414
2415                 private string ReadPubidLiteral()
2416                 {
2417                         Expect ("PUBLIC");
2418                         if (!SkipWhitespace ())
2419                                 throw new XmlException (this as IXmlLineInfo,
2420                                         "Whitespace is required after 'PUBLIC'.");
2421                         int quoteChar = ReadChar ();
2422                         int startPos = currentTagLength;
2423                         int c = 0;
2424                         ClearValueBuffer ();
2425                         while(c != quoteChar)
2426                         {
2427                                 c = ReadChar ();
2428                                 if(c < 0) throw new XmlException (this as IXmlLineInfo,"Unexpected end of stream in ExternalID.");
2429                                 if(c != quoteChar && !XmlChar.IsPubidChar (c))
2430                                         throw new XmlException (this as IXmlLineInfo,"character '" + (char) c + "' not allowed for PUBLIC ID");
2431                                 if (c != quoteChar)
2432                                         AppendValueChar (c);
2433                         }
2434                         return CreateValueString ();
2435                 }
2436
2437                 // The reader is positioned on the first character
2438                 // of the name.
2439                 private string ReadName ()
2440                 {
2441                         int ch = PeekChar ();
2442                         if (!XmlChar.IsFirstNameChar (ch))
2443                                 throw new XmlException (this as IXmlLineInfo,String.Format ("a name did not start with a legal character {0} ({1})", ch, (char) ch));
2444
2445                         nameLength = 0;
2446
2447                         AppendNameChar (ReadChar ());
2448
2449                         while (XmlChar.IsNameChar (PeekChar ())) {
2450                                 AppendNameChar (ReadChar ());
2451                         }
2452
2453                         return CreateNameString ();
2454                 }
2455
2456                 // Read the next character and compare it against the
2457                 // specified character.
2458                 private void Expect (int expected)
2459                 {
2460                         int ch = ReadChar ();
2461
2462                         if (ch != expected) {
2463                                 throw new XmlException (this as IXmlLineInfo,
2464                                         String.Format (
2465                                                 "expected '{0}' ({1:X}) but found '{2}' ({3:X})",
2466                                                 (char) expected,
2467                                                 expected,
2468                                                 (char) ch,
2469                                                 ch));
2470                         }
2471                 }
2472
2473                 private void Expect (string expected)
2474                 {
2475                         int len = expected.Length;
2476                         for(int i=0; i< len; i++)
2477                                 Expect (expected[i]);
2478                 }
2479
2480                 private void ExpectAfterWhitespace (char c)
2481                 {
2482                         while (true) {
2483                                 int i = ReadChar ();
2484                                 if (i < 0x21 && XmlChar.IsWhitespace (i))
2485                                         continue;
2486                                 if (c != i)
2487                                         throw new XmlException (this, String.Join (String.Empty, new string [] {"Expected ", c.ToString (), ", but found " + (char) i, "[", i.ToString (), "]"}));
2488                                 break;
2489                         }
2490                 }
2491
2492                 // Does not consume the first non-whitespace character.
2493                 private bool SkipWhitespace ()
2494                 {
2495                         bool skipped = XmlChar.IsWhitespace (PeekChar ());
2496                         if (!skipped)
2497                                 return false;
2498                         while (XmlChar.IsWhitespace (PeekChar ()))
2499                                 ReadChar ();
2500                         return skipped;
2501                 }
2502
2503                 private void ReadWhitespace ()
2504                 {
2505                         if (currentState == XmlNodeType.None)
2506                                 currentState = XmlNodeType.XmlDeclaration;
2507
2508                         ClearValueBuffer ();
2509                         int ch = PeekChar ();
2510                         do {
2511                                 AppendValueChar (ReadChar ());
2512                         } while ((ch = PeekChar ()) != -1 && XmlChar.IsWhitespace (ch));
2513
2514                         if (currentState == XmlNodeType.Element && ch != -1 && ch != '<')
2515                                 ReadText (false);
2516                         else {
2517                                 XmlNodeType nodeType = (this.XmlSpace == XmlSpace.Preserve) ?
2518                                         XmlNodeType.SignificantWhitespace : XmlNodeType.Whitespace;
2519                                 SetProperties (nodeType,
2520                                                String.Empty,
2521                                                false,
2522                                                null, // value: create only when required
2523                                                true);
2524                         }
2525
2526                         return;
2527                 }
2528
2529                 private byte GetBase64Byte (char ch)
2530                 {
2531                         switch (ch) {
2532                         case '+':
2533                                 return 62;
2534                         case '/':
2535                                 return 63;
2536                         case '=':
2537                                 return 0;
2538                         default:
2539                                 if (ch >= 'A' && ch <= 'Z')
2540                                         return (byte) (ch - 'A');
2541                                 else if (ch >= 'a' && ch <= 'z')
2542                                         return (byte) (ch - 'a' + 26);
2543                                 else if (ch >= '0' && ch <= '9')
2544                                         return (byte) (ch - '0' + 52);
2545                                 else
2546                                         throw new XmlException ("Invalid Base64 character was found.");
2547                         }
2548                 }
2549
2550                 // Returns -1 if it should throw an error.
2551                 private int ReadCharsInternal (char [] buffer, int offset, int length)
2552                 {
2553                         if (IsEmptyElement) {
2554                                 Read ();
2555                                 return 0;
2556                         }
2557
2558                         shouldSkipUntilEndTag = true;
2559
2560                         if (offset < 0)
2561                                 throw new ArgumentOutOfRangeException ("offset", offset, "Offset must be non-negative integer.");
2562                         else if (length < 0)
2563                                 throw new ArgumentOutOfRangeException ("length", length, "Length must be non-negative integer.");
2564                         else if (buffer.Length < offset + length)
2565                                 throw new ArgumentOutOfRangeException ("buffer length is smaller than the sum of offset and length.");
2566
2567                         if (NodeType != XmlNodeType.Element)
2568                                 return 0;
2569
2570                         int bufIndex = offset;
2571                         for (int i = 0; i < length; i++) {
2572                                 int c = PeekChar ();
2573                                 switch (c) {
2574                                 case -1:
2575                                         throw new XmlException (this as IXmlLineInfo, "Unexpected end of xml.");
2576                                 case '<':
2577                                         ReadChar ();
2578                                         if (PeekChar () != '/') {
2579                                                 buffer [bufIndex++] = '<';
2580                                                 continue;
2581                                         }
2582                                         // Seems to skip immediate EndElement
2583                                         Expect ('/');
2584                                         if (depthUp) {
2585                                                 depth++;
2586                                                 depthUp = false;
2587                                         }
2588                                         ReadEndTag();
2589                                         shouldSkipUntilEndTag = false;
2590                                         Read ();
2591                                         return i;
2592                                 default:
2593                                         ReadChar ();
2594                                         if (c < Char.MaxValue)
2595                                                 buffer [bufIndex++] = (char) c;
2596                                         else {
2597                                                 buffer [bufIndex++] = (char) (c / 0x10000 + 0xD800 - 1);
2598                                                 buffer [bufIndex++] = (char) (c % 0x10000 + 0xDC00);
2599                                         }
2600                                         break;
2601                                 }
2602                         }
2603                         return length;
2604                 }
2605
2606                 private bool ReadUntilEndTag ()
2607                 {
2608                         int ch;
2609                         do {
2610                                 ch = ReadChar ();
2611                                 switch (ch) {
2612                                 case -1:
2613                                         throw new XmlException (this as IXmlLineInfo,
2614                                                 "Unexpected end of xml.");
2615                                 case '<':
2616                                         if (PeekChar () != '/')
2617                                                 continue;
2618                                         ReadChar ();
2619                                         string name = ReadName ();
2620                                         if (name != elementNames [elementNameStackPos - 1])
2621                                                 continue;
2622                                         Expect ('>');
2623                                         depth--;
2624                                         elementNames [--elementNameStackPos] = null;
2625                                         return Read ();
2626                                 }
2627                         } while (true);
2628                 }
2629                 #endregion
2630         }
2631 }