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