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