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