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