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