b6633645dbf4cd8112ed96322c1a8c7706237b50
[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 //   I haven't checked whether DTD parser runs correct.
15 //
16 //   More strict well-formedness checking should be done.
17 //
18 //   NameTables aren't being used completely yet.
19 //
20 //   Some thought needs to be given to performance. There's too many
21 //   strings being allocated.
22 //
23
24 using System;
25 using System.Collections;
26 using System.IO;
27 using System.Text;
28 using System.Xml.Schema;
29 using Mono.Xml;
30 using Mono.Xml.Native;
31
32 namespace System.Xml
33 {
34         public class XmlTextReader : XmlReader, IXmlLineInfo
35         {
36                 #region Constructors
37
38                 protected XmlTextReader ()
39                 {
40                 }
41
42                 public XmlTextReader (Stream input)
43                         : this (new XmlStreamReader (input))
44                 {
45                 }
46
47                 public XmlTextReader (string url)
48                         : this(url, new NameTable ())
49                 {
50                 }
51
52                 public XmlTextReader (TextReader input)
53                         : this (input, new NameTable ())
54                 {
55                 }
56
57                 protected XmlTextReader (XmlNameTable nt)
58                         : this (String.Empty, null, XmlNodeType.None, null)
59                 {
60                 }
61
62                 public XmlTextReader (Stream input, XmlNameTable nt)
63                         : this(new XmlStreamReader (input), nt)
64                 {
65                 }
66
67                 public XmlTextReader (string url, Stream input)
68                         : this (url, new XmlStreamReader (input))
69                 {
70                 }
71
72                 public XmlTextReader (string url, TextReader input)
73                         : this (url, input, new NameTable ())
74                 {
75                 }
76
77                 public XmlTextReader (string url, XmlNameTable nt)
78                         : this (url, new XmlStreamReader (url, null, null), nt)
79                 {
80                 }
81
82                 public XmlTextReader (TextReader input, XmlNameTable nt)
83                         : this (String.Empty, input, nt)
84                 {
85                 }
86
87                 public XmlTextReader (Stream xmlFragment, XmlNodeType fragType, XmlParserContext context)
88                         : this (context != null ? context.BaseURI : String.Empty,
89                                 new XmlStreamReader (xmlFragment),
90                         fragType,
91                         context)
92                 {
93                 }
94
95                 public XmlTextReader (string url, Stream input, XmlNameTable nt)
96                         : this (url, new XmlStreamReader (input), nt)
97                 {
98                 }
99
100                 public XmlTextReader (string url, TextReader input, XmlNameTable nt)
101                         : this (url, input, XmlNodeType.Document, null)
102                 {
103                 }
104
105                 [MonoTODO("TODO as same as private XmlTextReader(TextReader, XmlNodeType, XmlParserContext)")]
106                 public XmlTextReader (string xmlFragment, XmlNodeType fragType, XmlParserContext context)
107                         : this (context != null ? context.BaseURI : String.Empty,
108                                 new StringReader (xmlFragment),
109                                 fragType,
110                                 context)
111                 {
112                 }
113
114                 // TODO still remains as described at head of this file,
115                 // but it might not be TODO of the constructors...
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 attributes.Count; }
128                 }
129
130                 public override string BaseURI
131                 {
132                         get { return parserContext.BaseURI; }
133                 }
134
135                 public override int Depth
136                 {
137                         get {
138                                 return elementDepth;
139                         }
140                 }
141
142                 public Encoding Encoding
143                 {
144                         get { return parserContext.Encoding; }
145                 }
146
147                 public override bool EOF
148                 {
149                         get
150                         {
151                                 return
152                                         readState == ReadState.EndOfFile ||
153                                         readState == ReadState.Closed;
154                         }
155                 }
156
157                 public override bool HasValue
158                 {
159                         get { 
160                                 if (this.valueBuilderAvailable)
161                                         return valueBuilder.Length != 0;
162                                 else
163                                         return value != String.Empty;
164                         }
165                 }
166
167                 public override bool IsDefault
168                 {
169                         get
170                         {
171                                 // XmlTextReader does not expand default attributes.
172                                 return false;
173                         }
174                 }
175
176                 public override bool IsEmptyElement
177                 {
178                         get { return isEmptyElement; }
179                 }
180
181                 public override string this [int i]
182                 {
183                         get { return GetAttribute (i); }
184                 }
185
186                 public override string this [string name]
187                 {
188                         get { return GetAttribute (name); }
189                 }
190
191                 public override string this [string localName, string namespaceName]
192                 {
193                         get { return GetAttribute (localName, namespaceName); }
194                 }
195
196                 public int LineNumber
197                 {
198                         get { return currentInput.LineNumber; }
199                 }
200
201                 public int LinePosition
202                 {
203                         get { return currentInput.LinePosition; }
204                 }
205
206                 public override string LocalName
207                 {
208                         get { return localName; }
209                 }
210
211                 public override string Name
212                 {
213                         get { return name; }
214                 }
215
216                 public bool Namespaces
217                 {
218                         get { return namespaces; }
219                         set { 
220                                 if (readState != ReadState.Initial)
221                                         throw new InvalidOperationException ("Namespaces have to be set before reading.");
222                                 namespaces = value;
223                         }
224                 }
225
226                 public override string NamespaceURI
227                 {
228                         get { return namespaceURI; }
229                 }
230
231                 public override XmlNameTable NameTable
232                 {
233                         get { return parserContext.NameTable; }
234                 }
235
236                 public override XmlNodeType NodeType
237                 {
238                         get { return nodeType; }
239                 }
240
241                 [MonoTODO]
242                 public bool Normalization
243                 {
244                         get { return normalization; }
245                         set { normalization = value; }
246                 }
247
248                 public override string Prefix
249                 {
250                         get { return prefix; }
251                 }
252
253                 public override char QuoteChar
254                 {
255                         get {
256                                 // value string holds attribute quotation char.
257                                 if (NodeType == XmlNodeType.Attribute)
258                                         return value [0];
259                                 else
260                                         return '"';
261                         }
262                 }
263
264                 public override ReadState ReadState
265                 {
266                         get { return readState; }
267                 }
268
269                 public override string Value
270                 {
271                         get {
272                                 
273                                 string v = value;
274                                 if (valueBuilderAvailable)
275                                         v = valueBuilder.ToString ();
276                                 if(NodeType == XmlNodeType.Attribute)
277                                         return UnescapeAttributeValue(v);
278                                 else
279                                         return v;
280                         }
281                 }
282
283                 public WhitespaceHandling WhitespaceHandling
284                 {
285                         get { return whitespaceHandling; }
286                         set { whitespaceHandling = value; }
287                 }
288
289                 public override string XmlLang
290                 {
291                         get { return parserContext.XmlLang; }
292                 }
293
294                 public XmlResolver XmlResolver
295                 {
296                         set { resolver = value; }
297                 }
298
299                 public override XmlSpace XmlSpace
300                 {
301                         get { return parserContext.XmlSpace; }
302                 }
303
304                 #endregion
305
306                 #region Methods
307
308                 public override void Close ()
309                 {
310                         readState = ReadState.Closed;
311                         foreach (XmlParserInput input in parserInputStack.ToArray ())
312                                 input.Close ();
313                         this.currentInput.Close ();
314                 }
315
316                 public override string GetAttribute (int i)
317                 {
318                         if (i > attributes.Count)
319                                 throw new ArgumentOutOfRangeException ("i is smaller than AttributeCount");
320                         else
321                                 return UnescapeAttributeValue (attributes [orderedAttributes [i]] as string);
322                 }
323
324                 // MS.NET 1.0 documentation says that this method returns String.Empty for
325                 // not-exist attribute, but in fact it returns null.
326                 // That description is corrected in MS.NET 1.1 documentation.
327                 public override string GetAttribute (string name)
328                 {
329                         return UnescapeAttributeValue (attributes [name] as string);
330                 }
331
332                 private int GetIndexOfQualifiedAttribute (string localName, string namespaceURI)
333                 {
334                         for(int i = 0; i < orderedAttributes.Count; i++)
335                         {
336                                 string thisName = (string) orderedAttributes [i];
337
338                                 int indexOfColon = thisName.IndexOf (':');
339
340                                 if (indexOfColon != -1) {
341                                         string thisLocalName = thisName.Substring (indexOfColon + 1);
342
343                                         if (localName == thisLocalName) {
344                                                 string thisPrefix = thisName.Substring (0, indexOfColon);
345                                                 string thisNamespaceURI = LookupNamespace (thisPrefix);
346
347                                                 if (namespaceURI == thisNamespaceURI)
348                                                         return i;
349                                         }
350                                 } else if (localName == "xmlns" && namespaceURI == "http://www.w3.org/2000/xmlns/" && thisName == "xmlns")
351                                         return i;
352                         }
353                         return -1;
354                 }
355
356                 internal XmlParserContext GetInternalParserContext ()
357                 {
358                         return parserContext;
359                 }
360
361                 public override string GetAttribute (string localName, string namespaceURI)
362                 {
363                         int idx = this.GetIndexOfQualifiedAttribute (localName, namespaceURI);
364                         if (idx < 0)
365                                 return null;
366                         return UnescapeAttributeValue (attributes [orderedAttributes [idx]] as string);
367                 }
368
369                 [MonoTODO]
370                 public TextReader GetRemainder ()
371                 {
372                         throw new NotImplementedException ();
373                 }
374
375                 bool IXmlLineInfo.HasLineInfo ()
376                 {
377                         return true;
378                 }
379
380                 public override string LookupNamespace (string prefix)
381                 {
382                         return parserContext.NamespaceManager.LookupNamespace (prefix);
383                 }
384
385                 public override void MoveToAttribute (int i)
386                 {
387                         MoveToElement ();
388
389                         if (attributes == null || orderedAttributes.Count < i || i < 0)
390                                 throw new ArgumentOutOfRangeException ("attribute index out of range.");
391
392                         if (orderedAttributesEnumerator == null) {
393                                 SaveProperties ();
394                         }
395
396                         orderedAttributesEnumerator = orderedAttributes.GetEnumerator ();
397                         for (int n=0; n<=i; n++)
398                                 orderedAttributesEnumerator.MoveNext();
399
400                         string name = orderedAttributes [i] as string;
401                         string value = attributes [name] as string;
402                         SetProperties (
403                                 XmlNodeType.Attribute, // nodeType
404                                 name, // name
405                                 false, // isEmptyElement
406                                 value, // value
407                                 false // clearAttributes
408                                 );
409                         attributeValuePos = 0;
410                 }
411
412                 public override bool MoveToAttribute (string name)
413                 {
414                         MoveToElement ();
415                         bool match = false;
416
417                         if (attributes == null)
418                                 return false;
419
420                         if (orderedAttributesEnumerator == null) {
421                                 SaveProperties ();
422                         }
423
424                         orderedAttributesEnumerator = orderedAttributes.GetEnumerator ();
425                         while (orderedAttributesEnumerator.MoveNext ()) {
426                                 if(name == orderedAttributesEnumerator.Current as string) {
427                                         match = true;
428                                         break;
429                                 }
430                         }
431
432                         if (match) {
433                                 string value = attributes [name] as string;
434                                 SetProperties (
435                                         XmlNodeType.Attribute, // nodeType
436                                         name, // name
437                                         false, // isEmptyElement
438                                         value, // value
439                                         false // clearAttributes
440                                 );
441                                 attributeValuePos = 0;
442                         }
443
444                         return match;
445                 }
446
447                 public override bool MoveToAttribute (string localName, string namespaceName)
448                 {
449                         MoveToElement ();
450
451                         if (attributes == null)
452                                 return false;
453
454                         int idx = GetIndexOfQualifiedAttribute (localName, namespaceName);
455                         if (idx < 0)
456                                 return false;
457                         MoveToAttribute (idx);
458                         return true;
459                 }
460
461                 public override bool MoveToElement ()
462                 {
463                         if (orderedAttributesEnumerator != null) {
464                                 orderedAttributesEnumerator = null;
465                                 if (isPropertySaved)
466                                         RestoreProperties ();
467                                 return true;
468                         }
469
470                         return false;
471                 }
472
473                 public override bool MoveToFirstAttribute ()
474                 {
475                         MoveToElement ();
476                         return MoveToNextAttribute ();
477                 }
478
479                 public override bool MoveToNextAttribute ()
480                 {
481                         if (attributes == null)
482                                 return false;
483
484                         if (orderedAttributesEnumerator == null) {
485                                 SaveProperties ();
486                                 orderedAttributesEnumerator = orderedAttributes.GetEnumerator ();
487                         }
488
489                         if (orderedAttributesEnumerator.MoveNext ()) {
490                                 string name = orderedAttributesEnumerator.Current as string;
491                                 string value = attributes [name] as string;
492                                 SetProperties (
493                                         XmlNodeType.Attribute, // nodeType
494                                         name, // name
495                                         false, // isEmptyElement
496                                         value, // value
497                                         false // clearAttributes
498                                 );
499                                 attributeValuePos = 0;
500                                 return true;
501                         }
502
503                         return false;
504                 }
505
506                 public override bool Read ()
507                 {
508                         bool more = false;
509                         isPropertySaved = false;
510                         readState = ReadState.Interactive;
511
512                         // It was moved from end of ReadStartTag ().
513                         if (depthUp)
514                                 ++depth;
515                         depthUp = false;
516
517                         more = ReadContent ();
518
519                         if (depth == 0 && !allowMultipleRoot && (IsEmptyElement || NodeType == XmlNodeType.EndElement))
520                                 currentState = XmlNodeType.EndElement;
521                         if (maybeTextDecl != 0)
522                                 maybeTextDecl--;
523
524                         return more;
525                 }
526
527                 public override bool ReadAttributeValue ()
528                 {
529                         // 'attributeString' holds real string value (without their
530                         // quotation characters).
531                         //
532                         // 'attributeValuePos' holds current position
533                         // of 'attributeString' while iterating ReadAttribute().
534                         // It may be:
535                         //   -1 if ReadAttributeValue() has already finished.
536                         //    0 if ReadAttributeValue() ready to start reading.
537                         //   >0 if ReadAttributeValue() already got 1 or more values
538                         //
539                         // local 'refPosition' holds the position on the 
540                         // attributeString which may be used next time.
541
542                         if (attributeValuePos < 0)
543                                 return false;
544
545                         // If not started, then initialize attributeString when parsing is at start.
546                         if (attributeValuePos == 0)
547                                 attributeString =
548                                         value.Substring (1, value.Length - 2);
549
550                         // It occurs when attribute dully consists of entity reference.
551                         if (attributeValuePos == attributeString.Length)
552                                 return false;
553
554                         returnEntityReference = false;
555                         value = String.Empty;
556                         int refPosition;
557                         int loop = 0;
558
559                         do {
560                                 refPosition = attributeString.IndexOf ('&', attributeValuePos);
561                                 if (refPosition < 0) {
562                                         // Reached to the end of value string.
563                                         value += attributeString.Substring (attributeValuePos);
564                                         attributeValuePos = -1;
565                                         break;
566                                 } else if (refPosition == attributeValuePos) {
567                                         string parsed = ReadAttributeValueReference ();
568                                         if (parsed != null)
569                                                 value += parsed;
570                                         else {
571                                                 // Found that an entity reference starts from this point.
572                                                 // reset position to after '&'.
573                                                 attributeValuePos = refPosition;
574                                                 if (value.Length <= 0) {
575                                                         int endNamePos = attributeString.IndexOf (";", attributeValuePos);
576                                                         value = attributeString.Substring (attributeValuePos+1, endNamePos - attributeValuePos - 1);
577                                                         attributeValuePos += value.Length + 2;
578                                                         returnEntityReference = true;
579                                                 }
580                                                 break;
581                                         }
582                                 } else {
583                                         value += attributeString.Substring (attributeValuePos,
584                                                 refPosition - attributeValuePos);
585                                         attributeValuePos = refPosition;
586                                         continue;
587                                 }
588                         } while (++loop > 0);
589
590                         if (returnEntityReference)
591                                 SetProperties (XmlNodeType.EntityReference,
592                                         value,
593                                         false,
594                                         String.Empty,
595                                         false);
596                         else
597                                 SetProperties (XmlNodeType.Text,
598                                         "",
599                                         false,
600                                         value,
601                                         false);
602
603                         return true;
604                 }
605
606                 [MonoTODO]
607                 public int ReadBase64 (byte [] buffer, int offset, int length)
608                 {
609                         throw new NotImplementedException ();
610                 }
611
612                 [MonoTODO]
613                 public int ReadBinHex (byte [] buffer, int offset, int length)
614                 {
615                         throw new NotImplementedException ();
616                 }
617
618                 [MonoTODO]
619                 public int ReadChars (char [] buffer, int offset, int length)
620                 {
621                         throw new NotImplementedException ();
622                 }
623
624                 public override string ReadInnerXml ()
625                 {
626                         if (readState != ReadState.Interactive)
627                                 return String.Empty;
628
629                         switch (NodeType) {
630                         case XmlNodeType.Attribute:
631                                 return value.Substring (1, value.Length - 2);
632                         case XmlNodeType.Element:
633                                 if (IsEmptyElement)
634                                         return String.Empty;
635
636                                 int startDepth = depth;
637
638                                 if (innerXmlBuilder == null)
639                                         innerXmlBuilder = new StringBuilder ();
640                                 innerXmlBuilder.Length = 0;
641                                 bool loop = true;
642                                 do {
643                                         Read ();
644                                         if (NodeType ==XmlNodeType.None)
645                                                 throw new XmlException ("unexpected end of xml.");
646                                         else if (NodeType == XmlNodeType.EndElement && depth == startDepth) {
647                                                 loop = false;
648                                                 Read ();
649                                         }
650                                         else
651                                                 innerXmlBuilder.Append (currentTag);
652                                 } while (loop);
653                                 string xml = innerXmlBuilder.ToString ();
654                                 innerXmlBuilder.Length = 0;
655                                 return xml;
656                         case XmlNodeType.None:
657                                 // MS document is incorrect. Seems not to progress.
658                                 return String.Empty;
659                         default:
660                                 Read ();
661                                 return String.Empty;
662                         }
663                 }
664
665                 public override string ReadOuterXml ()
666                 {
667                         if (readState != ReadState.Interactive)
668                                 return String.Empty;
669
670                         switch (NodeType) {
671                         case XmlNodeType.Attribute:
672                                 // strictly incompatible with MS... (it holds spaces attribute between name, value and "=" char (very trivial).
673                                 return String.Format ("{0}={1}{2}{1}", Name, QuoteChar, ReadInnerXml ());
674                         case XmlNodeType.Element:
675                                 bool isEmpty = IsEmptyElement;
676                                 string startTag = currentTag.ToString ();
677                                 string name = Name;
678
679                                 if (NodeType == XmlNodeType.Element && !isEmpty)
680                                         return String.Format ("{0}{1}</{2}>", startTag, ReadInnerXml (), name);
681                                 else
682                                         return currentTag.ToString ();
683                         case XmlNodeType.None:
684                                 // MS document is incorrect. Seems not to progress.
685                                 return String.Empty;
686                         default:
687                                 Read ();
688                                 return String.Empty;
689                         }
690                 }
691
692                 public override string ReadString ()
693                 {
694                         return ReadStringInternal ();
695                 }
696
697                 public void ResetState ()
698                 {
699                         Init ();
700                 }
701
702                 public override void ResolveEntity ()
703                 {
704                         // XmlTextReaders don't resolve entities.
705                         throw new InvalidOperationException ("XmlTextReader cannot resolve external entities.");
706                 }
707
708                 #endregion
709
710                 #region Internals
711                 // Parsed DTD Objects
712                 internal DTDObjectModel DTD;
713                 #endregion
714
715                 #region Privates
716
717                 private XmlParserContext parserContext;
718
719                 private XmlParserInput currentInput;
720                 private Stack parserInputStack;
721                 private ReadState readState;
722
723                 private int depth;
724                 private int elementDepth;
725                 private bool depthUp;
726
727                 private bool popScope;
728                 private Stack elementStack;
729                 private bool allowMultipleRoot;
730
731                 private XmlNodeType nodeType;
732                 private string name;
733                 private string prefix;
734                 private string localName;
735                 private string namespaceURI;
736                 private bool isEmptyElement;
737                 private string value;
738                 private StringBuilder valueBuilder;
739                 private bool valueBuilderAvailable = false;
740
741                 private bool isPropertySaved;
742                 private XmlNodeType saveNodeType;
743                 private string saveName;
744                 private string savePrefix;
745                 private string saveLocalName;
746                 private string saveNamespaceURI;
747                 private bool saveIsEmptyElement;
748
749                 private Hashtable attributes;
750                 private ArrayList orderedAttributes;
751                 private IEnumerator orderedAttributesEnumerator;
752
753                 private bool returnEntityReference;
754                 private string entityReferenceName;
755
756                 private char [] nameBuffer;
757                 private int nameLength;
758                 private int nameCapacity;
759                 private const int initialNameCapacity = 256;
760
761                 private StringBuilder valueBuffer;
762
763                 // A buffer for ReadContent for ReadOuterXml
764                 private StringBuilder currentTag {
765                         get {
766                                 return currentInput.CurrentMarkup;
767                         }
768                 }
769
770                 private string attributeString;
771                 private int attributeValuePos;
772                 // This should be only referenced(used) by ReadInnerXml(). Kind of flyweight pattern.
773                 private StringBuilder innerXmlBuilder;
774
775                 // Parameter entity placeholder
776                 private Hashtable parameterEntities;
777                 private int dtdIncludeSect;
778                 private bool isIntSubset;
779
780                 // State machine attribute.
781                 //      XmlDeclaration: after the first node.
782                 //      DocumentType: after doctypedecl
783                 //      Element: inside document element
784                 //      EndElement: after document element
785                 private XmlNodeType currentState;
786                 private int maybeTextDecl;
787
788                 // These values are never re-initialized.
789                 private XmlResolver resolver = new XmlUrlResolver ();
790                 private bool namespaces = true;
791                 private WhitespaceHandling whitespaceHandling = WhitespaceHandling.All;
792                 private bool normalization = false;
793
794                 private void Init ()
795                 {
796                         readState = ReadState.Initial;
797                         currentState = XmlNodeType.None;
798                         maybeTextDecl = 0;
799                         allowMultipleRoot = false;
800
801                         depth = 0;
802                         depthUp = false;
803
804                         popScope = false;
805                         parserInputStack = new Stack ();
806                         elementStack = new Stack();
807
808                         nodeType = XmlNodeType.None;
809                         name = String.Empty;
810                         prefix = String.Empty;
811                         localName = string.Empty;
812                         isEmptyElement = false;
813                         value = String.Empty;
814
815                         attributes = new Hashtable ();
816                         attributeString = String.Empty;
817                         orderedAttributes = new ArrayList ();
818                         orderedAttributesEnumerator = null;
819
820                         returnEntityReference = false;
821                         entityReferenceName = String.Empty;
822
823                         nameBuffer = new char [initialNameCapacity];
824                         nameLength = 0;
825                         nameCapacity = initialNameCapacity;
826                         
827                         valueBuffer = new StringBuilder (8192);
828                         parameterEntities = new Hashtable ();
829                 }
830
831                 private void InitializeContext (string url, XmlParserContext context, TextReader fragment, XmlNodeType fragType)
832                 {
833                         parserContext = context;
834                         if (context == null) {
835                                 XmlNameTable nt = new NameTable ();
836                                 parserContext = new XmlParserContext (nt,
837                                         new XmlNamespaceManager (nt),
838                                         String.Empty,
839                                         XmlSpace.None);
840                         }
841
842                         if (url != null && url != String.Empty) {
843                                 string path = Path.GetFullPath ("./a");
844                                 Uri uri = new Uri (new Uri (path), url);
845                                 parserContext.BaseURI = uri.ToString ();
846                         }
847
848                         Init ();
849
850                         switch (fragType) {
851                         case XmlNodeType.Attribute:
852                                 value = String.Format ("{0}{1}{0}", "'", fragment.ReadToEnd ().Replace ("'", "&apos;"));
853                                 break;
854                         case XmlNodeType.Element:
855                                 currentState = XmlNodeType.Element;
856                                 allowMultipleRoot = true;
857                                 break;
858                         case XmlNodeType.Document:
859                                 break;
860                         default:
861                                 throw new XmlException (String.Format ("NodeType {0} is not allowed to create XmlTextReader.", fragType));
862                         }
863
864                         this.currentInput = new XmlParserInput (fragment, url);
865                         StreamReader sr = fragment as StreamReader;
866                 }
867
868                 // Use this method rather than setting the properties
869                 // directly so that all the necessary properties can
870                 // be changed in harmony with each other. Maybe the
871                 // fields should be in a seperate class to help enforce
872                 // this.
873                 private void SetProperties (
874                         XmlNodeType nodeType,
875                         string name,
876                         bool isEmptyElement,
877                         string value,
878                         bool clearAttributes)
879                 {
880                         this.nodeType = nodeType;
881                         this.name = name;
882                         this.isEmptyElement = isEmptyElement;
883                         this.value = value;
884                         this.elementDepth = depth;
885                         this.valueBuilderAvailable = false;
886
887                         if (clearAttributes)
888                                 ClearAttributes ();
889
890                         if (namespaces) {
891                                 int indexOfColon = name.IndexOf (':');
892
893                                 if (indexOfColon == -1) {
894                                         prefix = String.Empty;
895                                         localName = name;
896                                 } else {
897                                         prefix = name.Substring (0, indexOfColon);
898                                         localName = name.Substring (indexOfColon + 1);
899                                 }
900                         } else {
901                                 prefix = String.Empty;
902                                 localName = name;
903                         }
904
905                         switch (nodeType) {
906                         case XmlNodeType.Attribute:
907                                 if (prefix == string.Empty) namespaceURI = string.Empty;
908                                 else namespaceURI = LookupNamespace (prefix);
909                                 if (localName == "xmlns" && prefix == "")
910                                         namespaceURI = "http://www.w3.org/2000/xmlns/";
911                                 break;
912
913                         case XmlNodeType.Element:
914                         case XmlNodeType.EndElement:
915                                 namespaceURI = LookupNamespace (prefix);
916                                 break;
917                         default:
918                                 namespaceURI = "";
919                                 break;
920                         }
921                 }
922                 
923                 private void SetProperties (
924                         XmlNodeType nodeType,
925                         string name,
926                         bool isEmptyElement,
927                         StringBuilder value,
928                         bool clearAttributes) {
929                         SetProperties (nodeType, name, isEmptyElement, (string)null, clearAttributes);
930                         this.valueBuilderAvailable = true;
931                         this.valueBuilder = value;
932                 }
933
934                 private void SaveProperties ()
935                 {
936                         // If already saved, then return.
937                         if (isPropertySaved)
938                                 return;
939
940                         saveNodeType = nodeType;
941                         saveName = name;
942                         savePrefix = prefix;
943                         saveLocalName = localName;
944                         saveNamespaceURI = namespaceURI;
945                         saveIsEmptyElement = isEmptyElement;
946                         // An element's value is always String.Empty.
947                         isPropertySaved = true;
948                 }
949
950                 private void RestoreProperties ()
951                 {
952                         nodeType = saveNodeType;
953                         name = saveName;
954                         prefix = savePrefix;
955                         localName = saveLocalName;
956                         namespaceURI = saveNamespaceURI;
957                         isEmptyElement = saveIsEmptyElement;
958                         value = String.Empty;
959                         isPropertySaved = false;
960                 }
961
962                 private void AddAttribute (string name, string value)
963                 {
964                         if (attributes.ContainsKey (name))
965                                 throw new XmlException (this as IXmlLineInfo,
966                                         String.Format ("Attribute {0} already exists.", name));
967                         attributes.Add (name, value);
968                         orderedAttributes.Add (name);
969                 }
970
971                 private void ClearAttributes ()
972                 {
973                         if (attributes.Count > 0) {
974                                 attributes.Clear ();
975                                 orderedAttributes.Clear ();
976                         }
977
978                         orderedAttributesEnumerator = null;
979                 }
980
981                 private int PeekChar ()
982                 {
983                         return currentInput.PeekChar ();
984                 }
985
986                 private int ReadChar ()
987                 {
988                         return currentInput.ReadChar ();
989                 }
990
991                 // This should really keep track of some state so
992                 // that it's not possible to have more than one document
993                 // element or text outside of the document element.
994                 private bool ReadContent ()
995                 {
996                         currentTag.Length = 0;
997                         if (popScope) {
998                                 parserContext.NamespaceManager.PopScope ();
999                                 popScope = false;
1000                         }
1001
1002                         if (returnEntityReference) {
1003                                 SetEntityReferenceProperties ();
1004                         } else {
1005                                 switch (PeekChar ()) {
1006                                 case '<':
1007                                         ReadChar ();
1008                                         ReadTag ();
1009                                         break;
1010                                 case '\r': goto case ' ';
1011                                 case '\n': goto case ' ';
1012                                 case '\t': goto case ' ';
1013                                 case ' ':
1014                                         if (whitespaceHandling == WhitespaceHandling.All ||
1015                                                 whitespaceHandling == WhitespaceHandling.Significant)
1016                                                 return ReadWhitespace ();
1017
1018                                         SkipWhitespace ();
1019                                         return ReadContent ();
1020                                 case -1:
1021                                         if (depth > 0)
1022                                                 throw new XmlException ("unexpected end of file. Current depth is " + depth);
1023                                         readState = ReadState.EndOfFile;
1024                                         SetProperties (
1025                                                 XmlNodeType.None, // nodeType
1026                                                 String.Empty, // name
1027                                                 false, // isEmptyElement
1028                                                 String.Empty, // value
1029                                                 true // clearAttributes
1030                                         );
1031                                         break;
1032                                 default:
1033                                         ReadText (true);
1034                                         break;
1035                                 }
1036                         }
1037                         return this.ReadState != ReadState.EndOfFile;
1038                 }
1039
1040                 private void SetEntityReferenceProperties ()
1041                 {
1042                         SetProperties (
1043                                 XmlNodeType.EntityReference, // nodeType
1044                                 entityReferenceName, // name
1045                                 false, // isEmptyElement
1046                                 String.Empty, // value
1047                                 true // clearAttributes
1048                         );
1049
1050                         returnEntityReference = false;
1051                         entityReferenceName = String.Empty;
1052                 }
1053
1054                 // The leading '<' has already been consumed.
1055                 private void ReadTag ()
1056                 {
1057                         switch (PeekChar ())
1058                         {
1059                         case '/':
1060                                 ReadChar ();
1061                                 ReadEndTag ();
1062                                 break;
1063                         case '?':
1064                                 ReadChar ();
1065                                 ReadProcessingInstruction ();
1066                                 break;
1067                         case '!':
1068                                 ReadChar ();
1069                                 ReadDeclaration ();
1070                                 break;
1071                         default:
1072                                 ReadStartTag ();
1073                                 break;
1074                         }
1075                 }
1076
1077                 // The leading '<' has already been consumed.
1078                 private void ReadStartTag ()
1079                 {
1080                         if (currentState == XmlNodeType.EndElement)
1081                                 throw new XmlException (this as IXmlLineInfo,
1082                                         "Element cannot appear in this state.");
1083                         currentState = XmlNodeType.Element;
1084
1085                         parserContext.NamespaceManager.PushScope ();
1086
1087                         string name = ReadName ();
1088                         if (currentState == XmlNodeType.EndElement)
1089                                 throw new XmlException (this as IXmlLineInfo,"document has terminated, cannot open new element");
1090
1091                         bool isEmptyElement = false;
1092
1093                         ClearAttributes ();
1094
1095                         SkipWhitespace ();
1096                         if (XmlConstructs.IsNameStart (PeekChar ()))
1097                                 ReadAttributes (false);
1098
1099                         string baseUri = GetAttribute ("xml:base");
1100                         if (baseUri != null)
1101                                 parserContext.BaseURI = baseUri;
1102                         string xmlLang = GetAttribute ("xml:lang");
1103                         if (xmlLang != null)
1104                                 parserContext.XmlLang = xmlLang;
1105                         string xmlSpaceAttr = GetAttribute ("xml:space");
1106                         if (xmlSpaceAttr != null) {
1107                                 if (xmlSpaceAttr == "preserve")
1108                                         parserContext.XmlSpace = XmlSpace.Preserve;
1109                                 else if (xmlSpaceAttr == "default")
1110                                         parserContext.XmlSpace = XmlSpace.Default;
1111                                 else
1112                                         throw new XmlException (this as IXmlLineInfo,String.Format ("Invalid xml:space value: {0}", xmlSpaceAttr));
1113                         }
1114                         if (PeekChar () == '/') {
1115                                 ReadChar ();
1116                                 isEmptyElement = true;
1117                                 popScope = true;
1118                         }
1119                         else {
1120                                 depthUp = true;
1121                                 elementStack.Push (name);
1122                                 parserContext.PushScope ();
1123                         }
1124
1125                         Expect ('>');
1126
1127                         SetProperties (
1128                                 XmlNodeType.Element, // nodeType
1129                                 name, // name
1130                                 isEmptyElement, // isEmptyElement
1131                                 String.Empty, // value
1132                                 false // clearAttributes
1133                         );
1134                 }
1135
1136                 // The reader is positioned on the first character
1137                 // of the element's name.
1138                 private void ReadEndTag ()
1139                 {
1140                         if (currentState != XmlNodeType.Element)
1141                                 throw new XmlException (this as IXmlLineInfo,
1142                                         "End tag cannot appear in this state.");
1143
1144                         string name = ReadName ();
1145                         if (elementStack.Count == 0)
1146                                 throw new XmlException (this as IXmlLineInfo,"closing element without matching opening element");
1147                         string expected = (string)elementStack.Pop();
1148                         if (expected != name)
1149                                 throw new XmlException (this as IXmlLineInfo,String.Format ("unmatched closing element: expected {0} but found {1}", expected, name));
1150                         parserContext.PopScope ();
1151
1152                         SkipWhitespace ();
1153                         Expect ('>');
1154
1155                         --depth;
1156
1157                         SetProperties (
1158                                 XmlNodeType.EndElement, // nodeType
1159                                 name, // name
1160                                 false, // isEmptyElement
1161                                 String.Empty, // value
1162                                 true // clearAttributes
1163                         );
1164
1165                         popScope = true;
1166                 }
1167
1168                 private void AppendNameChar (int ch)
1169                 {
1170                         CheckNameCapacity ();
1171                         nameBuffer [nameLength++] = (char)ch;
1172                 }
1173
1174                 private void CheckNameCapacity ()
1175                 {
1176                         if (nameLength == nameCapacity) {
1177                                 nameCapacity = nameCapacity * 2;
1178                                 char [] oldNameBuffer = nameBuffer;
1179                                 nameBuffer = new char [nameCapacity];
1180                                 Array.Copy (oldNameBuffer, nameBuffer, nameLength);
1181                         }
1182                 }
1183
1184                 private string CreateNameString ()
1185                 {
1186                         return parserContext.NameTable.Add (nameBuffer, 0, nameLength);
1187                 }
1188
1189                 private void AppendValueChar (int ch)
1190                 {
1191                         valueBuffer.Append ((char)ch);
1192                 }
1193
1194                 private string CreateValueString ()
1195                 {
1196                         return valueBuffer.ToString ();
1197                 }
1198                 
1199                 private void ClearValueBuffer ()
1200                 {
1201                         valueBuffer.Length = 0;
1202                 }
1203
1204                 // The reader is positioned on the first character
1205                 // of the text.
1206                 private void ReadText (bool cleanValue)
1207                 {
1208                         if (currentState != XmlNodeType.Element)
1209                                 throw new XmlException (this as IXmlLineInfo,
1210                                         "Text node cannot appear in this state.");
1211
1212                         if (cleanValue)
1213                                 ClearValueBuffer ();
1214
1215                         int ch = PeekChar ();
1216
1217                         while (ch != '<' && ch != -1) {
1218                                 if (ch == '&') {
1219                                         ReadChar ();
1220                                         if (ReadReference (false))
1221                                                 break;
1222                                 } else
1223                                         AppendValueChar (ReadChar ());
1224
1225                                 ch = PeekChar ();
1226                         }
1227
1228                         if (returnEntityReference && valueBuffer.Length == 0) {
1229                                 SetEntityReferenceProperties ();
1230                         } else {
1231                                 SetProperties (
1232                                         XmlNodeType.Text, // nodeType
1233                                         String.Empty, // name
1234                                         false, // isEmptyElement
1235                                         valueBuffer, // value
1236                                         true // clearAttributes
1237                                 );
1238                         }
1239                 }
1240
1241                 // The leading '&' has already been consumed.
1242                 // Returns true if the entity reference isn't a simple
1243                 // character reference or one of the predefined entities.
1244                 // This allows the ReadText method to break so that the
1245                 // next call to Read will return the EntityReference node.
1246                 private bool ReadReference (bool ignoreEntityReferences)
1247                 {
1248                         if (PeekChar () == '#') {
1249                                 ReadChar ();
1250                                 ReadCharacterReference ();
1251                         } else
1252                                 ReadEntityReference (ignoreEntityReferences);
1253
1254                         return returnEntityReference;
1255                 }
1256
1257                 private void ReadCharacterReference ()
1258                 {
1259                         int value = 0;
1260
1261                         if (PeekChar () == 'x') {
1262                                 ReadChar ();
1263
1264                                 while (PeekChar () != ';' && PeekChar () != -1) {
1265                                         int ch = ReadChar ();
1266
1267                                         if (ch >= '0' && ch <= '9')
1268                                                 value = (value << 4) + ch - '0';
1269                                         else if (ch >= 'A' && ch <= 'F')
1270                                                 value = (value << 4) + ch - 'A' + 10;
1271                                         else if (ch >= 'a' && ch <= 'f')
1272                                                 value = (value << 4) + ch - 'a' + 10;
1273                                         else
1274                                                 throw new XmlException (this as IXmlLineInfo,
1275                                                         String.Format (
1276                                                                 "invalid hexadecimal digit: {0} (#x{1:X})",
1277                                                                 (char)ch,
1278                                                                 ch));
1279                                 }
1280                         } else {
1281                                 while (PeekChar () != ';' && PeekChar () != -1) {
1282                                         int ch = ReadChar ();
1283
1284                                         if (ch >= '0' && ch <= '9')
1285                                                 value = value * 10 + ch - '0';
1286                                         else
1287                                                 throw new XmlException (this as IXmlLineInfo,
1288                                                         String.Format (
1289                                                                 "invalid decimal digit: {0} (#x{1:X})",
1290                                                                 (char)ch,
1291                                                                 ch));
1292                                 }
1293                         }
1294
1295                         ReadChar (); // ';'
1296
1297                         AppendValueChar (value);
1298                 }
1299
1300                 private void ReadEntityReference (bool ignoreEntityReferences)
1301                 {
1302                         nameLength = 0;
1303
1304                         int ch = PeekChar ();
1305
1306                         while (ch != ';' && ch != -1) {
1307                                 AppendNameChar (ReadChar ());
1308                                 ch = PeekChar ();
1309                         }
1310
1311                         Expect (';');
1312
1313                         string name = CreateNameString ();
1314
1315                         switch (name)
1316                         {
1317                                 case "lt":
1318                                         AppendValueChar ('<');
1319                                         break;
1320                                 case "gt":
1321                                         AppendValueChar ('>');
1322                                         break;
1323                                 case "amp":
1324                                         AppendValueChar ('&');
1325                                         break;
1326                                 case "apos":
1327                                         AppendValueChar ('\'');
1328                                         break;
1329                                 case "quot":
1330                                         AppendValueChar ('"');
1331                                         break;
1332                                 default:
1333                                         if (ignoreEntityReferences) {
1334                                                 AppendValueChar ('&');
1335
1336                                                 foreach (char ch2 in name) {
1337                                                         AppendValueChar (ch2);
1338                                                 }
1339
1340                                                 AppendValueChar (';');
1341                                         } else {
1342                                                 returnEntityReference = true;
1343                                                 entityReferenceName = name;
1344                                         }
1345                                         break;
1346                         }
1347                 }
1348
1349                 // The reader is positioned on the first character of
1350                 // the attribute name.
1351                 private void ReadAttributes (bool allowPIEnd)
1352                 {
1353                         int peekChar = -1;
1354                         bool requireWhitespace = false;
1355                         do {
1356                                 if (!SkipWhitespace () && requireWhitespace)
1357                                         throw new XmlException ("Unexpected token. Name is required here.");
1358                                 string name = ReadName ();
1359                                 SkipWhitespace ();
1360                                 Expect ('=');
1361                                 SkipWhitespace ();
1362                                 string value = ReadAttribute ();
1363
1364                                 if (name == "xmlns")
1365                                         parserContext.NamespaceManager.AddNamespace (String.Empty, UnescapeAttributeValue (value));
1366                                 else if (name.StartsWith ("xmlns:"))
1367                                         parserContext.NamespaceManager.AddNamespace (name.Substring (6), UnescapeAttributeValue (value));
1368
1369                                 AddAttribute (name, value);
1370
1371                                 if (XmlConstructs.IsSpace (PeekChar ()))
1372                                         SkipWhitespace ();
1373                                 else
1374                                         requireWhitespace = true;
1375                                 peekChar = PeekChar ();
1376                                 if (peekChar == '?' && allowPIEnd)
1377                                         break;
1378                         } while (peekChar != '/' && peekChar != '>' && peekChar != -1);
1379                 }
1380
1381                 // The reader is positioned on the quote character.
1382                 // *Keeps quote char* to value to get_QuoteChar() correctly.
1383                 private string ReadAttribute ()
1384                 {
1385                         ClearValueBuffer ();
1386
1387                         int quoteChar = ReadChar ();
1388
1389                         if (quoteChar != '\'' && quoteChar != '\"')
1390                                 throw new XmlException (this as IXmlLineInfo,"an attribute value was not quoted");
1391
1392                         AppendValueChar (quoteChar);
1393
1394                         while (PeekChar () != quoteChar) {
1395                                 int ch = ReadChar ();
1396
1397                                 switch (ch)
1398                                 {
1399                                 case '<':
1400                                         throw new XmlException (this as IXmlLineInfo,"attribute values cannot contain '<'");
1401                                 case -1:
1402                                         throw new XmlException (this as IXmlLineInfo,"unexpected end of file in an attribute value");
1403                                 default:
1404                                         AppendValueChar (ch);
1405                                         break;
1406                                 }
1407                         }
1408
1409                         ReadChar (); // quoteChar
1410                         AppendValueChar (quoteChar);
1411
1412                         return CreateValueString ();
1413                 }
1414
1415                 // The reader is positioned on the first character
1416                 // of the target.
1417                 //
1418                 // It may be xml declaration or processing instruction.
1419                 private void ReadProcessingInstruction ()
1420                 {
1421                         string target = ReadName ();
1422                         if (target == "xml") {
1423                                 ReadXmlDeclaration ();
1424                                 return;
1425                         }
1426                         if (currentState == XmlNodeType.None)
1427                                 currentState = XmlNodeType.XmlDeclaration;
1428
1429                         SkipWhitespace ();
1430
1431                         ClearValueBuffer ();
1432
1433                         while (PeekChar () != -1) {
1434                                 int ch = ReadChar ();
1435
1436                                 if (ch == '?' && PeekChar () == '>') {
1437                                         ReadChar ();
1438                                         break;
1439                                 }
1440
1441                                 AppendValueChar ((char)ch);
1442                         }
1443
1444                         SetProperties (
1445                                 XmlNodeType.ProcessingInstruction, // nodeType
1446                                 target, // name
1447                                 false, // isEmptyElement
1448                                 valueBuffer, // value
1449                                 true // clearAttributes
1450                         );
1451                 }
1452
1453                 // The reader is positioned after "<?xml "
1454                 private void ReadXmlDeclaration ()
1455                 {
1456                         if (currentState != XmlNodeType.None) {
1457                                 if (maybeTextDecl == 0)
1458                                         throw new XmlException (this as IXmlLineInfo,
1459                                                 "XML declaration cannot appear in this state.");
1460                         }
1461                         currentState = XmlNodeType.XmlDeclaration;
1462
1463                         ClearAttributes ();
1464
1465                         ReadAttributes (true);  // They must have "version."
1466                         Expect ("?>");
1467
1468                         if (maybeTextDecl != 0)
1469                                 if (this ["standalone"] != null)
1470                                         throw new XmlException (this as IXmlLineInfo,
1471                                                 "Invalid text declaration.");
1472                         maybeTextDecl = 0;
1473
1474                         SetProperties (
1475                                 XmlNodeType.XmlDeclaration, // nodeType
1476                                 "xml", // name
1477                                 false, // isEmptyElement
1478                                 currentInput.CurrentMarkup.ToString (6, currentInput.CurrentMarkup.Length - 6), // value
1479                                 false // clearAttributes
1480                         );
1481                 }
1482
1483                 // The reader is positioned on the first character after
1484                 // the leading '<!'.
1485                 private void ReadDeclaration ()
1486                 {
1487                         int ch = PeekChar ();
1488
1489                         switch (ch)
1490                         {
1491                         case '-':
1492                                 Expect ("--");
1493                                 ReadComment ();
1494                                 break;
1495                         case '[':
1496                                 ReadChar ();
1497                                 Expect ("CDATA[");
1498                                 ReadCDATA ();
1499                                 break;
1500                         case 'D':
1501                                 Expect ("DOCTYPE");
1502                                 ReadDoctypeDecl ();
1503                                 break;
1504                         }
1505                 }
1506
1507                 // The reader is positioned on the first character after
1508                 // the leading '<!--'.
1509                 private void ReadComment ()
1510                 {
1511                         if (currentState == XmlNodeType.None)
1512                                 currentState = XmlNodeType.XmlDeclaration;
1513
1514                         ClearValueBuffer ();
1515
1516                         while (PeekChar () != -1) {
1517                                 int ch = ReadChar ();
1518
1519                                 if (ch == '-' && PeekChar () == '-') {
1520                                         ReadChar ();
1521
1522                                         if (PeekChar () != '>')
1523                                                 throw new XmlException (this as IXmlLineInfo,"comments cannot contain '--'");
1524
1525                                         ReadChar ();
1526                                         break;
1527                                 }
1528
1529                                 AppendValueChar ((char)ch);
1530                         }
1531
1532                         SetProperties (
1533                                 XmlNodeType.Comment, // nodeType
1534                                 String.Empty, // name
1535                                 false, // isEmptyElement
1536                                 valueBuffer, // value
1537                                 true // clearAttributes
1538                         );
1539                 }
1540
1541                 // The reader is positioned on the first character after
1542                 // the leading '<![CDATA['.
1543                 private void ReadCDATA ()
1544                 {
1545                         if (currentState != XmlNodeType.Element)
1546                                 throw new XmlException (this as IXmlLineInfo,
1547                                         "CDATA section cannot appear in this state.");
1548
1549                         ClearValueBuffer ();
1550
1551                         bool skip = false;
1552                         int ch = 0;
1553                         while (PeekChar () != -1) {
1554                                 if (!skip)
1555                                         ch = ReadChar ();
1556                                 skip = false;
1557
1558                                 if (ch == ']' && PeekChar () == ']') {
1559                                         ch = ReadChar (); // ']'
1560
1561                                         if (PeekChar () == '>') {
1562                                                 ReadChar (); // '>'
1563                                                 break;
1564                                         } else {
1565                                                 skip = true;
1566 //                                              AppendValueChar (']');
1567 //                                              AppendValueChar (']');
1568 //                                              ch = ReadChar ();
1569                                         }
1570                                 }
1571
1572                                 AppendValueChar ((char)ch);
1573                         }
1574
1575                         SetProperties (
1576                                 XmlNodeType.CDATA, // nodeType
1577                                 String.Empty, // name
1578                                 false, // isEmptyElement
1579                                 valueBuffer, // value
1580                                 true // clearAttributes
1581                         );
1582                 }
1583
1584                 // The reader is positioned on the first character after
1585                 // the leading '<!DOCTYPE'.
1586                 private void ReadDoctypeDecl ()
1587                 {
1588                         switch (currentState) {
1589                         case XmlNodeType.DocumentType:
1590                         case XmlNodeType.Element:
1591                         case XmlNodeType.EndElement:
1592                                 throw new XmlException (this as IXmlLineInfo,
1593                                         "Document type cannot appear in this state.");
1594                         }
1595                         currentState = XmlNodeType.DocumentType;
1596
1597                         string doctypeName = null;
1598                         string publicId = String.Empty;
1599                         string systemId = String.Empty;
1600                         int intSubsetStartLine = 0;
1601                         int intSubsetStartColumn = 0;
1602
1603                         SkipWhitespace ();
1604                         doctypeName = ReadName ();
1605                         SkipWhitespace ();
1606                         switch(PeekChar ())
1607                         {
1608                         case 'S':
1609                                 systemId = ReadSystemLiteral (true);
1610                                 break;
1611                         case 'P':
1612                                 publicId = ReadPubidLiteral ();
1613                                 SkipWhitespace ();
1614                                 systemId = ReadSystemLiteral (false);
1615                                 break;
1616                         }
1617                         SkipWhitespace ();
1618
1619
1620                         if(PeekChar () == '[')
1621                         {
1622                                 // read markupdecl etc. or end of decl
1623                                 ReadChar ();
1624                                 intSubsetStartLine = this.LineNumber;
1625                                 intSubsetStartColumn = this.LinePosition;
1626                                 int startPos = currentTag.Length;
1627                                 isIntSubset = true;
1628                                 ReadInternalSubset ();
1629                                 isIntSubset = false;
1630                                 int endPos = currentTag.Length - 1;
1631                                 parserContext.InternalSubset = currentTag.ToString (startPos, endPos - startPos);
1632                         }
1633                         // end of DOCTYPE decl.
1634                         SkipWhitespace ();
1635                         Expect ('>');
1636
1637                         GenerateDTDObjectModel (doctypeName, publicId,
1638                                 systemId, parserContext.InternalSubset,
1639                                 intSubsetStartLine, intSubsetStartColumn);
1640
1641                         // set properties for <!DOCTYPE> node
1642                         SetProperties (
1643                                 XmlNodeType.DocumentType, // nodeType
1644                                 doctypeName, // name
1645                                 false, // isEmptyElement
1646                                 parserContext.InternalSubset, // value
1647                                 true // clearAttributes
1648                                 );
1649                 }
1650
1651                 internal DTDObjectModel GenerateDTDObjectModel (string name, string publicId,
1652                         string systemId, string internalSubset)
1653                 {
1654                         return GenerateDTDObjectModel (name, publicId, systemId, internalSubset, 0, 0);
1655                 }
1656
1657                 internal DTDObjectModel GenerateDTDObjectModel (string name, string publicId,
1658                         string systemId, string internalSubset, int intSubsetStartLine, int intSubsetStartColumn)
1659                 {
1660                         // now compile DTD
1661                         DTD = new DTDObjectModel ();    // merges both internal and external subsets in the meantime,
1662                         DTD.Name = name;
1663                         int originalParserDepth = parserInputStack.Count;
1664                         if (internalSubset != null && internalSubset.Length > 0) {
1665                                 XmlParserInput original = currentInput;
1666                                 currentInput = new XmlParserInput (new StringReader (internalSubset), BaseURI, intSubsetStartLine, intSubsetStartColumn);
1667                                 do {
1668                                         CompileDTDSubset ();
1669                                         if (PeekChar () == -1 && parserInputStack.Count > 0)
1670                                                 PopParserInput ();
1671                                 } while (nodeType != XmlNodeType.None || parserInputStack.Count > originalParserDepth);
1672                                 if (dtdIncludeSect != 0)
1673                                         throw new XmlException (this as IXmlLineInfo,"INCLUDE section is not ended correctly.");
1674                                 currentInput = original;
1675                         }
1676                         if (systemId != null && systemId != String.Empty && resolver != null) {
1677                                 PushParserInput (systemId);
1678                                 do {
1679                                         this.CompileDTDSubset ();
1680                                         if (PeekChar () == -1 && parserInputStack.Count > 1)
1681                                                 PopParserInput ();
1682                                 } while (nodeType != XmlNodeType.None || parserInputStack.Count > originalParserDepth + 1);
1683                                 PopParserInput ();
1684                         }
1685
1686                         return DTD;
1687                 }
1688
1689                 private void PushParserInput (string url)
1690                 {
1691                         Uri baseUri = null;
1692                         try {
1693                                 baseUri = new Uri (BaseURI);
1694                         } catch (UriFormatException) {
1695                         }
1696
1697                         Uri absUri = resolver.ResolveUri (baseUri, url);
1698                         string absPath = absUri.ToString ();
1699
1700                         foreach (XmlParserInput i in parserInputStack.ToArray ()) {
1701                                 if (i.BaseURI == absPath)
1702                                         throw new XmlException (this as IXmlLineInfo, "Nested inclusion is not allowed: " + url);
1703                         }
1704                         parserInputStack.Push (currentInput);
1705                         currentInput = new XmlParserInput (new XmlStreamReader (url, false, resolver, BaseURI), absPath);
1706                         parserContext.PushScope ();
1707                         parserContext.BaseURI = absPath;
1708
1709                         maybeTextDecl = 2;
1710                 }
1711
1712                 private void PopParserInput ()
1713                 {
1714                         currentInput = parserInputStack.Pop () as XmlParserInput;
1715                         parserContext.PopScope ();
1716                 }
1717
1718                 private enum DtdInputState
1719                 {
1720                         Free = 1,
1721                         ElementDecl,
1722                         AttlistDecl,
1723                         EntityDecl,
1724                         NotationDecl,
1725                         PI,
1726                         Comment,
1727                         InsideSingleQuoted,
1728                         InsideDoubleQuoted,
1729                 }
1730
1731                 private class DtdInputStateStack
1732                 {
1733                         Stack intern = new Stack ();
1734                         public DtdInputStateStack ()
1735                         {
1736                                 Push (DtdInputState.Free);
1737                         }
1738
1739                         public DtdInputState Peek ()
1740                         {
1741                                 return (DtdInputState) intern.Peek ();
1742                         }
1743
1744                         public DtdInputState Pop ()
1745                         {
1746                                 return (DtdInputState) intern.Pop ();
1747                         }
1748
1749                         public void Push (DtdInputState val)
1750                         {
1751                                 intern.Push (val);
1752                         }
1753                 }
1754
1755
1756                 DtdInputStateStack stateStack = new DtdInputStateStack ();
1757                 DtdInputState State {
1758                         get { return stateStack.Peek (); }
1759                 }
1760
1761                 // Simply read but not generate any result.
1762                 private void ReadInternalSubset ()
1763                 {
1764                         bool continueParse = true;
1765
1766                         while (continueParse) {
1767                                 switch (ReadChar ()) {
1768                                 case ']':
1769                                         switch (State) {
1770                                         case DtdInputState.Free:
1771                                                 continueParse = false;
1772                                                 break;
1773                                         case DtdInputState.InsideDoubleQuoted:
1774                                                 continue;
1775                                         case DtdInputState.InsideSingleQuoted:
1776                                                 continue;
1777                                         default:
1778                                                 throw new XmlException (this as IXmlLineInfo,"unexpected end of file at DTD.");
1779                                         }
1780                                         break;
1781                                 case -1:
1782                                         throw new XmlException (this as IXmlLineInfo,"unexpected end of file at DTD.");
1783                                 case '<':
1784                                         if (State == DtdInputState.InsideDoubleQuoted ||
1785                                                 State == DtdInputState.InsideSingleQuoted)
1786                                                 continue;       // well-formed
1787                                         switch (ReadChar ()) {
1788                                         case '?':
1789                                                 stateStack.Push (DtdInputState.PI);
1790                                                 break;
1791                                         case '!':
1792                                                 switch (ReadChar ()) {
1793                                                 case 'E':
1794                                                         switch (ReadChar ()) {
1795                                                         case 'L':
1796                                                                 Expect ("EMENT");
1797                                                                 stateStack.Push (DtdInputState.ElementDecl);
1798                                                                 break;
1799                                                         case 'N':
1800                                                                 Expect ("TITY");
1801                                                                 stateStack.Push (DtdInputState.EntityDecl);
1802                                                                 break;
1803                                                         default:
1804                                                                 throw new XmlException (this as IXmlLineInfo,"unexpected token '<!E'.");
1805                                                         }
1806                                                         break;
1807                                                 case 'A':
1808                                                         Expect ("TTLIST");
1809                                                         stateStack.Push (DtdInputState.AttlistDecl);
1810                                                         break;
1811                                                 case 'N':
1812                                                         Expect ("OTATION");
1813                                                         stateStack.Push (DtdInputState.NotationDecl);
1814                                                         break;
1815                                                 case '-':
1816                                                         Expect ("-");
1817                                                         stateStack.Push (DtdInputState.Comment);
1818                                                         break;
1819                                                 }
1820                                                 break;
1821                                         default:
1822                                                 throw new XmlException (this as IXmlLineInfo,"unexpected '>'.");
1823                                         }
1824                                         break;
1825                                 case '\'':
1826                                         if (State == DtdInputState.InsideSingleQuoted)
1827                                                 stateStack.Pop ();
1828                                         else if (State != DtdInputState.InsideDoubleQuoted && State != DtdInputState.Comment)
1829                                                 stateStack.Push (DtdInputState.InsideSingleQuoted);
1830                                         break;
1831                                 case '"':
1832                                         if (State == DtdInputState.InsideDoubleQuoted)
1833                                                 stateStack.Pop ();
1834                                         else if (State != DtdInputState.InsideSingleQuoted && State != DtdInputState.Comment)
1835                                                 stateStack.Push (DtdInputState.InsideDoubleQuoted);
1836                                         break;
1837                                 case '>':
1838                                         switch (State) {
1839                                         case DtdInputState.ElementDecl:
1840                                                 goto case DtdInputState.NotationDecl;
1841                                         case DtdInputState.AttlistDecl:
1842                                                 goto case DtdInputState.NotationDecl;
1843                                         case DtdInputState.EntityDecl:
1844                                                 goto case DtdInputState.NotationDecl;
1845                                         case DtdInputState.NotationDecl:
1846                                                 stateStack.Pop ();
1847                                                 break;
1848                                         case DtdInputState.InsideDoubleQuoted:
1849                                                 continue;
1850                                         case DtdInputState.InsideSingleQuoted:
1851                                                 continue; // well-formed
1852                                         case DtdInputState.Comment:
1853                                                 continue;
1854                                         default:
1855                                                 throw new XmlException (this as IXmlLineInfo,"unexpected token '>'");
1856                                         }
1857                                         break;
1858                                 case '?':
1859                                         if (State == DtdInputState.PI) {
1860                                                 if (ReadChar () == '>')
1861                                                         stateStack.Pop ();
1862                                         }
1863                                         break;
1864                                 case '-':
1865                                         if (State == DtdInputState.Comment) {
1866                                                 if (PeekChar () == '-') {
1867                                                         ReadChar ();
1868                                                         Expect ('>');
1869                                                         stateStack.Pop ();
1870                                                 }
1871                                         }
1872                                         break;
1873                                 case '%':
1874                                         if (State != DtdInputState.Free && State != DtdInputState.EntityDecl && State != DtdInputState.Comment && State != DtdInputState.InsideDoubleQuoted && State != DtdInputState.InsideSingleQuoted)
1875                                                 throw new XmlException (this as IXmlLineInfo,"Parameter Entity Reference cannot appear as a part of markupdecl (see XML spec 2.8).");
1876                                         break;
1877                                 }
1878                         }
1879                 }
1880
1881                 // Read any one of following:
1882                 //   elementdecl, AttlistDecl, EntityDecl, NotationDecl,
1883                 //   PI, Comment, Parameter Entity, or doctype termination char(']')
1884                 //
1885                 // returns a node of some nodeType or null, setting nodeType.
1886                 //       (if None then ']' was found.)
1887                 private void CompileDTDSubset()
1888                 {
1889                         SkipWhitespace ();
1890                         switch(PeekChar ())
1891                         {
1892                         case -1:
1893                                 nodeType = XmlNodeType.None;
1894                                 break;
1895                         case '%':
1896                                 // It affects on entity references' well-formedness
1897                                 if (isIntSubset)
1898                                         DTD.InternalSubsetHasPEReference = true;
1899                                 TryExpandPERef ();
1900                                 break;
1901                         case '<':
1902                                 ReadChar ();
1903                                 switch(ReadChar ())
1904                                 {
1905                                 case '?':
1906                                         // Only read, no store.
1907                                         ReadProcessingInstruction ();
1908                                         break;
1909                                 case '!':
1910                                         CompileDeclaration ();
1911                                         break;
1912                                 default:
1913                                         throw new XmlException (this as IXmlLineInfo,"Syntax Error after '<' character.");
1914                                 }
1915                                 break;
1916                         case ']':
1917                                 // End of inclusion
1918                                 Expect ("]]>");
1919                                 dtdIncludeSect--;
1920                                 SkipWhitespace ();
1921                                 break;
1922                         default:
1923                                 throw new XmlException (this as IXmlLineInfo,String.Format ("Syntax Error inside doctypedecl markup : {0}({1})", PeekChar (), (char) PeekChar ()));
1924                         }
1925                 }
1926
1927                 private void CompileDeclaration ()
1928                 {
1929                         nodeType = XmlNodeType.DocumentType;    // Hack!!
1930                         switch(ReadChar ())
1931                         {
1932                         case '-':
1933                                 Expect ('-');
1934                                 // Only read, no store.
1935                                 ReadComment ();
1936                                 break;
1937                         case 'E':
1938                                 switch(ReadChar ())
1939                                 {
1940                                 case 'N':
1941                                         Expect ("TITY");
1942                                         SkipWhitespace ();
1943                                         LOOPBACK:
1944                                         if (PeekChar () == '%') {
1945                                                 ReadChar ();
1946                                                 if (!XmlConstructs.IsSpace (PeekChar ())) {
1947                                                         ExpandPERef ();
1948                                                         goto LOOPBACK;
1949 //                                                      throw new XmlException (this as IXmlLineInfo,"expected whitespace between '%' and name.");
1950                                                 } else {
1951                                                         SkipWhitespace ();
1952                                                         TryExpandPERef ();
1953                                                         if (XmlConstructs.IsName (PeekChar ()))
1954                                                         ReadParameterEntityDecl ();
1955                                                         else
1956                                                                 throw new XmlException (this as IXmlLineInfo,"expected name character");
1957                                                 }
1958                                                 break;
1959                                         }
1960                                         DTDEntityDeclaration ent = ReadEntityDecl ();
1961                                         if (DTD.EntityDecls [ent.Name] == null)
1962                                                 DTD.EntityDecls.Add (ent.Name, ent);
1963                                         break;
1964                                 case 'L':
1965                                         Expect ("EMENT");
1966                                         DTDElementDeclaration el = ReadElementDecl ();
1967                                         DTD.ElementDecls.Add (el.Name, el);
1968                                         break;
1969                                 default:
1970                                         throw new XmlException (this as IXmlLineInfo,"Syntax Error after '<!E' (ELEMENT or ENTITY must be found)");
1971                                 }
1972                                 break;
1973                         case 'A':
1974                                 Expect ("TTLIST");
1975                                 DTDAttListDeclaration atl = ReadAttListDecl ();
1976 //                              if (DTD.AttListDecls.ContainsKey (atl.Name))
1977                                         DTD.AttListDecls.Add (atl.Name, atl);
1978                                 break;
1979                         case 'N':
1980                                 Expect ("OTATION");
1981                                 DTDNotationDeclaration not = ReadNotationDecl ();
1982                                 DTD.NotationDecls.Add (not.Name, not);
1983                                 break;
1984                         case '[':
1985                                 // conditional sections
1986                                 SkipWhitespace ();
1987                                 TryExpandPERef ();
1988                                 SkipWhitespace ();
1989                                 Expect ('I');
1990                                 switch (ReadChar ()) {
1991                                 case 'N':
1992                                         Expect ("CLUDE");
1993                                         SkipWhitespace ();
1994                                         Expect ('[');
1995                                         dtdIncludeSect++;
1996                                         break;
1997                                 case 'G':
1998                                         Expect ("NORE");
1999                                         ReadIgnoreSect ();
2000                                         break;
2001                                 }
2002                                 break;
2003                         default:
2004                                 throw new XmlException (this as IXmlLineInfo,"Syntax Error after '<!' characters.");
2005                         }
2006                 }
2007
2008                 private void ReadIgnoreSect ()
2009                 {
2010                         bool skip = false;
2011                         SkipWhitespace ();
2012                         Expect ('[');
2013                         int dtdIgnoreSect = 1;
2014                         while (dtdIgnoreSect > 0) {
2015                                 switch (skip ? PeekChar () : ReadChar ()) {
2016                                 case -1:
2017                                         throw new XmlException (this as IXmlLineInfo,"Unexpected IGNORE section end.");
2018                                 case '<':
2019                                         if (ReadChar () == '!' && ReadChar () == '[')
2020                                                 dtdIgnoreSect++;
2021                                         break;
2022                                 case ']':
2023                                         if (ReadChar () == ']') {
2024                                                 if (ReadChar () == '>')
2025                                                         dtdIgnoreSect--;
2026                                                 else
2027                                                         skip = true;
2028                                         }
2029                                         break;
2030                                 }
2031                                 skip = false;
2032                         }
2033                 }
2034
2035                 // The reader is positioned on the head of the name.
2036                 private DTDElementDeclaration ReadElementDecl ()
2037                 {
2038                         DTDElementDeclaration decl = new DTDElementDeclaration (DTD);
2039                         SkipWhitespace ();
2040                         TryExpandPERef ();
2041                         decl.Name = ReadName ();
2042                         SkipWhitespace ();
2043                         TryExpandPERef ();
2044                         ReadContentSpec (decl);
2045                         SkipWhitespace ();
2046                         // This expanding is only allowed as a non-validating parser.
2047                         TryExpandPERef ();
2048                         Expect ('>');
2049                         return decl;
2050                 }
2051
2052                 // read 'children'(BNF) of contentspec
2053                 private void ReadContentSpec (DTDElementDeclaration decl)
2054                 {
2055                         switch(PeekChar ())
2056                         {
2057                         case 'E':
2058                                 decl.IsEmpty = true;
2059                                 Expect ("EMPTY");
2060                                 break;
2061                         case 'A':
2062                                 decl.IsAny = true;
2063                                 Expect ("ANY");
2064                                 break;
2065                         case '(':
2066                                 DTDContentModel model = decl.ContentModel;
2067                                 ReadChar ();
2068                                 SkipWhitespace ();
2069                                 TryExpandPERef ();
2070                                 if(PeekChar () == '#') {
2071                                         // Mixed Contents. "#PCDATA" must appear first.
2072                                         decl.IsMixedContent = true;
2073                                         Expect ("#PCDATA");
2074                                         SkipWhitespace ();
2075                                         TryExpandPERef ();
2076                                         SkipWhitespace ();
2077                                         while(PeekChar () != ')') {
2078                                                 Expect('|');
2079                                                 SkipWhitespace ();
2080                                                 TryExpandPERef ();
2081                                                 SkipWhitespace ();
2082                                                 DTDContentModel elem = new DTDContentModel (DTD, decl.Name);
2083                                                 model.ElementName = ReadName ();
2084                                                 model.ChildModels.Add (elem);
2085                                                 SkipWhitespace ();
2086                                                 TryExpandPERef ();
2087                                         }
2088                                         Expect (')');
2089                                         if (model.ChildModels.Count > 0) {
2090                                                 Expect ('*');
2091                                                 model.Occurence = DTDOccurence.ZeroOrMore;
2092                                         }
2093                                         else if (PeekChar () == '*')
2094                                                 Expect ('*');
2095                                 } else {
2096                                         // Non-Mixed Contents
2097                                         model.ChildModels.Add (ReadCP (decl));
2098                                         SkipWhitespace ();
2099
2100                                         do {    // copied from ReadCP() ...;-)
2101                                                 TryExpandPERef ();
2102                                                 SkipWhitespace ();
2103                                                 if(PeekChar ()=='|') {
2104                                                         // CPType=Or
2105                                                         model.OrderType = DTDContentOrderType.Or;
2106                                                         ReadChar ();
2107                                                         SkipWhitespace ();
2108                                                         model.ChildModels.Add (ReadCP (decl));
2109                                                         SkipWhitespace ();
2110                                                 }
2111                                                 else if(PeekChar () == ',')
2112                                                 {
2113                                                         // CPType=Seq
2114                                                         model.OrderType = DTDContentOrderType.Seq;
2115                                                         ReadChar ();
2116                                                         SkipWhitespace ();
2117                                                         model.ChildModels.Add (ReadCP (decl));
2118                                                         SkipWhitespace ();
2119                                                 }
2120                                                 else
2121                                                         break;
2122                                         }
2123                                         while(true);
2124
2125                                         Expect (')');
2126                                         switch(PeekChar ())
2127                                         {
2128                                         case '?':
2129                                                 model.Occurence = DTDOccurence.Optional;
2130                                                 ReadChar ();
2131                                                 break;
2132                                         case '*':
2133                                                 model.Occurence = DTDOccurence.ZeroOrMore;
2134                                                 ReadChar ();
2135                                                 break;
2136                                         case '+':
2137                                                 model.Occurence = DTDOccurence.OneOrMore;
2138                                                 ReadChar ();
2139                                                 break;
2140                                         }
2141                                         SkipWhitespace ();
2142                                 }
2143                                 SkipWhitespace ();
2144                                 break;
2145                         }
2146                 }
2147
2148                 // Read 'cp' (BNF) of contentdecl (BNF)
2149                 private DTDContentModel ReadCP (DTDElementDeclaration elem)
2150                 {
2151                         DTDContentModel model = null;
2152                         TryExpandPERef ();
2153                         if(PeekChar () == '(') {
2154                                 model = new DTDContentModel (DTD, elem.Name);
2155                                 ReadChar ();
2156                                 SkipWhitespace ();
2157                                 model.ChildModels.Add (ReadCP (elem));
2158                                 SkipWhitespace ();
2159                                 do {
2160                                         TryExpandPERef ();
2161                                         SkipWhitespace ();
2162                                         if(PeekChar ()=='|') {
2163                                                 // CPType=Or
2164                                                 model.OrderType = DTDContentOrderType.Or;
2165                                                 ReadChar ();
2166                                                 SkipWhitespace ();
2167                                                 model.ChildModels.Add (ReadCP (elem));
2168                                                 SkipWhitespace ();
2169                                         }
2170                                         else if(PeekChar () == ',') {
2171                                                 // CPType=Seq
2172                                                 model.OrderType = DTDContentOrderType.Seq;
2173                                                 ReadChar ();
2174                                                 SkipWhitespace ();
2175                                                 model.ChildModels.Add (ReadCP (elem));
2176                                                 SkipWhitespace ();
2177                                         }
2178                                         else
2179                                                 break;
2180                                 }
2181                                 while(true);
2182                                 SkipWhitespace ();
2183                                 Expect (')');
2184                         }
2185                         else {
2186                                 TryExpandPERef ();
2187                                 model = new DTDContentModel (DTD, elem.Name);
2188                                 model.ElementName = ReadName ();
2189                         }
2190
2191                         switch(PeekChar ()) {
2192                         case '?':
2193                                 model.Occurence = DTDOccurence.Optional;
2194                                 ReadChar ();
2195                                 break;
2196                         case '*':
2197                                 model.Occurence = DTDOccurence.ZeroOrMore;
2198                                 ReadChar ();
2199                                 break;
2200                         case '+':
2201                                 model.Occurence = DTDOccurence.OneOrMore;
2202                                 ReadChar ();
2203                                 break;
2204                         }
2205                         return model;
2206                 }
2207
2208                 // The reader is positioned on the first name char.
2209                 private void ReadParameterEntityDecl ()
2210                 {
2211                         DTDParameterEntityDeclaration decl = 
2212                                 new DTDParameterEntityDeclaration();
2213                         decl.BaseURI = BaseURI;
2214
2215                         decl.Name = ReadName ();
2216                         SkipWhitespace ();
2217
2218                         if (PeekChar () == 'S' || PeekChar () == 'P') {
2219 //                              throw new NotImplementedException ("External parameter entity reference is not implemented yet.");
2220                                 // read publicId/systemId
2221                                 ReadExternalID ();
2222                                 decl.PublicId = attributes ["PUBLIC"] as string;
2223                                 decl.SystemId = attributes ["SYSTEM"] as string;
2224                                 SkipWhitespace ();
2225                         }
2226                         else {
2227                                 TryExpandPERef ();
2228                                 int quoteChar = ReadChar ();
2229                                 int start = currentTag.Length;
2230                                 while (true) {
2231                                         SkipWhitespace ();
2232                                         int c = PeekChar ();
2233                                         if ((int) c == -1)
2234                                                 throw new XmlException ("unexpected end of stream in entity value definition.");
2235                                         switch (c) {
2236                                         case '"':
2237                                                 ReadChar ();
2238                                                 if (quoteChar == '"') goto SKIP;
2239                                                 break;
2240                                         case '\'':
2241                                                 ReadChar ();
2242                                                 if (quoteChar == '\'') goto SKIP;
2243                                                 break;
2244                                         case '%':
2245                                                 ImportAsPERef ();
2246                                                 break;
2247                                         default:
2248                                                 ReadChar ();
2249                                                 break;
2250                                         }
2251                                 }
2252                                 SKIP:
2253                                 decl.Value = currentTag.ToString (start, currentTag.Length - start - 1);
2254                         }
2255                         SkipWhitespace ();
2256                         Expect ('>');
2257                         if (parameterEntities [decl.Name] == null) {
2258                                 parameterEntities.Add (decl.Name, decl);
2259                         }
2260                 }
2261
2262                 // reader is positioned on '%'
2263                 private void ImportAsPERef ()
2264                 {
2265                         StringBuilder sb = null;
2266                         int peRefStart = currentTag.Length;
2267                         string appendStr = "";
2268                         ReadChar ();
2269                         string peName = ReadName ();
2270                         Expect (';');
2271                         DTDParameterEntityDeclaration peDecl =
2272                                 this.parameterEntities [peName] as DTDParameterEntityDeclaration;
2273                         if (peDecl == null)
2274                                 throw new XmlException (this as IXmlLineInfo,"Parameter entity " + peName + " not found.");
2275                         if (peDecl.SystemId != null) {
2276                                 PushParserInput (peDecl.SystemId);
2277                                 if (sb == null)
2278                                         sb = new StringBuilder ();
2279                                 else
2280                                         sb.Length = 0;
2281                                 while (PeekChar () != -1)
2282                                         sb.Append (ReadChar ());
2283                                 PopParserInput ();
2284                                 appendStr = sb.ToString ();
2285                         } else {
2286                                 appendStr = peDecl.Value;
2287                         }
2288                         currentTag.Remove (peRefStart,
2289                                 currentTag.Length - peRefStart);
2290                         currentTag.Append (Dereference (appendStr));
2291                 }
2292
2293                 // The reader is positioned on the head of the name.
2294                 private DTDEntityDeclaration ReadEntityDecl ()
2295                 {
2296                         DTDEntityDeclaration decl = new DTDEntityDeclaration ();
2297                         decl.IsInternalSubset = isIntSubset;
2298                         decl.Name = ReadName ();
2299                         SkipWhitespace ();
2300                         TryExpandPERef ();
2301                         SkipWhitespace ();
2302
2303                         if (PeekChar () == 'S' || PeekChar () == 'P') {
2304                                 // external entity
2305                                 ReadExternalID ();
2306                                 decl.PublicId = attributes ["PUBLIC"] as string;
2307                                 decl.SystemId = attributes ["SYSTEM"] as string;
2308                                 if (SkipWhitespace ()) {
2309                                         if (PeekChar () == 'N') {
2310                                                 // NDataDecl
2311                                                 Expect ("NDATA");
2312                                                 if (SkipWhitespace ())
2313                                                         decl.NotationName = ReadName ();        // ndata_name
2314                                         }
2315                                 }
2316                         }
2317                         else {
2318                                 // general entity
2319                                 decl.EntityValue = ReadEntityValueDecl ();
2320                         }
2321                         SkipWhitespace ();
2322                         // This expanding is only allowed as a non-validating parser.
2323                         TryExpandPERef ();
2324                         Expect ('>');
2325                         return decl;
2326                 }
2327
2328                 private string ReadEntityValueDecl ()
2329                 {
2330                         SkipWhitespace ();
2331                         // quotation char will be finally removed on unescaping
2332                         int quoteChar = ReadChar ();
2333                         int start = currentTag.Length;
2334                         if (quoteChar != '\'' && quoteChar != '"')
2335                                 throw new XmlException ("quotation char was expected.");
2336
2337                         while (PeekChar () != quoteChar) {
2338                                 switch (PeekChar ()) {
2339                                 case '%':
2340                                         this.ImportAsPERef ();
2341                                         continue;
2342                                 case '&':
2343                                         ReadChar ();
2344                                         ReadReference (true);
2345                                         break;
2346                                 case -1:
2347                                         throw new XmlException ("unexpected end of stream.");
2348                                 default:
2349                                         ReadChar ();
2350                                         break;
2351                                 }
2352                         }
2353                         string value = Dereference (currentTag.ToString (start, currentTag.Length - start));
2354                         Expect (quoteChar);
2355                         return value;
2356                 }
2357
2358                 private DTDAttListDeclaration ReadAttListDecl ()
2359                 {
2360                         SkipWhitespace ();
2361                         TryExpandPERef ();
2362                         string name = ReadName ();      // target element name
2363                         DTDAttListDeclaration decl =
2364                                 DTD.AttListDecls [name] as DTDAttListDeclaration;
2365                         if (decl == null)
2366                                 decl = new DTDAttListDeclaration ();
2367                         decl.Name = name;
2368
2369                         SkipWhitespace ();
2370                         TryExpandPERef ();
2371                         SkipWhitespace ();
2372
2373                         while (XmlConstructs.IsName ((char) PeekChar ())) {
2374                                 DTDAttributeDefinition def = ReadAttributeDefinition ();
2375                                 if (decl [def.Name] == null)
2376                                         decl.Add (def);
2377                                 SkipWhitespace ();
2378                                 TryExpandPERef ();
2379                                 SkipWhitespace ();
2380                         }
2381                         SkipWhitespace ();
2382                         // This expanding is only allowed as a non-validating parser.
2383                         TryExpandPERef ();
2384                         Expect ('>');
2385                         return decl;
2386                 }
2387
2388                 private DTDAttributeDefinition ReadAttributeDefinition ()
2389                 {
2390                         DTDAttributeDefinition def = new DTDAttributeDefinition ();
2391
2392                         // attr_name
2393                         TryExpandPERef ();
2394                         def.Name = ReadName ();
2395                         SkipWhitespace ();
2396
2397                         // attr_value
2398                         TryExpandPERef ();
2399                         switch(PeekChar ()) {
2400                         case 'C':       // CDATA
2401                                 Expect ("CDATA");
2402                                 def.Datatype = XmlSchemaDatatype.FromName ("normalizedString");
2403                                 break;
2404                         case 'I':       // ID, IDREF, IDREFS
2405                                 Expect ("ID");
2406                                 if(PeekChar () == 'R') {
2407                                         Expect ("REF");
2408                                         if(PeekChar () == 'S') {
2409                                                 // IDREFS
2410                                                 ReadChar ();
2411                                                 def.Datatype = XmlSchemaDatatype.FromName ("IDREFS");
2412                                         }
2413                                         else    // IDREF
2414                                                 def.Datatype = XmlSchemaDatatype.FromName ("IDREF");
2415                                 }
2416                                 else    // ID
2417                                         def.Datatype = XmlSchemaDatatype.FromName ("ID");
2418                                 break;
2419                         case 'E':       // ENTITY, ENTITIES
2420                                 Expect ("ENTIT");
2421                                 switch(ReadChar ()) {
2422                                         case 'Y':       // ENTITY
2423                                                 def.Datatype = XmlSchemaDatatype.FromName ("ENTITY");
2424                                                 break;
2425                                         case 'I':       // ENTITIES
2426                                                 Expect ("ES");
2427                                                 def.Datatype = XmlSchemaDatatype.FromName ("ENTITIES");
2428                                                 break;
2429                                 }
2430                                 break;
2431                         case 'N':       // NMTOKEN, NMTOKENS, NOTATION
2432                                 ReadChar ();
2433                                 switch(PeekChar ()) {
2434                                 case 'M':
2435                                         Expect ("MTOKEN");
2436                                         if(PeekChar ()=='S') {  // NMTOKENS
2437                                                 ReadChar ();
2438                                                 def.Datatype = XmlSchemaDatatype.FromName ("NMTOKENS");
2439                                         }
2440                                         else    // NMTOKEN
2441                                                 def.Datatype = XmlSchemaDatatype.FromName ("NMTOKEN");
2442                                         break;
2443                                 case 'O':
2444                                         Expect ("OTATION");
2445                                         def.Datatype = XmlSchemaDatatype.FromName ("NOTATION");
2446                                         SkipWhitespace ();
2447                                         Expect ('(');
2448                                         SkipWhitespace ();
2449                                         def.EnumeratedNotations.Add (ReadName ());              // notation name
2450                                         SkipWhitespace ();
2451                                         while(PeekChar () == '|') {
2452                                                 ReadChar ();
2453                                                 SkipWhitespace ();
2454                                                 def.EnumeratedNotations.Add (ReadName ());      // notation name
2455                                                 SkipWhitespace ();
2456                                         }
2457                                         Expect (')');
2458                                         break;
2459                                 default:
2460                                         throw new XmlException ("attribute declaration syntax error.");
2461                                 }
2462                                 break;
2463                         default:        // Enumerated Values
2464                                 TryExpandPERef ();
2465                                 Expect ('(');
2466                                 SkipWhitespace ();
2467                                 def.EnumeratedAttributeDeclaration.Add (ReadNmToken ());                // enum value
2468                                 SkipWhitespace ();
2469                                 while(PeekChar () == '|') {
2470                                         ReadChar ();
2471                                         SkipWhitespace ();
2472                                         def.EnumeratedAttributeDeclaration.Add (ReadNmToken ());        // enum value
2473                                         SkipWhitespace ();
2474                                 }
2475                                 Expect (')');
2476                                 break;
2477                         }
2478                         SkipWhitespace ();
2479
2480                         TryExpandPERef ();
2481
2482                         // def_value
2483                         if(PeekChar () == '#')
2484                         {
2485                                 ReadChar ();
2486                                 switch(PeekChar ())
2487                                 {
2488                                 case 'R':
2489                                         Expect ("REQUIRED");
2490                                         def.OccurenceType = DTDAttributeOccurenceType.Required;
2491                                         break;
2492                                 case 'I':
2493                                         Expect ("IMPLIED");
2494                                         def.OccurenceType = DTDAttributeOccurenceType.Optional;
2495                                         break;
2496                                 case 'F':
2497                                         Expect ("FIXED");
2498                                         def.OccurenceType = DTDAttributeOccurenceType.Fixed;
2499                                         SkipWhitespace ();
2500                                         def.UnresolvedDefaultValue = ReadAttribute ();
2501                                         break;
2502                                 }
2503                         } else {
2504                                 // one of the enumerated value
2505                                 if (PeekChar () == -1) {
2506                                         PopParserInput ();
2507                                 }
2508                                 SkipWhitespace ();
2509                                 def.UnresolvedDefaultValue = ReadAttribute ();
2510                         }
2511
2512                         return def;
2513                 }
2514
2515                 private DTDNotationDeclaration ReadNotationDecl()
2516                 {
2517                         DTDNotationDeclaration decl = new DTDNotationDeclaration ();
2518                         SkipWhitespace ();
2519                         decl.Name = ReadName ();        // notation name
2520                         if (namespaces) {       // copy from SetProperties ;-)
2521                                 int indexOfColon = decl.Name.IndexOf (':');
2522
2523                                 if (indexOfColon == -1) {
2524                                         decl.Prefix = String.Empty;
2525                                         decl.LocalName = decl.Name;
2526                                 } else {
2527                                         decl.Prefix = decl.Name.Substring (0, indexOfColon);
2528                                         decl.LocalName = decl.Name.Substring (indexOfColon + 1);
2529                                 }
2530                         } else {
2531                                 decl.Prefix = String.Empty;
2532                                 decl.LocalName = decl.Name;
2533                         }
2534
2535                         SkipWhitespace ();
2536                         if(PeekChar () == 'P') {
2537                                 decl.PublicId = ReadPubidLiteral ();
2538                                 SkipWhitespace ();
2539                                 if (PeekChar () == '\'' || PeekChar () == '"') {
2540                                         decl.SystemId = ReadSystemLiteral (false);
2541                                         SkipWhitespace ();
2542                                 }
2543                         } else if(PeekChar () == 'S') {
2544                                 decl.SystemId = ReadSystemLiteral (true);
2545                                 SkipWhitespace ();
2546                         }
2547                         if(decl.PublicId == null && decl.SystemId == null)
2548                                 throw new XmlException ("public or system declaration required for \"NOTATION\" declaration.");
2549                         // This expanding is only allowed as a non-validating parser.
2550                         TryExpandPERef ();
2551                         Expect ('>');
2552                         return decl;
2553                 }
2554
2555                 private void TryExpandPERef ()
2556                 {
2557                         if (PeekChar () == '%') {
2558                                 ReadChar ();
2559                                 if (!XmlConstructs.IsName (PeekChar ()))
2560                                         return;
2561                                 ExpandPERef ();
2562                         }
2563                 }
2564
2565                 // reader is positioned on the first letter of the name.
2566                 private void ExpandPERef ()
2567                 {
2568                         ExpandPERef (true);
2569                 }
2570
2571                 private void ExpandPERef (bool attachSpace)
2572                 {
2573                         string peName = ReadName ();
2574                         Expect (";");
2575                         ExpandNamedPERef (peName, attachSpace);
2576                 }
2577
2578                 private void ExpandNamedPERef (string peName, bool attachSpace)
2579                 {
2580                         DTDParameterEntityDeclaration decl =
2581                                 parameterEntities [peName] as DTDParameterEntityDeclaration;
2582                         if (decl == null)
2583                                 throw new XmlException ("undeclared parameter entity: '" + peName + "'");
2584                         if (decl.SystemId != null) {
2585                                 PushParserInput (decl.SystemId);
2586                         }
2587                         // add buffer
2588                         else
2589                                 currentInput.InsertParameterEntityBuffer (attachSpace ? " " + Dereference (decl.Value) + " " : decl.Value);
2590                         SkipWhitespace ();      // is it ok?
2591 //                      while (PeekChar () == '%')
2592 //                              TryExpandPERef ();      // recursive
2593                 }
2594
2595                 private void ReadExternalID() {
2596                         switch(PeekChar ()) {
2597                         case 'S':
2598                                 attributes ["PUBLIC"] = null;
2599                                 attributes ["SYSTEM"] = ReadSystemLiteral (true);
2600                                 break;
2601                         case 'P':
2602                                 attributes ["PUBLIC"] = ReadPubidLiteral ();
2603                                 SkipWhitespace ();
2604                                 attributes ["SYSTEM"] = ReadSystemLiteral (false);
2605                                 break;
2606                         }
2607                 }
2608
2609                 // The reader is positioned on the first 'S' of "SYSTEM".
2610                 private string ReadSystemLiteral (bool expectSYSTEM)
2611                 {
2612                         if(expectSYSTEM)
2613                                 Expect ("SYSTEM");
2614                         SkipWhitespace ();
2615                         int quoteChar = ReadChar ();    // apos or quot
2616                         int startPos = currentTag.Length;
2617                         int c = 0;
2618                         while(c != quoteChar) {
2619                                 c = ReadChar ();
2620                                 if(c < 0) throw new XmlException (this as IXmlLineInfo,"Unexpected end of stream in ExternalID.");
2621                         }
2622                         return currentTag.ToString (startPos, currentTag.Length - 1 - startPos);
2623                 }
2624
2625                 private string ReadPubidLiteral()
2626                 {
2627                         Expect ("PUBLIC");
2628                         SkipWhitespace ();
2629                         int quoteChar = ReadChar ();
2630                         int startPos = currentTag.Length;
2631                         int c = 0;
2632                         while(c != quoteChar)
2633                         {
2634                                 c = ReadChar ();
2635                                 if(c < 0) throw new XmlException (this as IXmlLineInfo,"Unexpected end of stream in ExternalID.");
2636                                 if(c != quoteChar && !XmlConstructs.IsPubid (c))
2637                                         throw new XmlException (this as IXmlLineInfo,"character '" + (char)c + "' not allowed for PUBLIC ID");
2638                         }
2639                         return currentTag.ToString (startPos, currentTag.Length - 1 - startPos);
2640                 }
2641
2642                 // The reader is positioned on the first character
2643                 // of the name.
2644                 internal string ReadName ()
2645                 {
2646                         return ReadNameOrNmToken(false);
2647                 }
2648
2649                 // The reader is positioned on the first character
2650                 // of the name.
2651                 private string ReadNmToken ()
2652                 {
2653                         return ReadNameOrNmToken(true);
2654                 }
2655
2656                 private string ReadNameOrNmToken(bool isNameToken)
2657                 {
2658                         int ch = PeekChar ();
2659                         if(isNameToken) {
2660                                 if (!XmlConstructs.IsName ((char) ch))
2661                                         throw new XmlException (this as IXmlLineInfo,String.Format ("a nmtoken did not start with a legal character {0} ({1})", ch, (char)ch));
2662                         }
2663                         else {
2664                                 if (!XmlConstructs.IsNameStart ((char) ch))
2665                                         throw new XmlException (this as IXmlLineInfo,String.Format ("a name did not start with a legal character {0} ({1})", ch, (char)ch));
2666                         }
2667
2668                         nameLength = 0;
2669
2670                         AppendNameChar (ReadChar ());
2671
2672                         while (XmlConstructs.IsName (PeekChar ())) {
2673                                 AppendNameChar (ReadChar ());
2674                         }
2675
2676                         return CreateNameString ();
2677                 }
2678
2679                 // Read the next character and compare it against the
2680                 // specified character.
2681                 private void Expect (int expected)
2682                 {
2683                         int ch = ReadChar ();
2684
2685                         if (ch != expected) {
2686                                 throw new XmlException (this as IXmlLineInfo,
2687                                         String.Format (
2688                                                 "expected '{0}' ({1:X}) but found '{2}' ({3:X})",
2689                                                 (char)expected,
2690                                                 expected,
2691                                                 (char)ch,
2692                                                 ch));
2693                         }
2694                 }
2695
2696                 private void Expect (string expected)
2697                 {
2698                         int len = expected.Length;
2699                         for(int i=0; i< len; i++)
2700                                 Expect (expected[i]);
2701                 }
2702
2703                 // Does not consume the first non-whitespace character.
2704                 private bool SkipWhitespace ()
2705                 {
2706                         //FIXME: Should not skip if whitespaceHandling == WhiteSpaceHandling.None
2707                         bool skipped = XmlConstructs.IsSpace (PeekChar ());
2708                         while (XmlConstructs.IsSpace (PeekChar ()))
2709                                 ReadChar ();
2710                         return skipped;
2711                 }
2712
2713                 private bool ReadWhitespace ()
2714                 {
2715                         if (currentState == XmlNodeType.None)
2716                                 currentState = XmlNodeType.XmlDeclaration;
2717
2718                         ClearValueBuffer ();
2719                         int ch = PeekChar ();
2720                         do {
2721                                 AppendValueChar (ReadChar ());
2722                         } while ((ch = PeekChar ()) != -1 && XmlConstructs.IsSpace (ch));
2723
2724                         if (currentState == XmlNodeType.Element && ch != -1 && ch != '<')
2725                                 ReadText (false);
2726                         else
2727                                 SetProperties (XmlNodeType.Whitespace,
2728                                                String.Empty,
2729                                                false,
2730                                                valueBuffer,
2731                                                true);
2732
2733                         return (PeekChar () != -1);
2734                 }
2735
2736                 // read entity reference from attribute string and if parsable then return the value.
2737                 private string ReadAttributeValueReference ()
2738                 {
2739                         int endEntityPosition = attributeString.IndexOf(';',
2740                                 attributeValuePos);
2741                         if (endEntityPosition < 0)
2742                                 throw new XmlException ("Insufficient markup of entity reference");
2743                         string entityName = attributeString.Substring (attributeValuePos + 1,
2744                                 endEntityPosition - attributeValuePos - 1);
2745
2746                         attributeValuePos = endEntityPosition + 1;
2747
2748                         if(entityName [0] == '#') {
2749                                 char c;
2750                                 // character entity
2751                                 if(entityName [1] == 'x') {
2752                                         // hexadecimal
2753                                         c = (char) int.Parse ("0" + entityName.Substring (2),
2754                                                 System.Globalization.NumberStyles.HexNumber);
2755                                 } else {
2756                                         // decimal
2757                                         c = (char) int.Parse (entityName.Substring (1));
2758                                 }
2759                                 return c.ToString();
2760                         }
2761                         else {
2762                                 switch(entityName)
2763                                 {
2764                                 case "lt": return "<";
2765                                 case "gt": return ">";
2766                                 case "amp": return "&";
2767                                 case "quot": return "\"";
2768                                 case "apos": return "'";
2769                                 default: return null;
2770                                 }
2771                         }
2772                 }
2773
2774                 private string UnescapeAttributeValue (string unresolved)
2775                 {
2776                         if(unresolved == null) return null;
2777
2778                         // trim start/end edge of quotation character.
2779                         return Dereference (unresolved.Substring (1, unresolved.Length - 2));
2780                 }
2781
2782                 private string Dereference (string unresolved)
2783                 {
2784                         StringBuilder resolved = new StringBuilder();
2785                         int pos = 0;
2786                         int next = unresolved.IndexOf ('&');
2787                         if(next < 0)
2788                                 return unresolved;
2789
2790                         while(next >= 0) {
2791                                 if(pos < next)
2792                                         resolved.Append (unresolved.Substring (pos, next - pos));// - 1);
2793                                 int endPos = unresolved.IndexOf (';', next+1);
2794                                 string entityName =
2795                                         unresolved.Substring (next + 1, endPos - next - 1);
2796                                 if(entityName [0] == '#') {
2797                                         char c;
2798                                         // character entity
2799                                         if(entityName [1] == 'x') {
2800                                                 // hexadecimal
2801                                                 c = (char) int.Parse ("0" + entityName.Substring (2),
2802                                                         System.Globalization.NumberStyles.HexNumber);
2803                                         } else {
2804                                                 // decimal
2805                                                 c = (char) int.Parse (entityName.Substring (1));
2806                                         }
2807                                         resolved.Append (c);
2808                                 } else {
2809                                         switch(entityName) {
2810                                         case "lt": resolved.Append ("<"); break;
2811                                         case "gt": resolved.Append (">"); break;
2812                                         case "amp": resolved.Append ("&"); break;
2813                                         case "quot": resolved.Append ("\""); break;
2814                                         case "apos": resolved.Append ("'"); break;
2815                                         // With respect to "Value", MS document is helpless
2816                                         // and the implemention returns inconsistent value
2817                                         // (e.g. XML: "&ent; &amp;ent;" ---> Value: "&ent; &ent;".)
2818                                         default: resolved.Append ("&" + entityName + ";"); break;
2819                                         }
2820                                 }
2821                                 pos = endPos + 1;
2822                                 if(pos > unresolved.Length)
2823                                         break;
2824                                 next = unresolved.IndexOf('&', pos);
2825                         }
2826                         resolved.Append (unresolved.Substring(pos));
2827
2828                         return resolved.ToString();
2829                 }
2830
2831                 #endregion
2832         }
2833 }