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