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