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