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