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