2004-05-30 Atsushi Enomoto <atsushi@ximian.com>
[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                                         "Element cannot appear in this state.");
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                         string baseUri = GetAttribute ("xml:base");
1293                         if (baseUri != null) {
1294                                 if (this.resolver != null)
1295                                         parserContext.BaseURI = resolver.ResolveUri (new Uri (BaseURI), baseUri).ToString ();
1296                                 else
1297                                         parserContext.BaseURI = baseUri;
1298                         }
1299                         string xmlLang = GetAttribute ("xml:lang");
1300                         if (xmlLang != null)
1301                                 parserContext.XmlLang = xmlLang;
1302                         string xmlSpaceAttr = GetAttribute ("xml:space");
1303                         if (xmlSpaceAttr != null) {
1304                                 if (xmlSpaceAttr == "preserve")
1305                                         parserContext.XmlSpace = XmlSpace.Preserve;
1306                                 else if (xmlSpaceAttr == "default")
1307                                         parserContext.XmlSpace = XmlSpace.Default;
1308                                 else
1309                                         throw new XmlException (this as IXmlLineInfo,String.Format ("Invalid xml:space value: {0}", xmlSpaceAttr));
1310                         }
1311                         if (PeekChar () == '/') {
1312                                 ReadChar ();
1313                                 isEmptyElement = true;
1314                                 popScope = true;
1315                         }
1316                         else {
1317                                 depthUp = true;
1318                                 PushElementName (name);
1319                                 parserContext.PushScope ();
1320                         }
1321
1322                         Expect ('>');
1323                         SetProperties (
1324                                 XmlNodeType.Element, // nodeType
1325                                 name, // name
1326                                 isEmptyElement, // isEmptyElement
1327                                 null, // value
1328                                 false // clearAttributes
1329                         );
1330
1331                         if (IsEmptyElement)
1332                                 CheckCurrentStateUpdate ();
1333                 }
1334
1335                 private void PushElementName (string name)
1336                 {
1337                         if (elementNames.Length == elementNameStackPos) {
1338                                 string [] newArray = new string [elementNames.Length * 2];
1339                                 Array.Copy (elementNames, 0, newArray, 0, elementNameStackPos);
1340                                 elementNames = newArray;
1341                         }
1342                         elementNames [elementNameStackPos++] = name;
1343                 }
1344
1345                 // The reader is positioned on the first character
1346                 // of the element's name.
1347                 private void ReadEndTag ()
1348                 {
1349                         if (currentState != XmlNodeType.Element)
1350                                 throw new XmlException (this as IXmlLineInfo,
1351                                         "End tag cannot appear in this state.");
1352
1353                         string name = ReadName ();
1354                         if (elementNameStackPos == 0)
1355                                 throw new XmlException (this as IXmlLineInfo,"closing element without matching opening element");
1356                         string expected = elementNames [--elementNameStackPos];
1357                         if (expected != name)
1358                                 throw new XmlException (this as IXmlLineInfo,String.Format ("unmatched closing element: expected {0} but found {1}", expected, name));
1359                         parserContext.PopScope ();
1360
1361                         ExpectAfterWhitespace ('>');
1362
1363                         --depth;
1364
1365                         SetProperties (
1366                                 XmlNodeType.EndElement, // nodeType
1367                                 name, // name
1368                                 false, // isEmptyElement
1369                                 null, // value
1370                                 true // clearAttributes
1371                         );
1372
1373                         popScope = true;
1374
1375                         CheckCurrentStateUpdate ();
1376                 }
1377
1378                 private void CheckCurrentStateUpdate ()
1379                 {
1380                         if (depth == 0 && !allowMultipleRoot && (IsEmptyElement || NodeType == XmlNodeType.EndElement))
1381                                 currentState = XmlNodeType.EndElement;
1382                 }
1383
1384                 private void AppendNameChar (int ch)
1385                 {
1386                         if (nameLength == nameCapacity)
1387                                 ExpandNameCapacity ();
1388                         if (ch < Char.MaxValue)
1389                                 nameBuffer [nameLength++] = (char) ch;
1390                         else {
1391                                 nameBuffer [nameLength++] = (char) (ch / 0x10000 + 0xD800 - 1);
1392                                 if (nameLength == nameCapacity)
1393                                         ExpandNameCapacity ();
1394                                 nameBuffer [nameLength++] = (char) (ch % 0x10000 + 0xDC00);
1395                         }
1396                 }
1397
1398                 private void ExpandNameCapacity ()
1399                 {
1400                         nameCapacity = nameCapacity * 2;
1401                         char [] oldNameBuffer = nameBuffer;
1402                         nameBuffer = new char [nameCapacity];
1403                         Array.Copy (oldNameBuffer, nameBuffer, nameLength);
1404                 }
1405
1406                 private string CreateNameString ()
1407                 {
1408                         return parserContext.NameTable.Add (nameBuffer, 0, nameLength);
1409                 }
1410
1411                 private void AppendValueChar (int ch)
1412                 {
1413                         if (valueLength == valueCapacity)
1414                                 ExpandValueCapacity ();
1415                         if (ch < Char.MaxValue)
1416                                 valueBuffer [valueLength++] = (char) ch;
1417                         else {
1418                                 valueBuffer [valueLength++] = (char) (ch / 0x10000 + 0xD800 - 1);
1419                                 if (valueLength == valueCapacity)
1420                                         ExpandValueCapacity ();
1421                                 valueBuffer [valueLength++] = (char) (ch % 0x10000 + 0xDC00);
1422                         }
1423                 }
1424
1425                 private void ExpandValueCapacity ()
1426                 {
1427                         valueCapacity = valueCapacity * 2;
1428                         char [] oldValueBuffer = valueBuffer;
1429                         valueBuffer = new char [valueCapacity];
1430                         Array.Copy (oldValueBuffer, valueBuffer, valueLength);
1431                 }
1432
1433                 private string CreateValueString ()
1434                 {
1435                         return new string (valueBuffer, 0, valueLength);
1436                 }
1437
1438                 private void ClearValueBuffer ()
1439                 {
1440                         valueLength = 0;
1441                 }
1442
1443                 private void AppendCurrentTagChar (int ch)
1444                 {
1445                         if (currentTagLength == currentTagCapacity)
1446                                 ExpandCurrentTagCapacity ();
1447                         if (ch < Char.MaxValue)
1448                                 currentTagBuffer [currentTagLength++] = (char) ch;
1449                         else {
1450                                 currentTagBuffer [currentTagLength++] = (char) (ch / 0x10000 + 0xD800 - 1);
1451                                 if (currentTagLength == currentTagCapacity)
1452                                         ExpandCurrentTagCapacity ();
1453                                 currentTagBuffer [currentTagLength++] = (char) (ch % 0x10000 + 0xDC00);
1454                         }
1455                 }
1456
1457                 private void ExpandCurrentTagCapacity ()
1458                 {
1459                         currentTagCapacity = currentTagCapacity * 2;
1460                         char [] oldCurrentTagBuffer = currentTagBuffer;
1461                         currentTagBuffer = new char [currentTagCapacity];
1462                         Array.Copy (oldCurrentTagBuffer, currentTagBuffer, currentTagLength);
1463                 }
1464
1465                 private string CreateCurrentTagString ()
1466                 {
1467                         return new string (currentTagBuffer, 0, currentTagLength);
1468                 }
1469
1470                 private void ClearCurrentTagBuffer ()
1471                 {
1472                         currentTagLength = 0;
1473                 }
1474
1475                 // The reader is positioned on the first character
1476                 // of the text.
1477                 private void ReadText (bool notWhitespace)
1478                 {
1479                         if (currentState != XmlNodeType.Element)
1480                                 throw new XmlException (this as IXmlLineInfo,
1481                                         "Text node cannot appear in this state.");
1482
1483                         if (notWhitespace)
1484                                 ClearValueBuffer ();
1485
1486                         int ch = PeekChar ();
1487                         bool previousWasCloseBracket = false;
1488
1489                         while (ch != '<' && ch != -1) {
1490                                 if (ch == '&') {
1491                                         ReadChar ();
1492                                         ch = ReadReference (false);
1493                                         if (returnEntityReference) // Returns -1 if char validation should not be done
1494                                                 break;
1495                                 }
1496                                 else
1497                                         ch = ReadChar ();
1498
1499                                 if (normalization && XmlChar.IsInvalid (ch))
1500                                         throw new XmlException (this, "Not allowed character was found.");
1501                                 AppendValueChar (ch);
1502
1503                                 // Block "]]>"
1504                                 if (ch == ']') {
1505                                         if (previousWasCloseBracket)
1506                                                 if (PeekChar () == '>')
1507                                                         throw new XmlException (this as IXmlLineInfo,
1508                                                                 "Inside text content, character sequence ']]>' is not allowed.");
1509                                         previousWasCloseBracket = true;
1510                                 }
1511                                 else if (previousWasCloseBracket)
1512                                         previousWasCloseBracket = false;
1513                                 ch = PeekChar ();
1514                                 notWhitespace = true;
1515                         }
1516
1517                         if (returnEntityReference && valueLength == 0) {
1518                                 SetEntityReferenceProperties ();
1519                         } else {
1520                                 XmlNodeType nodeType = notWhitespace ? XmlNodeType.Text :
1521                                         this.XmlSpace == XmlSpace.Preserve ? XmlNodeType.SignificantWhitespace : XmlNodeType.Whitespace;
1522                                 SetProperties (
1523                                         nodeType, // nodeType
1524                                         String.Empty, // name
1525                                         false, // isEmptyElement
1526                                         null, // value: create only when required
1527                                         true // clearAttributes
1528                                 );
1529                         }
1530                 }
1531
1532                 // The leading '&' has already been consumed.
1533                 // Returns true if the entity reference isn't a simple
1534                 // character reference or one of the predefined entities.
1535                 // This allows the ReadText method to break so that the
1536                 // next call to Read will return the EntityReference node.
1537                 private int ReadReference (bool ignoreEntityReferences)
1538                 {
1539                         if (PeekChar () == '#') {
1540                                 ReadChar ();
1541                                 return ReadCharacterReference ();
1542                         } else
1543                                 return ReadEntityReference (ignoreEntityReferences);
1544                 }
1545
1546                 private int ReadCharacterReference ()
1547                 {
1548                         int value = 0;
1549
1550                         if (PeekChar () == 'x') {
1551                                 ReadChar ();
1552
1553                                 while (PeekChar () != ';' && PeekChar () != -1) {
1554                                         int ch = ReadChar ();
1555
1556                                         if (ch >= '0' && ch <= '9')
1557                                                 value = (value << 4) + ch - '0';
1558                                         else if (ch >= 'A' && ch <= 'F')
1559                                                 value = (value << 4) + ch - 'A' + 10;
1560                                         else if (ch >= 'a' && ch <= 'f')
1561                                                 value = (value << 4) + ch - 'a' + 10;
1562                                         else
1563                                                 throw new XmlException (this as IXmlLineInfo,
1564                                                         String.Format (
1565                                                                 "invalid hexadecimal digit: {0} (#x{1:X})",
1566                                                                 (char) ch,
1567                                                                 ch));
1568                                 }
1569                         } else {
1570                                 while (PeekChar () != ';' && PeekChar () != -1) {
1571                                         int ch = ReadChar ();
1572
1573                                         if (ch >= '0' && ch <= '9')
1574                                                 value = value * 10 + ch - '0';
1575                                         else
1576                                                 throw new XmlException (this as IXmlLineInfo,
1577                                                         String.Format (
1578                                                                 "invalid decimal digit: {0} (#x{1:X})",
1579                                                                 (char) ch,
1580                                                                 ch));
1581                                 }
1582                         }
1583
1584                         ReadChar (); // ';'
1585
1586                         // There is no way to save surrogate pairs...
1587                         if (normalization && XmlChar.IsInvalid (value))
1588                                 throw new XmlException (this as IXmlLineInfo,
1589                                         "Referenced character was not allowed in XML.");
1590                         return value;
1591                 }
1592
1593                 // Returns -1 if it should not be validated.
1594                 // Real EOF must not be detected here.
1595                 private int ReadEntityReference (bool ignoreEntityReferences)
1596                 {
1597                         string name = ReadName ();
1598                         Expect (';');
1599
1600                         int predefined = XmlChar.GetPredefinedEntity (name);
1601                         if (predefined >= 0)
1602                                 return predefined;
1603                         else {
1604                                 if (ignoreEntityReferences) {
1605                                         AppendValueChar ('&');
1606                                         for (int i = 0; i < name.Length; i++)
1607                                                 AppendValueChar (name [i]);
1608                                         AppendValueChar (';');
1609                                 } else {
1610                                         returnEntityReference = true;
1611                                         entityReferenceName = name;
1612                                 }
1613                         }
1614                         return -1;
1615                 }
1616
1617                 // The reader is positioned on the first character of
1618                 // the attribute name.
1619                 private void ReadAttributes (bool isXmlDecl)
1620                 {
1621                         int peekChar = -1;
1622                         bool requireWhitespace = false;
1623                         currentAttribute = -1;
1624                         currentAttributeValue = -1;
1625
1626                         do {
1627                                 if (!SkipWhitespace () && requireWhitespace)
1628                                         throw new XmlException ("Unexpected token. Name is required here.");
1629
1630                                 IncrementAttributeToken ();
1631                                 currentAttributeToken.LineNumber = line;
1632                                 currentAttributeToken.LinePosition = column;
1633
1634                                 currentAttributeToken.LocalName = 
1635                                         currentAttributeToken.Name = ReadName ();
1636                                 ExpectAfterWhitespace ('=');
1637                                 SkipWhitespace ();
1638                                 ReadAttributeValueTokens (-1);
1639                                 attributeCount++;
1640
1641                                 if (currentAttributeToken.Name == "xmlns")
1642                                         parserContext.NamespaceManager.AddNamespace (String.Empty, GetAttribute (currentAttribute));
1643                                 else if (currentAttributeToken.Name.StartsWith ("xmlns:")) {
1644                                         string nsPrefix = currentAttributeToken.Name.Substring (6);
1645                                         parserContext.NamespaceManager.AddNamespace (nsPrefix, GetAttribute (currentAttribute));
1646                                 }
1647
1648                                 if (!SkipWhitespace ())
1649                                         requireWhitespace = true;
1650                                 peekChar = PeekChar ();
1651                                 if (isXmlDecl) {
1652                                         if (peekChar == '?')
1653                                                 break;
1654                                 }
1655                                 else if (peekChar == '/' || peekChar == '>')
1656                                         break;
1657                         } while (peekChar != -1);
1658
1659                         currentAttribute = -1;
1660                         currentAttributeValue = -1;
1661                 }
1662
1663                 private void AddAttribute (string name, string value)
1664                 {
1665                         IncrementAttributeToken ();
1666                         XmlAttributeTokenInfo ati = attributeTokens [currentAttribute];
1667                         ati.Name = "SYSTEM";
1668                         ati.FillNames ();
1669                         IncrementAttributeValueToken ();
1670                         XmlTokenInfo vti = attributeValueTokens [currentAttributeValue];
1671                         vti.Value = value;
1672                         SetProperties (vti, XmlNodeType.Text, String.Empty, false, value, false);
1673                         attributeCount++;
1674                 }
1675
1676                 private void IncrementAttributeToken ()
1677                 {
1678                         currentAttribute++;
1679                         if (attributeTokens.Length == currentAttribute) {
1680                                 XmlAttributeTokenInfo [] newArray = 
1681                                         new XmlAttributeTokenInfo [attributeTokens.Length * 2];
1682                                 attributeTokens.CopyTo (newArray, 0);
1683                                 attributeTokens = newArray;
1684                         }
1685                         if (attributeTokens [currentAttribute] == null)
1686                                 attributeTokens [currentAttribute] = new XmlAttributeTokenInfo (this);
1687                         currentAttributeToken = attributeTokens [currentAttribute];
1688                         currentAttributeToken.Clear ();
1689                 }
1690
1691                 private void IncrementAttributeValueToken ()
1692                 {
1693                         ClearValueBuffer ();
1694                         currentAttributeValue++;
1695                         if (attributeValueTokens.Length == currentAttributeValue) {
1696                                 XmlTokenInfo [] newArray = new XmlTokenInfo [attributeValueTokens.Length * 2];
1697                                 attributeValueTokens.CopyTo (newArray, 0);
1698                                 attributeValueTokens = newArray;
1699                         }
1700                         if (attributeValueTokens [currentAttributeValue] == null)
1701                                 attributeValueTokens [currentAttributeValue] = new XmlTokenInfo (this, false);
1702                         currentAttributeValueToken = attributeValueTokens [currentAttributeValue];
1703                         currentAttributeValueToken.Clear ();
1704                 }
1705
1706                 // LAMESPEC: Orthodox XML reader should normalize attribute values
1707                 private void ReadAttributeValueTokens (int dummyQuoteChar)
1708                 {
1709                         int quoteChar = (dummyQuoteChar < 0) ? ReadChar () : dummyQuoteChar;
1710
1711                         if (quoteChar != '\'' && quoteChar != '\"')
1712                                 throw new XmlException (this as IXmlLineInfo,"an attribute value was not quoted");
1713                         currentAttributeToken.QuoteChar = (char) quoteChar;
1714
1715                         IncrementAttributeValueToken ();
1716                         currentAttributeToken.ValueTokenStartIndex = currentAttributeValue;
1717                         currentAttributeValueToken.LineNumber = line;
1718                         currentAttributeValueToken.LinePosition = column;
1719
1720                         bool incrementToken = false;
1721                         bool isNewToken = true;
1722                         bool loop = true;
1723                         int ch = 0;
1724                         while (loop) {
1725                                 ch = ReadChar ();
1726                                 if (ch == quoteChar)
1727                                         break;
1728
1729                                 if (incrementToken) {
1730                                         IncrementAttributeValueToken ();
1731                                         currentAttributeValueToken.LineNumber = line;
1732                                         currentAttributeValueToken.LinePosition = column;
1733                                         incrementToken = false;
1734                                         isNewToken = true;
1735                                 }
1736
1737                                 switch (ch)
1738                                 {
1739                                 case '<':
1740                                         throw new XmlException (this as IXmlLineInfo,"attribute values cannot contain '<'");
1741                                 case -1:
1742                                         if (dummyQuoteChar < 0)
1743                                                 throw new XmlException (this as IXmlLineInfo,"unexpected end of file in an attribute value");
1744                                         else    // Attribute value constructor.
1745                                                 loop = false;
1746                                         break;
1747                                 case '&':
1748                                         int startPosition = currentTagLength - 1;
1749                                         if (PeekChar () == '#') {
1750                                                 ReadChar ();
1751                                                 ch = ReadCharacterReference ();
1752                                                 if (normalization && XmlChar.IsInvalid (ch))
1753                                                         throw new XmlException (this as IXmlLineInfo,
1754                                                                 "Not allowed character was found.");
1755                                                 AppendValueChar (ch);
1756                                                 break;
1757                                         }
1758                                         // Check XML 1.0 section 3.1 WFC.
1759                                         string entName = ReadName ();
1760                                         Expect (';');
1761                                         int predefined = XmlChar.GetPredefinedEntity (entName);
1762                                         if (predefined < 0) {
1763                                                 CheckAttributeEntityReferenceWFC (entName);
1764                                                 currentAttributeValueToken.Value = CreateValueString ();
1765                                                 currentAttributeValueToken.NodeType = XmlNodeType.Text;
1766                                                 if (!isNewToken)
1767                                                         IncrementAttributeValueToken ();
1768                                                 currentAttributeValueToken.Name = entName;
1769                                                 currentAttributeValueToken.Value = String.Empty;
1770                                                 currentAttributeValueToken.NodeType = XmlNodeType.EntityReference;
1771                                                 incrementToken = true;
1772                                         }
1773                                         else
1774                                                 AppendValueChar (predefined);
1775                                         break;
1776                                 default:
1777                                         if (normalization && XmlChar.IsInvalid (ch))
1778                                                 throw new XmlException (this, "Invalid character was found.");
1779                                         AppendValueChar (ch);
1780                                         break;
1781                                 }
1782
1783                                 isNewToken = false;
1784                         }
1785                         if (!incrementToken) {
1786                                 currentAttributeValueToken.Value = CreateValueString ();
1787                                 currentAttributeValueToken.NodeType = XmlNodeType.Text;
1788                         }
1789                         currentAttributeToken.ValueTokenEndIndex = currentAttributeValue;
1790
1791                 }
1792
1793                 private void CheckAttributeEntityReferenceWFC (string entName)
1794                 {
1795                         DTDEntityDeclaration entDecl = 
1796                                 DTD == null ? null : DTD.EntityDecls [entName];
1797                         if (DTD != null && resolver != null && entDecl == null)
1798                                 throw new XmlException (this, "Referenced entity does not exist.");
1799
1800                         if (entDecl == null)
1801                                 return;
1802
1803                         if (entDecl.HasExternalReference)
1804                                 throw new XmlException (this, "Reference to external entities is not allowed in the value of an attribute.");
1805                         if (isStandalone && !entDecl.IsInternalSubset)
1806                                 throw new XmlException (this, "Reference to external entities is not allowed in the internal subset.");
1807                         if (entDecl.EntityValue.IndexOf ('<') >= 0)
1808                                 throw new XmlException (this, "Attribute must not contain character '<' either directly or indirectly by way of entity references.");
1809                 }
1810
1811                 // The reader is positioned on the first character
1812                 // of the target.
1813                 //
1814                 // It may be xml declaration or processing instruction.
1815                 private void ReadProcessingInstruction ()
1816                 {
1817                         string target = ReadName ();
1818                         if (target == "xml") {
1819                                 ReadXmlDeclaration ();
1820                                 return;
1821                         } else if (target.ToLower () == "xml")
1822                                 throw new XmlException (this as IXmlLineInfo,
1823                                         "Not allowed processing instruction name which starts with 'X', 'M', 'L' was found.");
1824
1825                         if (currentState == XmlNodeType.None)
1826                                 currentState = XmlNodeType.XmlDeclaration;
1827
1828                         if (!SkipWhitespace ())
1829                                 if (PeekChar () != '?')
1830                                         throw new XmlException (this as IXmlLineInfo,
1831                                                 "Invalid processing instruction name was found.");
1832
1833                         ClearValueBuffer ();
1834
1835                         while (PeekChar () != -1) {
1836                                 int ch = ReadChar ();
1837
1838                                 if (ch == '?' && PeekChar () == '>') {
1839                                         ReadChar ();
1840                                         break;
1841                                 }
1842
1843                                 if (normalization && XmlChar.IsInvalid (ch))
1844                                         throw new XmlException (this, "Invalid character was found.");
1845                                 AppendValueChar (ch);
1846                         }
1847
1848                         SetProperties (
1849                                 XmlNodeType.ProcessingInstruction, // nodeType
1850                                 target, // name
1851                                 false, // isEmptyElement
1852                                 null, // value: create only when required
1853                                 true // clearAttributes
1854                         );
1855                 }
1856
1857                 // The reader is positioned after "<?xml "
1858                 private void ReadXmlDeclaration ()
1859                 {
1860                         if (currentState != XmlNodeType.None) {
1861                                 throw new XmlException (this as IXmlLineInfo,
1862                                         "XML declaration cannot appear in this state.");
1863                         }
1864                         currentState = XmlNodeType.XmlDeclaration;
1865
1866                         ClearAttributes ();
1867
1868                         ReadAttributes (true);  // They must have "version."
1869                         string version = GetAttribute ("version");
1870
1871                         string message = null;
1872
1873                         if (attributeTokens [0].Name != "version" || version != "1.0")
1874                                 message = "Version 1.0 declaration is required in XML Declaration.";
1875                         else if (attributeCount > 1 &&
1876                                         (attributeTokens [1].Name != "encoding" &&
1877                                         attributeTokens [1].Name != "standalone"))
1878                                 message = "Invalid Xml Declaration markup was found.";
1879                         else if (attributeCount > 2 && attributeTokens [2].Name != "standalone")
1880                                 message = "Invalid Xml Declaration markup was found.";
1881                         string sa = GetAttribute ("standalone");
1882                         if (sa != null && sa != "yes" && sa != "no")
1883                                 message = "Only 'yes' or 'no' is allowed for standalone.";
1884
1885                         this.isStandalone = (sa == "yes");
1886
1887                         if (message != null)
1888                                 throw new XmlException (this as IXmlLineInfo, message);
1889
1890                         SetProperties (
1891                                 XmlNodeType.XmlDeclaration, // nodeType
1892                                 "xml", // name
1893                                 false, // isEmptyElement
1894                                 new string (currentTagBuffer, 6, currentTagLength - 6), // value
1895                                 false // clearAttributes
1896                         );
1897
1898                         Expect ("?>");
1899                 }
1900
1901                 internal void SkipTextDeclaration ()
1902                 {
1903                         this.currentState = XmlNodeType.Element;
1904
1905                         if (PeekChar () != '<')
1906                                 return;
1907
1908                         ReadChar ();
1909
1910                         if (PeekChar () != '?') {
1911                                 peekCharsIndex = 0;
1912                                 return;
1913                         }
1914                         ReadChar ();
1915
1916                         while (peekCharsIndex < 6) {
1917                                 if (PeekChar () < 0)
1918                                         break;
1919                                 else
1920                                         ReadChar ();
1921                         }
1922                         if (new string (peekChars, 2, 4) != "xml ") {
1923                                 if (new string (peekChars, 2, 3).ToLower () == "xml") {
1924                                         throw new XmlException (this as IXmlLineInfo,
1925                                                 "Processing instruction name must not be character sequence 'X' 'M' 'L' with case insensitivity.");
1926                                 }
1927                                 peekCharsIndex = 0;
1928                                 return;
1929                         }
1930
1931                         SkipWhitespace ();
1932
1933                         // version decl
1934                         if (PeekChar () == 'v') {
1935                                 Expect ("version");
1936                                 ExpectAfterWhitespace ('=');
1937                                 SkipWhitespace ();
1938                                 int quoteChar = ReadChar ();
1939                                 char [] expect1_0 = new char [3];
1940                                 int versionLength = 0;
1941                                 switch (quoteChar) {
1942                                 case '\'':
1943                                 case '"':
1944                                         while (PeekChar () != quoteChar) {
1945                                                 if (PeekChar () == -1)
1946                                                         throw new XmlException (this as IXmlLineInfo,
1947                                                                 "Invalid version declaration inside text declaration.");
1948                                                 else if (versionLength == 3)
1949                                                         throw new XmlException (this as IXmlLineInfo,
1950                                                                 "Invalid version number inside text declaration.");
1951                                                 else {
1952                                                         expect1_0 [versionLength] = (char) ReadChar ();
1953                                                         versionLength++;
1954                                                         if (versionLength == 3 && new String (expect1_0) != "1.0")
1955                                                                 throw new XmlException (this as IXmlLineInfo,
1956                                                                         "Invalid version number inside text declaration.");
1957                                                 }
1958                                         }
1959                                         ReadChar ();
1960                                         SkipWhitespace ();
1961                                         break;
1962                                 default:
1963                                         throw new XmlException (this as IXmlLineInfo,
1964                                                 "Invalid version declaration inside text declaration.");
1965                                 }
1966                         }
1967
1968                         if (PeekChar () == 'e') {
1969                                 Expect ("encoding");
1970                                 ExpectAfterWhitespace ('=');
1971                                 SkipWhitespace ();
1972                                 int quoteChar = ReadChar ();
1973                                 switch (quoteChar) {
1974                                 case '\'':
1975                                 case '"':
1976                                         while (PeekChar () != quoteChar)
1977                                                 if (ReadChar () == -1)
1978                                                         throw new XmlException (this as IXmlLineInfo,
1979                                                                 "Invalid encoding declaration inside text declaration.");
1980                                         ReadChar ();
1981                                         SkipWhitespace ();
1982                                         break;
1983                                 default:
1984                                         throw new XmlException (this as IXmlLineInfo,
1985                                                 "Invalid encoding declaration inside text declaration.");
1986                                 }
1987                                 // Encoding value should be checked inside XmlInputStream.
1988                         }
1989                         else
1990                                 throw new XmlException (this as IXmlLineInfo,
1991                                         "Encoding declaration is mandatory in text declaration.");
1992
1993                         Expect ("?>");
1994                 }
1995
1996                 // The reader is positioned on the first character after
1997                 // the leading '<!'.
1998                 private void ReadDeclaration ()
1999                 {
2000                         int ch = PeekChar ();
2001
2002                         switch (ch)
2003                         {
2004                         case '-':
2005                                 Expect ("--");
2006                                 ReadComment ();
2007                                 break;
2008                         case '[':
2009                                 ReadChar ();
2010                                 Expect ("CDATA[");
2011                                 ReadCDATA ();
2012                                 break;
2013                         case 'D':
2014                                 Expect ("DOCTYPE");
2015                                 ReadDoctypeDecl ();
2016                                 break;
2017                         default:
2018                                 throw new XmlException (this as IXmlLineInfo,
2019                                         "Unexpected declaration markup was found.");
2020                         }
2021                 }
2022
2023                 // The reader is positioned on the first character after
2024                 // the leading '<!--'.
2025                 private void ReadComment ()
2026                 {
2027                         if (currentState == XmlNodeType.None)
2028                                 currentState = XmlNodeType.XmlDeclaration;
2029
2030                         ClearValueBuffer ();
2031
2032                         while (PeekChar () != -1) {
2033                                 int ch = ReadChar ();
2034
2035                                 if (ch == '-' && PeekChar () == '-') {
2036                                         ReadChar ();
2037
2038                                         if (PeekChar () != '>')
2039                                                 throw new XmlException (this as IXmlLineInfo,"comments cannot contain '--'");
2040
2041                                         ReadChar ();
2042                                         break;
2043                                 }
2044
2045                                 if (XmlChar.IsInvalid (ch))
2046                                         throw new XmlException (this as IXmlLineInfo,
2047                                                 "Not allowed character was found.");
2048
2049                                 AppendValueChar (ch);
2050                         }
2051
2052                         SetProperties (
2053                                 XmlNodeType.Comment, // nodeType
2054                                 String.Empty, // name
2055                                 false, // isEmptyElement
2056                                 null, // value: create only when required
2057                                 true // clearAttributes
2058                         );
2059                 }
2060
2061                 // The reader is positioned on the first character after
2062                 // the leading '<![CDATA['.
2063                 private void ReadCDATA ()
2064                 {
2065                         if (currentState != XmlNodeType.Element)
2066                                 throw new XmlException (this as IXmlLineInfo,
2067                                         "CDATA section cannot appear in this state.");
2068
2069                         ClearValueBuffer ();
2070
2071                         bool skip = false;
2072                         int ch = 0;
2073                         while (PeekChar () != -1) {
2074                                 if (!skip)
2075                                         ch = ReadChar ();
2076                                 skip = false;
2077
2078                                 if (ch == ']' && PeekChar () == ']') {
2079                                         ch = ReadChar (); // ']'
2080
2081                                         if (PeekChar () == '>') {
2082                                                 ReadChar (); // '>'
2083                                                 break;
2084                                         } else {
2085                                                 skip = true;
2086                                         }
2087                                 }
2088                                 if (normalization && XmlChar.IsInvalid (ch))
2089                                         throw new XmlException (this, "Invalid character was found.");
2090
2091                                 AppendValueChar (ch);
2092                         }
2093
2094                         SetProperties (
2095                                 XmlNodeType.CDATA, // nodeType
2096                                 String.Empty, // name
2097                                 false, // isEmptyElement
2098                                 null, // value: create only when required
2099                                 true // clearAttributes
2100                         );
2101                 }
2102
2103                 // The reader is positioned on the first character after
2104                 // the leading '<!DOCTYPE'.
2105                 private void ReadDoctypeDecl ()
2106                 {
2107                         switch (currentState) {
2108                         case XmlNodeType.DocumentType:
2109                         case XmlNodeType.Element:
2110                         case XmlNodeType.EndElement:
2111                                 throw new XmlException (this as IXmlLineInfo,
2112                                         "Document type cannot appear in this state.");
2113                         }
2114                         currentState = XmlNodeType.DocumentType;
2115
2116                         string doctypeName = null;
2117                         string publicId = null;
2118                         string systemId = null;
2119                         int intSubsetStartLine = 0;
2120                         int intSubsetStartColumn = 0;
2121
2122                         SkipWhitespace ();
2123                         doctypeName = ReadName ();
2124                         SkipWhitespace ();
2125                         switch(PeekChar ())
2126                         {
2127                         case 'S':
2128                                 systemId = ReadSystemLiteral (true);
2129                                 break;
2130                         case 'P':
2131                                 publicId = ReadPubidLiteral ();
2132                                 if (!SkipWhitespace ())
2133                                         throw new XmlException (this as IXmlLineInfo,
2134                                                 "Whitespace is required between PUBLIC id and SYSTEM id.");
2135                                 systemId = ReadSystemLiteral (false);
2136                                 break;
2137                         }
2138                         SkipWhitespace ();
2139
2140
2141                         if(PeekChar () == '[')
2142                         {
2143                                 // read markupdecl etc. or end of decl
2144                                 ReadChar ();
2145                                 intSubsetStartLine = this.LineNumber;
2146                                 intSubsetStartColumn = this.LinePosition;
2147                                 int startPos = currentTagLength;
2148                                 ReadInternalSubset ();
2149                                 int endPos = currentTagLength - 1;
2150                                 parserContext.InternalSubset = new string (currentTagBuffer, startPos, endPos - startPos);
2151                         }
2152                         // end of DOCTYPE decl.
2153                         ExpectAfterWhitespace ('>');
2154
2155                         GenerateDTDObjectModel (doctypeName, publicId,
2156                                 systemId, parserContext.InternalSubset,
2157                                 intSubsetStartLine, intSubsetStartColumn);
2158
2159                         // set properties for <!DOCTYPE> node
2160                         SetProperties (
2161                                 XmlNodeType.DocumentType, // nodeType
2162                                 doctypeName, // name
2163                                 false, // isEmptyElement
2164                                 parserContext.InternalSubset, // value
2165                                 true // clearAttributes
2166                                 );
2167
2168                         if (publicId != null)
2169                                 AddAttribute ("PUBLIC", publicId);
2170                         if (systemId != null)
2171                                 AddAttribute ("SYSTEM", systemId);
2172                         currentAttribute = currentAttributeValue = -1;
2173                 }
2174
2175                 internal DTDObjectModel GenerateDTDObjectModel (string name, string publicId,
2176                         string systemId, string internalSubset)
2177                 {
2178                         return GenerateDTDObjectModel (name, publicId, systemId, internalSubset, 0, 0);
2179                 }
2180
2181                 internal DTDObjectModel GenerateDTDObjectModel (string name, string publicId,
2182                         string systemId, string internalSubset, int intSubsetStartLine, int intSubsetStartColumn)
2183                 {
2184                         // now compile DTD
2185                         parserContext.Dtd = new DTDObjectModel (this.NameTable);        // merges both internal and external subsets in the meantime,
2186                         DTD.BaseURI = BaseURI;
2187                         DTD.Name = name;
2188                         DTD.PublicId = publicId;
2189                         DTD.SystemId = systemId;
2190                         DTD.InternalSubset = internalSubset;
2191                         DTD.XmlResolver = resolver;
2192                         DTD.IsStandalone = isStandalone;
2193                         DTD.LineNumber = line;
2194                         DTD.LinePosition = column;
2195
2196                         DTDReader dr = new DTDReader (DTD, intSubsetStartLine, intSubsetStartColumn);
2197                         dr.Normalization = this.normalization;
2198 #if DTD_HANDLE_EVENTS
2199                         dr.ValidationEventHandler += new ValidationEventHandler (OnValidationEvent);
2200 #endif
2201                         return dr.GenerateDTDObjectModel ();
2202                 }
2203
2204                 private void OnValidationEvent (object o, ValidationEventArgs e)
2205                 {
2206 #if DTD_HANDLE_EVENTS
2207                         if (ValidationEventHandler != null)
2208                                 // Override object as this.
2209                                 ValidationEventHandler (this, e);
2210 #endif
2211                 }
2212
2213                 private enum DtdInputState
2214                 {
2215                         Free = 1,
2216                         ElementDecl,
2217                         AttlistDecl,
2218                         EntityDecl,
2219                         NotationDecl,
2220                         PI,
2221                         Comment,
2222                         InsideSingleQuoted,
2223                         InsideDoubleQuoted,
2224                 }
2225
2226                 private class DtdInputStateStack
2227                 {
2228                         Stack intern = new Stack ();
2229                         public DtdInputStateStack ()
2230                         {
2231                                 Push (DtdInputState.Free);
2232                         }
2233
2234                         public DtdInputState Peek ()
2235                         {
2236                                 return (DtdInputState) intern.Peek ();
2237                         }
2238
2239                         public DtdInputState Pop ()
2240                         {
2241                                 return (DtdInputState) intern.Pop ();
2242                         }
2243
2244                         public void Push (DtdInputState val)
2245                         {
2246                                 intern.Push (val);
2247                         }
2248                 }
2249
2250
2251                 DtdInputStateStack stateStack = new DtdInputStateStack ();
2252                 DtdInputState State {
2253                         get { return stateStack.Peek (); }
2254                 }
2255
2256                 // Simply read but not generate any result.
2257                 private void ReadInternalSubset ()
2258                 {
2259                         bool continueParse = true;
2260
2261                         while (continueParse) {
2262                                 switch (ReadChar ()) {
2263                                 case ']':
2264                                         switch (State) {
2265                                         case DtdInputState.Free:
2266                                                 continueParse = false;
2267                                                 break;
2268                                         case DtdInputState.InsideDoubleQuoted:
2269                                                 continue;
2270                                         case DtdInputState.InsideSingleQuoted:
2271                                                 continue;
2272                                         default:
2273                                                 throw new XmlException (this as IXmlLineInfo,"unexpected end of file at DTD.");
2274                                         }
2275                                         break;
2276                                 case -1:
2277                                         throw new XmlException (this as IXmlLineInfo,"unexpected end of file at DTD.");
2278                                 case '<':
2279                                         if (State == DtdInputState.InsideDoubleQuoted ||
2280                                                 State == DtdInputState.InsideSingleQuoted)
2281                                                 continue;       // well-formed
2282                                         switch (ReadChar ()) {
2283                                         case '?':
2284                                                 stateStack.Push (DtdInputState.PI);
2285                                                 break;
2286                                         case '!':
2287                                                 switch (ReadChar ()) {
2288                                                 case 'E':
2289                                                         switch (ReadChar ()) {
2290                                                         case 'L':
2291                                                                 Expect ("EMENT");
2292                                                                 stateStack.Push (DtdInputState.ElementDecl);
2293                                                                 break;
2294                                                         case 'N':
2295                                                                 Expect ("TITY");
2296                                                                 stateStack.Push (DtdInputState.EntityDecl);
2297                                                                 break;
2298                                                         default:
2299                                                                 throw new XmlException (this as IXmlLineInfo,"unexpected token '<!E'.");
2300                                                         }
2301                                                         break;
2302                                                 case 'A':
2303                                                         Expect ("TTLIST");
2304                                                         stateStack.Push (DtdInputState.AttlistDecl);
2305                                                         break;
2306                                                 case 'N':
2307                                                         Expect ("OTATION");
2308                                                         stateStack.Push (DtdInputState.NotationDecl);
2309                                                         break;
2310                                                 case '-':
2311                                                         Expect ("-");
2312                                                         stateStack.Push (DtdInputState.Comment);
2313                                                         break;
2314                                                 }
2315                                                 break;
2316                                         default:
2317                                                 throw new XmlException (this as IXmlLineInfo,"unexpected '>'.");
2318                                         }
2319                                         break;
2320                                 case '\'':
2321                                         if (State == DtdInputState.InsideSingleQuoted)
2322                                                 stateStack.Pop ();
2323                                         else if (State != DtdInputState.InsideDoubleQuoted && State != DtdInputState.Comment)
2324                                                 stateStack.Push (DtdInputState.InsideSingleQuoted);
2325                                         break;
2326                                 case '"':
2327                                         if (State == DtdInputState.InsideDoubleQuoted)
2328                                                 stateStack.Pop ();
2329                                         else if (State != DtdInputState.InsideSingleQuoted && State != DtdInputState.Comment)
2330                                                 stateStack.Push (DtdInputState.InsideDoubleQuoted);
2331                                         break;
2332                                 case '>':
2333                                         switch (State) {
2334                                         case DtdInputState.ElementDecl:
2335                                                 goto case DtdInputState.NotationDecl;
2336                                         case DtdInputState.AttlistDecl:
2337                                                 goto case DtdInputState.NotationDecl;
2338                                         case DtdInputState.EntityDecl:
2339                                                 goto case DtdInputState.NotationDecl;
2340                                         case DtdInputState.NotationDecl:
2341                                                 stateStack.Pop ();
2342                                                 break;
2343                                         case DtdInputState.InsideDoubleQuoted:
2344                                                 continue;
2345                                         case DtdInputState.InsideSingleQuoted:
2346                                                 continue; // well-formed
2347                                         case DtdInputState.Comment:
2348                                                 continue;
2349                                         default:
2350                                                 throw new XmlException (this as IXmlLineInfo,"unexpected token '>'");
2351                                         }
2352                                         break;
2353                                 case '?':
2354                                         if (State == DtdInputState.PI) {
2355                                                 if (ReadChar () == '>')
2356                                                         stateStack.Pop ();
2357                                         }
2358                                         break;
2359                                 case '-':
2360                                         if (State == DtdInputState.Comment) {
2361                                                 if (PeekChar () == '-') {
2362                                                         ReadChar ();
2363                                                         Expect ('>');
2364                                                         stateStack.Pop ();
2365                                                 }
2366                                         }
2367                                         break;
2368                                 case '%':
2369                                         if (State != DtdInputState.Free && State != DtdInputState.EntityDecl && State != DtdInputState.Comment && State != DtdInputState.InsideDoubleQuoted && State != DtdInputState.InsideSingleQuoted)
2370                                                 throw new XmlException (this as IXmlLineInfo,"Parameter Entity Reference cannot appear as a part of markupdecl (see XML spec 2.8).");
2371                                         break;
2372                                 }
2373                         }
2374                 }
2375
2376                 // The reader is positioned on the first 'S' of "SYSTEM".
2377                 private string ReadSystemLiteral (bool expectSYSTEM)
2378                 {
2379                         if(expectSYSTEM) {
2380                                 Expect ("SYSTEM");
2381                                 if (!SkipWhitespace ())
2382                                         throw new XmlException (this as IXmlLineInfo,
2383                                                 "Whitespace is required after 'SYSTEM'.");
2384                         }
2385                         else
2386                                 SkipWhitespace ();
2387                         int quoteChar = ReadChar ();    // apos or quot
2388                         int startPos = currentTagLength;
2389                         int c = 0;
2390                         ClearValueBuffer ();
2391                         while (c != quoteChar) {
2392                                 c = ReadChar ();
2393                                 if (c < 0)
2394                                         throw new XmlException (this as IXmlLineInfo,"Unexpected end of stream in ExternalID.");
2395                                 if (c != quoteChar)
2396                                         AppendValueChar (c);
2397                         }
2398                         return CreateValueString ();
2399                 }
2400
2401                 private string ReadPubidLiteral()
2402                 {
2403                         Expect ("PUBLIC");
2404                         if (!SkipWhitespace ())
2405                                 throw new XmlException (this as IXmlLineInfo,
2406                                         "Whitespace is required after 'PUBLIC'.");
2407                         int quoteChar = ReadChar ();
2408                         int startPos = currentTagLength;
2409                         int c = 0;
2410                         ClearValueBuffer ();
2411                         while(c != quoteChar)
2412                         {
2413                                 c = ReadChar ();
2414                                 if(c < 0) throw new XmlException (this as IXmlLineInfo,"Unexpected end of stream in ExternalID.");
2415                                 if(c != quoteChar && !XmlChar.IsPubidChar (c))
2416                                         throw new XmlException (this as IXmlLineInfo,"character '" + (char) c + "' not allowed for PUBLIC ID");
2417                                 if (c != quoteChar)
2418                                         AppendValueChar (c);
2419                         }
2420                         return CreateValueString ();
2421                 }
2422
2423                 // The reader is positioned on the first character
2424                 // of the name.
2425                 private string ReadName ()
2426                 {
2427                         int ch = PeekChar ();
2428                         if (!XmlChar.IsFirstNameChar (ch))
2429                                 throw new XmlException (this as IXmlLineInfo,String.Format ("a name did not start with a legal character {0} ({1})", ch, (char) ch));
2430
2431                         nameLength = 0;
2432
2433                         AppendNameChar (ReadChar ());
2434
2435                         while (XmlChar.IsNameChar (PeekChar ())) {
2436                                 AppendNameChar (ReadChar ());
2437                         }
2438
2439                         return CreateNameString ();
2440                 }
2441
2442                 // Read the next character and compare it against the
2443                 // specified character.
2444                 private void Expect (int expected)
2445                 {
2446                         int ch = ReadChar ();
2447
2448                         if (ch != expected) {
2449                                 throw new XmlException (this as IXmlLineInfo,
2450                                         String.Format (
2451                                                 "expected '{0}' ({1:X}) but found '{2}' ({3:X})",
2452                                                 (char) expected,
2453                                                 expected,
2454                                                 (char) ch,
2455                                                 ch));
2456                         }
2457                 }
2458
2459                 private void Expect (string expected)
2460                 {
2461                         int len = expected.Length;
2462                         for(int i=0; i< len; i++)
2463                                 Expect (expected[i]);
2464                 }
2465
2466                 private void ExpectAfterWhitespace (char c)
2467                 {
2468                         while (true) {
2469                                 int i = ReadChar ();
2470                                 if (i < 0x21 && XmlChar.IsWhitespace (i))
2471                                         continue;
2472                                 if (c != i)
2473                                         throw new XmlException (this, String.Join (String.Empty, new string [] {"Expected ", c.ToString (), ", but found " + (char) i, "[", i.ToString (), "]"}));
2474                                 break;
2475                         }
2476                 }
2477
2478                 // Does not consume the first non-whitespace character.
2479                 private bool SkipWhitespace ()
2480                 {
2481                         bool skipped = XmlChar.IsWhitespace (PeekChar ());
2482                         if (!skipped)
2483                                 return false;
2484                         while (XmlChar.IsWhitespace (PeekChar ()))
2485                                 ReadChar ();
2486                         return skipped;
2487                 }
2488
2489                 private void ReadWhitespace ()
2490                 {
2491                         if (currentState == XmlNodeType.None)
2492                                 currentState = XmlNodeType.XmlDeclaration;
2493
2494                         ClearValueBuffer ();
2495                         int ch = PeekChar ();
2496                         do {
2497                                 AppendValueChar (ReadChar ());
2498                         } while ((ch = PeekChar ()) != -1 && XmlChar.IsWhitespace (ch));
2499
2500                         if (currentState == XmlNodeType.Element && ch != -1 && ch != '<')
2501                                 ReadText (false);
2502                         else {
2503                                 XmlNodeType nodeType = (this.XmlSpace == XmlSpace.Preserve) ?
2504                                         XmlNodeType.SignificantWhitespace : XmlNodeType.Whitespace;
2505                                 SetProperties (nodeType,
2506                                                String.Empty,
2507                                                false,
2508                                                null, // value: create only when required
2509                                                true);
2510                         }
2511
2512                         return;
2513                 }
2514
2515                 private byte GetBase64Byte (char ch)
2516                 {
2517                         switch (ch) {
2518                         case '+':
2519                                 return 62;
2520                         case '/':
2521                                 return 63;
2522                         case '=':
2523                                 return 0;
2524                         default:
2525                                 if (ch >= 'A' && ch <= 'Z')
2526                                         return (byte) (ch - 'A');
2527                                 else if (ch >= 'a' && ch <= 'z')
2528                                         return (byte) (ch - 'a' + 26);
2529                                 else if (ch >= '0' && ch <= '9')
2530                                         return (byte) (ch - '0' + 52);
2531                                 else
2532                                         throw new XmlException ("Invalid Base64 character was found.");
2533                         }
2534                 }
2535
2536                 // Returns -1 if it should throw an error.
2537                 private int ReadCharsInternal (char [] buffer, int offset, int length)
2538                 {
2539                         if (IsEmptyElement) {
2540                                 Read ();
2541                                 return 0;
2542                         }
2543
2544                         shouldSkipUntilEndTag = true;
2545
2546                         if (offset < 0)
2547                                 throw new ArgumentOutOfRangeException ("offset", offset, "Offset must be non-negative integer.");
2548                         else if (length < 0)
2549                                 throw new ArgumentOutOfRangeException ("length", length, "Length must be non-negative integer.");
2550                         else if (buffer.Length < offset + length)
2551                                 throw new ArgumentOutOfRangeException ("buffer length is smaller than the sum of offset and length.");
2552
2553                         if (NodeType != XmlNodeType.Element)
2554                                 return 0;
2555
2556                         int bufIndex = offset;
2557                         for (int i = 0; i < length; i++) {
2558                                 int c = PeekChar ();
2559                                 switch (c) {
2560                                 case -1:
2561                                         throw new XmlException (this as IXmlLineInfo, "Unexpected end of xml.");
2562                                 case '<':
2563                                         ReadChar ();
2564                                         if (PeekChar () != '/') {
2565                                                 buffer [bufIndex++] = '<';
2566                                                 continue;
2567                                         }
2568                                         // Seems to skip immediate EndElement
2569                                         Expect ('/');
2570                                         if (depthUp) {
2571                                                 depth++;
2572                                                 depthUp = false;
2573                                         }
2574                                         ReadEndTag();
2575                                         shouldSkipUntilEndTag = false;
2576                                         Read ();
2577                                         return i;
2578                                 default:
2579                                         ReadChar ();
2580                                         if (c < Char.MaxValue)
2581                                                 buffer [bufIndex++] = (char) c;
2582                                         else {
2583                                                 buffer [bufIndex++] = (char) (c / 0x10000 + 0xD800 - 1);
2584                                                 buffer [bufIndex++] = (char) (c % 0x10000 + 0xDC00);
2585                                         }
2586                                         break;
2587                                 }
2588                         }
2589                         return length;
2590                 }
2591
2592                 private bool ReadUntilEndTag ()
2593                 {
2594                         int ch;
2595                         do {
2596                                 ch = ReadChar ();
2597                                 switch (ch) {
2598                                 case -1:
2599                                         throw new XmlException (this as IXmlLineInfo,
2600                                                 "Unexpected end of xml.");
2601                                 case '<':
2602                                         if (PeekChar () != '/')
2603                                                 continue;
2604                                         ReadChar ();
2605                                         string name = ReadName ();
2606                                         if (name != elementNames [elementNameStackPos - 1])
2607                                                 continue;
2608                                         Expect ('>');
2609                                         depth--;
2610                                         elementNames [--elementNameStackPos] = null;
2611                                         return Read ();
2612                                 }
2613                         } while (true);
2614                 }
2615                 #endregion
2616         }
2617 }