2003-03-15 Atsushi Enomoto <ginga@kit.hi-ho.ne.jp>
[mono.git] / mcs / class / System.XML / System.Xml / XmlTextReader.cs
1 //
2 // System.Xml.XmlTextReader
3 //
4 // Author:
5 //   Jason Diamond (jason@injektilo.org)
6 //   Adam Treat (manyoso@yahoo.com)
7 //
8 // (C) 2001, 2002 Jason Diamond  http://injektilo.org/
9 //
10
11 // FIXME:
12 //   This can only parse basic XML: elements, attributes, processing
13 //   instructions, and comments are OK.
14 //
15 //   It barfs on DOCTYPE declarations.
16 //     => No barfing, but parsing is incomplete.
17 //        DTD nodes are not still created.
18 //
19 //   There's also no checking being done for validity.
20 //
21 //   More checking should be done for well-formedness.
22 //
23 //   NameTables aren't being used everywhere yet.
24 //
25 //   Some thought needs to be given to performance. There's too many
26 //   strings being allocated.
27 //
28 //   Some of the MoveTo methods haven't been implemented yet.
29 //
30 //   xml:space, xml:lang, and xml:base aren't being tracked.
31 //
32
33 using System;
34 using System.Collections;
35 using System.IO;
36 using System.Text;
37
38 namespace System.Xml
39 {
40         public class XmlTextReader : XmlReader, IXmlLineInfo
41         {
42                 WhitespaceHandling whitespaceHandling = WhitespaceHandling.All;
43                 #region Constructors
44
45                 protected XmlTextReader ()
46                 {
47                 }
48
49                 public XmlTextReader (Stream input)
50                         : this (new StreamReader (input))
51                 {
52                 }
53
54                 public XmlTextReader (string url)
55                         : this(url, new NameTable ())
56                 {
57                 }
58
59                 public XmlTextReader (TextReader input)
60                         : this (input, new NameTable ())
61                 {
62                 }
63
64                 protected XmlTextReader (XmlNameTable nt)
65                         : this (String.Empty, null, XmlNodeType.None, null)
66                 {
67                 }
68
69                 public XmlTextReader (Stream input, XmlNameTable nt)
70                         : this(new StreamReader (input), nt)
71                 {
72                 }
73
74                 public XmlTextReader (string url, Stream input)
75                         : this (url, new StreamReader (input))
76                 {
77                 }
78
79                 public XmlTextReader (string url, TextReader input)
80                         : this (url, input, new NameTable ())
81                 {
82                 }
83
84                 [MonoTODO("Non-filename-url must be supported. Waiting for WebClient")]
85                 public XmlTextReader (string url, XmlNameTable nt)
86                         // : this(url, new StreamReader ((Stream)new XmlUrlResolver ().GetEntity (new Uri (url), null, typeof(Stream))), nt)
87                         : this (url, new StreamReader (url), nt)
88                 {
89                 }
90
91                 public XmlTextReader (TextReader input, XmlNameTable nt)
92                         : this(String.Empty, input, nt)
93                 {
94                 }
95
96                 public XmlTextReader (Stream xmlFragment, XmlNodeType fragType, XmlParserContext context)
97                         : this (String.Empty, new StreamReader (xmlFragment), fragType, context)
98                 {
99                 }
100
101                 public XmlTextReader (string url, Stream input, XmlNameTable nt)
102                         : this (url, new StreamReader (input), nt)
103                 {
104                 }
105
106                 public XmlTextReader (string url, TextReader input, XmlNameTable nt)
107                         : this (url, input, XmlNodeType.Document, new XmlParserContext (nt, new XmlNamespaceManager (nt), null, XmlSpace.None))
108                 {
109                 }
110
111                 [MonoTODO("TODO as same as private XmlTextReader(TextReader, XmlNodeType, XmlParserContext)")]
112                 public XmlTextReader (string xmlFragment, XmlNodeType fragType, XmlParserContext context)
113                         : this (String.Empty, new StringReader (xmlFragment), fragType, context)
114                 {
115                 }
116
117                 // TODO still remains as described at head of this file,
118                 // but it might not be TODO of the constructors...
119                 XmlTextReader (string url, TextReader fragment, XmlNodeType fragType, XmlParserContext context)
120                 {
121                         this.SetReaderContext(url, context);
122                         this.SetReaderFragment(fragment, fragType);
123                 }
124
125                 #endregion
126
127                 #region Properties
128
129                 public override int AttributeCount
130                 {
131                         get { return attributes.Count; }
132                 }
133
134                 public override string BaseURI
135                 {
136                         get { return parserContext.BaseURI; }
137                 }
138
139                 public override int Depth
140                 {
141                         get {
142                                 return elementDepth;
143                         }
144                 }
145
146                 public Encoding Encoding
147                 {
148                         get { return parserContext.Encoding; }
149                 }
150
151                 public override bool EOF
152                 {
153                         get
154                         {
155                                 return
156                                         readState == ReadState.EndOfFile ||
157                                         readState == ReadState.Closed;
158                         }
159                 }
160
161                 public override bool HasValue
162                 {
163                         get { return value != String.Empty;     }
164                 }
165
166                 public override bool IsDefault
167                 {
168                         get
169                         {
170                                 // XmlTextReader does not expand default attributes.
171                                 return false;
172                         }
173                 }
174
175                 public override bool IsEmptyElement
176                 {
177                         get { return isEmptyElement; }
178                 }
179
180                 public override string this [int i]
181                 {
182                         get { return GetAttribute (i); }
183                 }
184
185                 public override string this [string name]
186                 {
187                         get { return GetAttribute (name); }
188                 }
189
190                 public override string this [string localName, string namespaceName]
191                 {
192                         get { return GetAttribute (localName, namespaceName); }
193                 }
194
195                 public int LineNumber
196                 {
197                         get { return line; }
198                 }
199
200                 public int LinePosition
201                 {
202                         get { return column; }
203                 }
204
205                 public override string LocalName
206                 {
207                         get { return localName; }
208                 }
209
210                 public override string Name
211                 {
212                         get { return name; }
213                 }
214
215                 [MonoTODO]
216                 public bool Namespaces
217                 {
218                         get { throw new NotImplementedException (); }
219                         set { throw new NotImplementedException (); }
220                 }
221
222                 public override string NamespaceURI
223                 {
224                         get { return namespaceURI; }
225                 }
226
227                 public override XmlNameTable NameTable
228                 {
229                         get { return parserContext.NameTable; }
230                 }
231
232                 public override XmlNodeType NodeType
233                 {
234                         get { return nodeType; }
235                 }
236
237                 [MonoTODO]
238                 public bool Normalization
239                 {
240                         get { throw new NotImplementedException (); }
241                         set { throw new NotImplementedException (); }
242                 }
243
244                 public override string Prefix
245                 {
246                         get { return prefix; }
247                 }
248
249                 public override char QuoteChar
250                 {
251                         get {
252                                 // value string holds attribute quotation char.
253                                 if (NodeType == XmlNodeType.Attribute)
254                                         return value [0];
255                                 else
256                                         return '"';
257                         }
258                 }
259
260                 public override ReadState ReadState
261                 {
262                         get { return readState; }
263                 }
264
265                 public override string Value
266                 {
267                         get {
268                                 if(NodeType == XmlNodeType.Attribute)
269                                         return UnescapeAttributeValue(value);
270                                 else
271                                         return value;
272                         }
273                 }
274
275                 public WhitespaceHandling WhitespaceHandling
276                 {
277                         get { return whitespaceHandling; }
278                         set { whitespaceHandling = value; }
279                 }
280
281                 [MonoTODO]
282                 public override string XmlLang
283                 {
284                         get { throw new NotImplementedException (); }
285                 }
286
287                 [MonoTODO]
288                 public XmlResolver XmlResolver
289                 {
290                         set { throw new NotImplementedException (); }
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                                 innerXmlBuilder.Length = 0;
595                                 do {
596                                         ReadContent ();
597                                         if (NodeType != XmlNodeType.EndElement || depth + 1 > startDepth)
598                                                 innerXmlBuilder.Append (currentTag);
599                                 } while (depth >= startDepth);
600
601                                 string xml = innerXmlBuilder.ToString ();
602                                 innerXmlBuilder.Length = 0;
603                                 return xml;
604                         case XmlNodeType.None:
605                                 // MS document is incorrect. Seems not to progress.
606                                 return String.Empty;
607                         default:
608                                 Read ();
609                                 return String.Empty;
610                         }
611                 }
612
613                 public override string ReadOuterXml ()
614                 {
615                         if (readState != ReadState.Interactive)
616                                 return String.Empty;
617
618                         switch (NodeType) {
619                         case XmlNodeType.Attribute:
620                                 // strictly incompatible with MS... (it holds spaces attribute between name, value and "=" char (very trivial).
621                                 return String.Format ("{0}={1}{2}{1}", Name, QuoteChar, ReadInnerXml ());
622                         case XmlNodeType.Element:
623                                 bool isEmpty = IsEmptyElement;
624                                 string startTag = currentTag.ToString ();
625                                 string name = Name;
626
627                                 if (NodeType == XmlNodeType.Element && !isEmpty)
628                                         return String.Format ("{0}{1}</{2}>", startTag, ReadInnerXml (), name);
629                                 else
630                                         return currentTag.ToString ();
631                         case XmlNodeType.None:
632                                 // MS document is incorrect. Seems not to progress.
633                                 return String.Empty;
634                         default:
635                                 Read ();
636                                 return String.Empty;
637                         }
638                 }
639
640                 [MonoTODO]
641                 public override string ReadString ()
642                 {
643                         throw new NotImplementedException ();
644                 }
645
646                 [MonoTODO]
647                 public void ResetState ()
648                 {
649                         throw new NotImplementedException ();
650                 }
651
652                 public override void ResolveEntity ()
653                 {
654                         // XmlTextReaders don't resolve entities.
655                         throw new InvalidOperationException ("XmlTextReader cannot resolve external entities.");
656                 }
657
658                 #endregion
659
660                 #region Internals
661                 internal string publicId;
662                 internal string systemId;
663
664                 internal void SetReaderContext (string url, XmlParserContext context)
665                 {
666                         parserContext = context;
667                         parserContext.BaseURI = url;
668                         Init ();
669                 }
670
671                 internal void SetReaderFragment(TextReader fragment, XmlNodeType fragType)
672                 {
673                         this.reader = fragment;
674                         StreamReader sr = fragment as StreamReader;
675                         if (sr != null)
676                                 can_seek = sr.BaseStream.CanSeek;
677                         else
678                                 can_seek = fragment != null && fragment.Peek () != -1;
679
680                         if (fragType == XmlNodeType.Attribute)
681                                 value = "''";
682                         else if (fragType == XmlNodeType.DocumentFragment)
683                                 allowMultipleRoot = true;
684 /*      for future use
685                         switch(fragType)
686                         {
687                         case XmlNodeType.Attribute:     // attribute content
688                                 parserContext.InputState = XmlParserInputState.AttributeValue;
689                                 break;
690                         case XmlNodeType.DocumentFragment:      // element content
691                                 parserContext.InputState = XmlParserInputState.Content;
692                                 break;
693                         case XmlNodeType.Element:       // one element
694                                 parserContext.InputState = XmlParserInputState.StartTag;
695                                 break;
696                         case XmlNodeType.Document:      // document content
697                                 parserContext.InputState = XmlParserInputState.Start;
698                                 break;
699                         default:
700                                 throw new InvalidOperationException("setting this xml node type not allowed.");
701                         }
702 */
703                 }
704                 #endregion
705
706                 #region Privates
707
708                 private XmlParserContext parserContext;
709
710                 private TextReader reader;
711                 private ReadState readState;
712
713                 private int depth;
714                 private int elementDepth;
715                 private bool depthDown;
716
717                 private bool popScope;
718                 private Stack elementStack;
719                 private bool haveEnteredDocument;
720                 private bool allowMultipleRoot = false;
721
722                 private XmlNodeType nodeType;
723                 private string name;
724                 private string prefix;
725                 private string localName;
726                 private string namespaceURI;
727                 private bool isEmptyElement;
728                 private string value;
729
730                 private XmlNodeType saveNodeType;
731                 private string saveName;
732                 private string savePrefix;
733                 private string saveLocalName;
734                 private string saveNamespaceURI;
735                 private bool saveIsEmptyElement;
736
737                 private Hashtable attributes;
738                 private ArrayList orderedAttributes;
739                 private IEnumerator orderedAttributesEnumerator;
740
741                 private bool returnEntityReference;
742                 private string entityReferenceName;
743
744                 private char [] nameBuffer;
745                 private int nameLength;
746                 private int nameCapacity;
747                 private const int initialNameCapacity = 256;
748
749                 private char [] valueBuffer;
750                 private int valueLength;
751                 private int valueCapacity;
752                 private const int initialValueCapacity = 8192;
753
754                 private StringBuilder currentTag; // A buffer for ReadContent for ReadOuterXml
755                 private int line = 1;
756                 private int column = 1;
757                 private bool has_peek;
758                 private bool can_seek;
759                 private int peek_char;
760
761                 private string attributeString = String.Empty;
762                 private int attributeValuePos;
763                 // This should be only referenced(used) by ReadInnerXml(). Kind of flyweight pattern.
764                 private StringBuilder innerXmlBuilder;
765
766                 private XmlException ReaderError(string message)
767                 {
768                         return new XmlException(message, LineNumber, LinePosition);
769                 }
770                 private void Init ()
771                 {
772                         readState = ReadState.Initial;
773
774                         depth = 0;
775                         depthDown = false;
776
777                         popScope = false;
778                         elementStack = new Stack();
779                         haveEnteredDocument = false;
780
781                         nodeType = XmlNodeType.None;
782                         name = String.Empty;
783                         prefix = String.Empty;
784                         localName = string.Empty;
785                         isEmptyElement = false;
786                         value = String.Empty;
787
788                         attributes = new Hashtable ();
789                         orderedAttributes = new ArrayList ();
790                         orderedAttributesEnumerator = null;
791
792                         returnEntityReference = false;
793                         entityReferenceName = String.Empty;
794
795                         nameBuffer = new char [initialNameCapacity];
796                         nameLength = 0;
797                         nameCapacity = initialNameCapacity;
798
799                         valueBuffer = new char [initialValueCapacity];
800                         valueLength = 0;
801                         valueCapacity = initialValueCapacity;
802
803                         currentTag = new StringBuilder ();
804                         innerXmlBuilder = new StringBuilder ();
805                 }
806
807                 // Use this method rather than setting the properties
808                 // directly so that all the necessary properties can
809                 // be changed in harmony with each other. Maybe the
810                 // fields should be in a seperate class to help enforce
811                 // this.
812                 private void SetProperties (
813                         XmlNodeType nodeType,
814                         string name,
815                         bool isEmptyElement,
816                         string value,
817                         bool clearAttributes)
818                 {
819                         this.nodeType = nodeType;
820                         this.name = name;
821                         this.isEmptyElement = isEmptyElement;
822                         this.value = value;
823                         this.elementDepth = depth;
824
825                         if (clearAttributes)
826                                 ClearAttributes ();
827
828                         int indexOfColon = name.IndexOf (':');
829
830                         if (indexOfColon == -1) {
831                                 prefix = String.Empty;
832                                 localName = name;
833                         } else {
834                                 prefix = name.Substring (0, indexOfColon);
835                                 localName = name.Substring (indexOfColon + 1);
836                         }
837
838                         namespaceURI = LookupNamespace (prefix);
839                 }
840
841                 private void SaveProperties ()
842                 {
843                         saveNodeType = nodeType;
844                         saveName = name;
845                         savePrefix = prefix;
846                         saveLocalName = localName;
847                         saveNamespaceURI = namespaceURI;
848                         saveIsEmptyElement = isEmptyElement;
849                         // An element's value is always String.Empty.
850                 }
851
852                 private void RestoreProperties ()
853                 {
854                         nodeType = saveNodeType;
855                         name = saveName;
856                         prefix = savePrefix;
857                         localName = saveLocalName;
858                         namespaceURI = saveNamespaceURI;
859                         isEmptyElement = saveIsEmptyElement;
860                         value = String.Empty;
861                 }
862
863                 private void AddAttribute (string name, string value)
864                 {
865                         attributes.Add (name, value);
866                         orderedAttributes.Add (name);
867                 }
868
869                 private void ClearAttributes ()
870                 {
871                         if (attributes.Count > 0) {
872                                 attributes.Clear ();
873                                 orderedAttributes.Clear ();
874                         }
875
876                         orderedAttributesEnumerator = null;
877                 }
878
879                 private int PeekChar ()
880                 {
881                         if (can_seek)
882                                 return reader.Peek ();
883
884                         if (has_peek)
885                                 return peek_char;
886
887                         peek_char = reader.Read ();
888                         has_peek = true;
889                         return peek_char;
890                 }
891
892                 private int ReadChar ()
893                 {
894                         int ch;
895                         if (has_peek) {
896                                 ch = peek_char;
897                                 has_peek = false;
898                         } else {
899                                 ch = reader.Read ();
900                         }
901
902                         if (ch == '\n') {
903                                 line++;
904                                 column = 1;
905                         } else {
906                                 column++;
907                         }
908                         currentTag.Append ((char) ch);
909                         return ch;
910                 }
911
912                 // This should really keep track of some state so
913                 // that it's not possible to have more than one document
914                 // element or text outside of the document element.
915                 private bool ReadContent ()
916                 {
917                         currentTag.Length = 0;
918                         if (popScope) {
919                                 parserContext.NamespaceManager.PopScope ();
920                                 popScope = false;
921                         }
922
923                         if (returnEntityReference) {
924                                 SetEntityReferenceProperties ();
925                         } else {
926                         switch (PeekChar ())
927                                 {
928                                 case '<':
929                                         ReadChar ();
930                                         ReadTag ();
931                                         break;
932                                 case '\r':
933                                         if (whitespaceHandling == WhitespaceHandling.All ||
934                                             whitespaceHandling == WhitespaceHandling.Significant)
935                                                 return ReadWhitespace ();
936
937                                         ReadChar ();
938                                         return ReadContent ();
939                                 case '\n':
940                                         if (whitespaceHandling == WhitespaceHandling.All ||
941                                             whitespaceHandling == WhitespaceHandling.Significant)
942                                                 return ReadWhitespace ();
943
944                                         ReadChar ();
945                                         return ReadContent ();
946                                 case ' ':
947                                         if (whitespaceHandling == WhitespaceHandling.All ||
948                                             whitespaceHandling == WhitespaceHandling.Significant)
949                                                 return ReadWhitespace ();
950
951                                         SkipWhitespace ();
952                                         return ReadContent ();
953                                 case -1:
954                                         readState = ReadState.EndOfFile;
955                                         SetProperties (
956                                                 XmlNodeType.None, // nodeType
957                                                 String.Empty, // name
958                                                 false, // isEmptyElement
959                                                 String.Empty, // value
960                                                 true // clearAttributes
961                                         );
962                                         break;
963                                 default:
964                                         ReadText (true);
965                                         break;
966                                 }
967                         }
968                         return this.ReadState != ReadState.EndOfFile;
969                 }
970
971                 private void SetEntityReferenceProperties ()
972                 {
973                         SetProperties (
974                                 XmlNodeType.EntityReference, // nodeType
975                                 entityReferenceName, // name
976                                 false, // isEmptyElement
977                                 String.Empty, // value
978                                 true // clearAttributes
979                         );
980
981                         returnEntityReference = false;
982                         entityReferenceName = String.Empty;
983                 }
984
985                 // The leading '<' has already been consumed.
986                 private void ReadTag ()
987                 {
988                         switch (PeekChar ())
989                         {
990                         case '/':
991                                 ReadChar ();
992                                 ReadEndTag ();
993                                 break;
994                         case '?':
995                                 ReadChar ();
996                                 ReadProcessingInstruction ();
997                                 break;
998                         case '!':
999                                 ReadChar ();
1000                                 ReadDeclaration ();
1001                                 break;
1002                         default:
1003                                 ReadStartTag ();
1004                                 break;
1005                         }
1006                 }
1007
1008                 // The leading '<' has already been consumed.
1009                 private void ReadStartTag ()
1010                 {
1011                         parserContext.NamespaceManager.PushScope ();
1012
1013                         string name = ReadName ();
1014                         if (haveEnteredDocument && elementStack.Count == 0 && !allowMultipleRoot)
1015                                 throw ReaderError("document has terminated, cannot open new element");
1016
1017                         haveEnteredDocument = true;
1018                         SkipWhitespace ();
1019
1020                         bool isEmptyElement = false;
1021
1022                         ClearAttributes ();
1023
1024                         if (XmlConstructs.IsNameStart (PeekChar ()))
1025                                 ReadAttributes ();
1026
1027                         if (PeekChar () == '/') {
1028                                 ReadChar ();
1029                                 isEmptyElement = true;
1030                                 depthDown = true;
1031                                 popScope = true;
1032                         }
1033                         else
1034                                 elementStack.Push(name);
1035
1036                         Expect ('>');
1037
1038                         SetProperties (
1039                                 XmlNodeType.Element, // nodeType
1040                                 name, // name
1041                                 isEmptyElement, // isEmptyElement
1042                                 String.Empty, // value
1043                                 false // clearAttributes
1044                         );
1045
1046                         if (!depthDown)
1047                                 ++depth;
1048                         else
1049                                 depthDown = false;
1050
1051                 }
1052
1053                 // The reader is positioned on the first character
1054                 // of the element's name.
1055                 private void ReadEndTag ()
1056                 {
1057                         string name = ReadName ();
1058                         if (elementStack.Count == 0)
1059                                 throw ReaderError("closing element without matching opening element");
1060                         if ((string)elementStack.Pop() != name)
1061                                 throw ReaderError("unmatched closing element");
1062
1063                         SkipWhitespace ();
1064                         Expect ('>');
1065
1066                         --depth;
1067
1068                         SetProperties (
1069                                 XmlNodeType.EndElement, // nodeType
1070                                 name, // name
1071                                 false, // isEmptyElement
1072                                 String.Empty, // value
1073                                 true // clearAttributes
1074                         );
1075
1076                         popScope = true;
1077                 }
1078
1079                 private void AppendNameChar (int ch)
1080                 {
1081                         CheckNameCapacity ();
1082                         nameBuffer [nameLength++] = (char)ch;
1083                 }
1084
1085                 private void CheckNameCapacity ()
1086                 {
1087                         if (nameLength == nameCapacity) {
1088                                 nameCapacity = nameCapacity * 2;
1089                                 char [] oldNameBuffer = nameBuffer;
1090                                 nameBuffer = new char [nameCapacity];
1091                                 Array.Copy (oldNameBuffer, nameBuffer, nameLength);
1092                         }
1093                 }
1094
1095                 private string CreateNameString ()
1096                 {
1097                         return new String (nameBuffer, 0, nameLength);
1098                 }
1099
1100                 private void AppendValueChar (int ch)
1101                 {
1102                         CheckValueCapacity ();
1103                         valueBuffer [valueLength++] = (char)ch;
1104                 }
1105
1106                 private void CheckValueCapacity ()
1107                 {
1108                         if (valueLength == valueCapacity) {
1109                                 valueCapacity = valueCapacity * 2;
1110                                 char [] oldValueBuffer = valueBuffer;
1111                                 valueBuffer = new char [valueCapacity];
1112                                 Array.Copy (oldValueBuffer, valueBuffer, valueLength);
1113                         }
1114                 }
1115
1116                 private string CreateValueString ()
1117                 {
1118                         return new String (valueBuffer, 0, valueLength);
1119                 }
1120
1121                 // The reader is positioned on the first character
1122                 // of the text.
1123                 private void ReadText (bool cleanValue)
1124                 {
1125                         if (cleanValue)
1126                                 valueLength = 0;
1127
1128                         int ch = PeekChar ();
1129
1130                         while (ch != '<' && ch != -1) {
1131                                 if (ch == '&') {
1132                                         ReadChar ();
1133                                         if (ReadReference (false))
1134                                                 break;
1135                                 } else
1136                                         AppendValueChar (ReadChar ());
1137
1138                                 ch = PeekChar ();
1139                         }
1140
1141                         if (returnEntityReference && valueLength == 0) {
1142                                 SetEntityReferenceProperties ();
1143                         } else {
1144                                 SetProperties (
1145                                         XmlNodeType.Text, // nodeType
1146                                         String.Empty, // name
1147                                         false, // isEmptyElement
1148                                         CreateValueString (), // value
1149                                         true // clearAttributes
1150                                 );
1151                         }
1152                 }
1153
1154                 // The leading '&' has already been consumed.
1155                 // Returns true if the entity reference isn't a simple
1156                 // character reference or one of the predefined entities.
1157                 // This allows the ReadText method to break so that the
1158                 // next call to Read will return the EntityReference node.
1159                 private bool ReadReference (bool ignoreEntityReferences)
1160                 {
1161                         if (PeekChar () == '#') {
1162                                 ReadChar ();
1163                                 ReadCharacterReference ();
1164                         } else
1165                                 ReadEntityReference (ignoreEntityReferences);
1166
1167                         return returnEntityReference;
1168                 }
1169
1170                 private void ReadCharacterReference ()
1171                 {
1172                         int value = 0;
1173
1174                         if (PeekChar () == 'x') {
1175                                 ReadChar ();
1176
1177                                 while (PeekChar () != ';' && PeekChar () != -1) {
1178                                         int ch = ReadChar ();
1179
1180                                         if (ch >= '0' && ch <= '9')
1181                                                 value = (value << 4) + ch - '0';
1182                                         else if (ch >= 'A' && ch <= 'F')
1183                                                 value = (value << 4) + ch - 'A' + 10;
1184                                         else if (ch >= 'a' && ch <= 'f')
1185                                                 value = (value << 4) + ch - 'a' + 10;
1186                                         else
1187                                                 throw ReaderError (
1188                                                         String.Format (
1189                                                                 "invalid hexadecimal digit: {0} (#x{1:X})",
1190                                                                 (char)ch,
1191                                                                 ch));
1192                                 }
1193                         } else {
1194                                 while (PeekChar () != ';' && PeekChar () != -1) {
1195                                         int ch = ReadChar ();
1196
1197                                         if (ch >= '0' && ch <= '9')
1198                                                 value = value * 10 + ch - '0';
1199                                         else
1200                                                 throw ReaderError (
1201                                                         String.Format (
1202                                                                 "invalid decimal digit: {0} (#x{1:X})",
1203                                                                 (char)ch,
1204                                                                 ch));
1205                                 }
1206                         }
1207
1208                         ReadChar (); // ';'
1209
1210                         AppendValueChar (value);
1211                 }
1212
1213                 private void ReadEntityReference (bool ignoreEntityReferences)
1214                 {
1215                         nameLength = 0;
1216
1217                         int ch = PeekChar ();
1218
1219                         while (ch != ';' && ch != -1) {
1220                                 AppendNameChar (ReadChar ());
1221                                 ch = PeekChar ();
1222                         }
1223
1224                         Expect (';');
1225
1226                         string name = CreateNameString ();
1227
1228                         switch (name)
1229                         {
1230                                 case "lt":
1231                                         AppendValueChar ('<');
1232                                         break;
1233                                 case "gt":
1234                                         AppendValueChar ('>');
1235                                         break;
1236                                 case "amp":
1237                                         AppendValueChar ('&');
1238                                         break;
1239                                 case "apos":
1240                                         AppendValueChar ('\'');
1241                                         break;
1242                                 case "quot":
1243                                         AppendValueChar ('"');
1244                                         break;
1245                                 default:
1246                                         if (ignoreEntityReferences) {
1247                                                 AppendValueChar ('&');
1248
1249                                                 foreach (char ch2 in name) {
1250                                                         AppendValueChar (ch2);
1251                                                 }
1252
1253                                                 AppendValueChar (';');
1254                                         } else {
1255                                                 returnEntityReference = true;
1256                                                 entityReferenceName = name;
1257                                         }
1258                                         break;
1259                         }
1260                 }
1261
1262                 // The reader is positioned on the first character of
1263                 // the attribute name.
1264                 private void ReadAttributes ()
1265                 {
1266                         do {
1267                                 string name = ReadName ();
1268                                 SkipWhitespace ();
1269                                 Expect ('=');
1270                                 SkipWhitespace ();
1271                                 string value = ReadAttribute ();
1272                                 SkipWhitespace ();
1273
1274                                 if (name == "xmlns")
1275                                         parserContext.NamespaceManager.AddNamespace (String.Empty, UnescapeAttributeValue (value));
1276                                 else if (name.StartsWith ("xmlns:"))
1277                                         parserContext.NamespaceManager.AddNamespace (name.Substring (6), UnescapeAttributeValue (value));
1278
1279                                 AddAttribute (name, value);
1280                         } while (PeekChar () != '/' && PeekChar () != '>' && PeekChar () != -1);
1281                 }
1282
1283                 // The reader is positioned on the quote character.
1284                 // *Keeps quote char* to value to get_QuoteChar() correctly.
1285                 private string ReadAttribute ()
1286                 {
1287                         valueLength = 0;
1288
1289                         int quoteChar = ReadChar ();
1290
1291                         if (quoteChar != '\'' && quoteChar != '\"')
1292                                 throw ReaderError ("an attribute value was not quoted");
1293
1294                         AppendValueChar (quoteChar);
1295
1296                         while (PeekChar () != quoteChar) {
1297                                 int ch = ReadChar ();
1298
1299                                 switch (ch)
1300                                 {
1301                                 case '<':
1302                                         throw ReaderError ("attribute values cannot contain '<'");
1303                                 case -1:
1304                                         throw ReaderError ("unexpected end of file in an attribute value");
1305                                 default:
1306                                         AppendValueChar (ch);
1307                                         break;
1308                                 }
1309                         }
1310
1311                         ReadChar (); // quoteChar
1312                         AppendValueChar (quoteChar);
1313
1314                         return CreateValueString ();
1315                 }
1316
1317                 // The reader is positioned on the first character
1318                 // of the target.
1319                 //
1320                 // Now it also reads XmlDeclaration, this method name became improper...
1321                 private void ReadProcessingInstruction ()
1322                 {
1323                         string target = ReadName ();
1324                         SkipWhitespace ();
1325
1326                         valueLength = 0;
1327
1328                         while (PeekChar () != -1) {
1329                                 int ch = ReadChar ();
1330
1331                                 if (ch == '?' && PeekChar () == '>') {
1332                                         ReadChar ();
1333                                         break;
1334                                 }
1335
1336                                 AppendValueChar ((char)ch);
1337                         }
1338
1339 /* for future use
1340                         if(target == "xml") && parserContext.InputState != XmlParserInputState.Start)
1341                                 throw new XmlException("Xml declaration is not allowed here.");
1342                         else {
1343                                 parserContext.InputState = XmlParserInputState.DTD; //for future use
1344                         }
1345 */
1346                         SetProperties (
1347                                 target == "xml" ?
1348                                 XmlNodeType.XmlDeclaration :
1349                                 XmlNodeType.ProcessingInstruction, // nodeType
1350                                 target, // name
1351                                 false, // isEmptyElement
1352                                 CreateValueString (), // value
1353                                 true // clearAttributes
1354                         );
1355                 }
1356
1357                 // The reader is positioned on the first character after
1358                 // the leading '<!'.
1359                 private void ReadDeclaration ()
1360                 {
1361                         int ch = PeekChar ();
1362
1363                         switch (ch)
1364                         {
1365                         case '-':
1366                                 Expect ("--");
1367                                 ReadComment ();
1368                                 break;
1369                         case '[':
1370                                 ReadChar ();
1371                                 Expect ("CDATA[");
1372                                 ReadCDATA ();
1373                                 break;
1374                         case 'D':
1375                                 Expect ("DOCTYPE");
1376                                 ReadDoctypeDecl ();
1377                                 break;
1378                         }
1379                 }
1380
1381                 // The reader is positioned on the first character after
1382                 // the leading '<!--'.
1383                 private void ReadComment ()
1384                 {
1385                         valueLength = 0;
1386
1387                         while (PeekChar () != -1) {
1388                                 int ch = ReadChar ();
1389
1390                                 if (ch == '-' && PeekChar () == '-') {
1391                                         ReadChar ();
1392
1393                                         if (PeekChar () != '>')
1394                                                 throw ReaderError ("comments cannot contain '--'");
1395
1396                                         ReadChar ();
1397                                         break;
1398                                 }
1399
1400                                 AppendValueChar ((char)ch);
1401                         }
1402
1403                         SetProperties (
1404                                 XmlNodeType.Comment, // nodeType
1405                                 String.Empty, // name
1406                                 false, // isEmptyElement
1407                                 CreateValueString (), // value
1408                                 true // clearAttributes
1409                         );
1410                 }
1411
1412                 // The reader is positioned on the first character after
1413                 // the leading '<![CDATA['.
1414                 private void ReadCDATA ()
1415                 {
1416                         valueLength = 0;
1417
1418                         while (PeekChar () != -1) {
1419                                 int ch = ReadChar ();
1420
1421                                 if (ch == ']' && PeekChar () == ']') {
1422                                         ch = ReadChar (); // ']'
1423
1424                                         if (PeekChar () == '>') {
1425                                                 ReadChar (); // '>'
1426                                                 break;
1427                                         } else {
1428                                                 AppendValueChar (']');
1429                                                 AppendValueChar (']');
1430                                                 ch = ReadChar ();
1431                                         }
1432                                 }
1433
1434                                 AppendValueChar ((char)ch);
1435                         }
1436
1437                         SetProperties (
1438                                 XmlNodeType.CDATA, // nodeType
1439                                 String.Empty, // name
1440                                 false, // isEmptyElement
1441                                 CreateValueString (), // value
1442                                 true // clearAttributes
1443                         );
1444                 }
1445
1446                 // The reader is positioned on the first character after
1447                 // the leading '<!DOCTYPE'.
1448                 private void ReadDoctypeDecl ()
1449                 {
1450                         string doctypeName = null;
1451                         string publicId = String.Empty;
1452                         string systemId = String.Empty;
1453
1454                         SkipWhitespace ();
1455                         doctypeName = ReadName ();
1456                         SkipWhitespace ();
1457                         switch(PeekChar ())
1458                         {
1459                         case 'S':
1460                                 systemId = ReadSystemLiteral (true);
1461                                 break;
1462                         case 'P':
1463                                 publicId = ReadPubidLiteral ();
1464                                 SkipWhitespace ();
1465                                 systemId = ReadSystemLiteral (false);
1466                                 break;
1467                         }
1468                         SkipWhitespace ();
1469
1470
1471                         if(PeekChar () == '[')
1472                         {
1473                                 // read markupdecl etc. or end of decl
1474                                 ReadChar ();
1475                                 int startPos = currentTag.Length;
1476                                 do {
1477                                         ReadDTDInternalSubset ();
1478                                 } while (nodeType != XmlNodeType.None);
1479                                 int endPos = currentTag.Length - 1;
1480                                 parserContext.InternalSubset = currentTag.ToString (startPos, endPos - startPos);
1481                         }
1482                         // end of DOCTYPE decl.
1483                         SkipWhitespace ();
1484                         Expect ('>');
1485
1486                         // set properties for <!DOCTYPE> node
1487                         SetProperties (
1488                                 XmlNodeType.DocumentType, // nodeType
1489                                 doctypeName, // name
1490                                 false, // isEmptyElement
1491                                 parserContext.InternalSubset, // value
1492                                 true // clearAttributes
1493                                 );
1494                 }
1495
1496                 // Read any one of following:
1497                 //   elementdecl, AttlistDecl, EntityDecl, NotationDecl,
1498                 //   PI, Comment, Parameter Entity, or doctype termination char(']')
1499                 //
1500                 // returns a node of some nodeType or null, setting nodeType.
1501                 //       (if None then ']' was found.)
1502                 private void ReadDTDInternalSubset()
1503                 {
1504                         SkipWhitespace ();
1505                         switch(ReadChar ())
1506                         {
1507                         case ']':
1508                                 nodeType = XmlNodeType.None;
1509                                 break;
1510                         case '%':
1511                                 string peName = ReadName ();
1512                                 Expect (';');
1513                                 nodeType = XmlNodeType.EntityReference; // It's chating a bit;-)
1514                                 break;
1515                         case '<':
1516                                 switch(ReadChar ())
1517                                 {
1518                                 case '?':
1519                                         ReadProcessingInstruction ();
1520                                         break;
1521                                 case '!':
1522                                         switch(ReadChar ())
1523                                         {
1524                                         case '-':
1525                                                 Expect ('-');
1526                                                 ReadComment ();
1527                                                 break;
1528                                         case 'E':
1529                                                 switch(ReadChar ())
1530                                                 {
1531                                                 case 'N':
1532                                                         Expect ("TITY");
1533                                                         ReadEntityDecl ();
1534                                                         break;
1535                                                 case 'L':
1536                                                         Expect ("EMENT");
1537                                                         ReadElementDecl ();
1538                                                         break;
1539                                                 default:
1540                                                         throw ReaderError ("Syntax Error after '<!E' (ELEMENT or ENTITY must be found)");
1541                                                 }
1542                                                 break;
1543                                         case 'A':
1544                                                 Expect ("TTLIST");
1545                                                 ReadAttListDecl ();
1546                                                 break;
1547                                         case 'N':
1548                                                 Expect ("OTATION");
1549                                                 ReadNotationDecl ();
1550                                                 break;
1551                                         default:
1552                                                 throw ReaderError ("Syntax Error after '<!' characters.");
1553                                         }
1554                                         break;
1555                                 default:
1556                                         throw ReaderError ("Syntax Error after '<' character.");
1557                                 }
1558                                 break;
1559                         default:
1560                                 throw ReaderError ("Syntax Error inside doctypedecl markup.");
1561                         }
1562                 }
1563
1564                 // The reader is positioned on the head of the name.
1565                 private void ReadElementDecl()
1566                 {
1567                         while(ReadChar () != '>');
1568                 }
1569
1570                 private void ReadEntityDecl()
1571                 {
1572                         while(ReadChar () != '>');
1573                 }
1574
1575                 private void ReadAttListDecl()
1576                 {
1577                         while(ReadChar () != '>');
1578                 }
1579
1580                 private void ReadNotationDecl()
1581                 {
1582                         while(ReadChar () != '>');
1583                 }
1584                 
1585                 // The reader is positioned on the first 'S' of "SYSTEM".
1586                 private string ReadSystemLiteral (bool expectSYSTEM)
1587                 {
1588                         if(expectSYSTEM)
1589                                 Expect ("SYSTEM");
1590                         SkipWhitespace ();
1591                         int quoteChar = ReadChar ();    // apos or quot
1592                         int startPos = currentTag.Length;
1593                         int c = 0;
1594                         while(c != quoteChar) {
1595                                 c = ReadChar ();
1596                                 if(c < 0) throw ReaderError ("Unexpected end of stream in ExternalID.");
1597                         }
1598                         return currentTag.ToString (startPos, currentTag.Length - 1 - startPos);
1599                 }
1600
1601                 private string ReadPubidLiteral()
1602                 {
1603                         Expect ("PUBLIC");
1604                         SkipWhitespace ();
1605                         int quoteChar = ReadChar ();
1606                         int startPos = currentTag.Length;
1607                         int c = 0;
1608                         while(c != quoteChar)
1609                         {
1610                                 c = ReadChar ();
1611                                 if(c < 0) throw ReaderError ("Unexpected end of stream in ExternalID.");
1612                                 if(c != quoteChar && !XmlConstructs.IsPubid (c))
1613                                         throw ReaderError("character '" + (char)c + "' not allowed for PUBLIC ID");
1614                         }
1615                         ReadChar();     // skips quoteChar
1616                         return currentTag.ToString (startPos, currentTag.Length - 1 - startPos);
1617                 }
1618
1619                 // The reader is positioned on the first character
1620                 // of the name.
1621                 private string ReadName ()
1622                 {
1623                         if (!XmlConstructs.IsNameStart (PeekChar ()))
1624                                 throw ReaderError ("a name did not start with a legal character" + PeekChar ());
1625
1626                         nameLength = 0;
1627
1628                         AppendNameChar (ReadChar ());
1629
1630                         while (XmlConstructs.IsName (PeekChar ())) {
1631                                 AppendNameChar (ReadChar ());
1632                         }
1633
1634                         return CreateNameString ();
1635                 }
1636
1637                 // Read the next character and compare it against the
1638                 // specified character.
1639                 private void Expect (int expected)
1640                 {
1641                         int ch = ReadChar ();
1642
1643                         if (ch != expected) {
1644                                 throw ReaderError (
1645                                         String.Format (
1646                                                 "expected '{0}' ({1:X}) but found '{2}' ({3:X})",
1647                                                 (char)expected,
1648                                                 expected,
1649                                                 (char)ch,
1650                                                 ch));
1651                         }
1652                 }
1653
1654                 private void Expect (string expected)
1655                 {
1656                         int len = expected.Length;
1657                         for(int i=0; i< len; i++)
1658                                 Expect (expected[i]);
1659                 }
1660
1661                 // Does not consume the first non-whitespace character.
1662                 private void SkipWhitespace ()
1663                 {
1664                         //FIXME: Should not skip if whitespaceHandling == WhiteSpaceHandling.None
1665                         while (XmlConstructs.IsSpace (PeekChar ()))
1666                                 ReadChar ();
1667                 }
1668
1669                 private bool ReadWhitespace ()
1670                 {
1671                         valueLength = 0;
1672                         int ch = PeekChar ();
1673                         do {
1674                                 AppendValueChar (ReadChar ());
1675                         } while ((ch = PeekChar ()) != -1 && XmlConstructs.IsSpace (ch));
1676
1677                         if (ch != -1 && ch != '<')
1678                                 ReadText (false);
1679                         else
1680                                 SetProperties (XmlNodeType.Whitespace,
1681                                                String.Empty,
1682                                                false,
1683                                                CreateValueString (),
1684                                                true);
1685
1686                         return (PeekChar () != -1);
1687                 }
1688
1689                 // read entity reference from attribute string and if parsable then return the value.
1690                 private string ReadAttributeValueReference ()
1691                 {
1692                         int endEntityPosition = attributeString.IndexOf(';',
1693                                 attributeValuePos);
1694                         string entityName = attributeString.Substring (attributeValuePos + 1,
1695                                 endEntityPosition - attributeValuePos - 1);
1696
1697                         attributeValuePos = endEntityPosition + 1;
1698
1699                         if(entityName [0] == '#') {
1700                                 char c;
1701                                 // character entity
1702                                 if(entityName [1] == 'x') {
1703                                         // hexadecimal
1704                                         c = (char) int.Parse ("0" + entityName.Substring (2),
1705                                                 System.Globalization.NumberStyles.HexNumber);
1706                                 } else {
1707                                         // decimal
1708                                         c = (char) int.Parse (entityName.Substring (1));
1709                                 }
1710                                 return c.ToString();
1711                         }
1712                         else {
1713                                 switch(entityName)
1714                                 {
1715                                 case "lt": return "<";
1716                                 case "gt": return ">";
1717                                 case "amp": return "&";
1718                                 case "quot": return "\"";
1719                                 case "apos": return "'";
1720                                 default: return null;
1721                                 }
1722                         }
1723                 }
1724
1725                 private string UnescapeAttributeValue (string unresolved)
1726                 {
1727                         if(unresolved == null) return null;
1728                         StringBuilder resolved = new StringBuilder();
1729                         int pos = 0;
1730
1731                         // trim start/end edge of quotation character.
1732                         unresolved = unresolved.Substring (1, unresolved.Length - 2);
1733
1734                         int next = unresolved.IndexOf ('&');
1735                         if(next < 0)
1736                                 return unresolved;
1737
1738                         while(next >= 0) {
1739                                 if(pos < next)
1740                                         resolved.Append (unresolved.Substring (pos, next - pos));// - 1);
1741                                 int endPos = unresolved.IndexOf (';', next+1);
1742                                 string entityName =
1743                                         unresolved.Substring (next + 1, endPos - next - 1);
1744                                 if(entityName [0] == '#') {
1745                                         char c;
1746                                         // character entity
1747                                         if(entityName [1] == 'x') {
1748                                                 // hexadecimal
1749                                                 c = (char) int.Parse ("0" + entityName.Substring (2),
1750                                                         System.Globalization.NumberStyles.HexNumber);
1751                                         } else {
1752                                                 // decimal
1753                                                 c = (char) int.Parse (entityName.Substring (1));
1754                                         }
1755                                         resolved.Append (c);
1756                                 } else {
1757                                         switch(entityName) {
1758                                         case "lt": resolved.Append ("<"); break;
1759                                         case "gt": resolved.Append (">"); break;
1760                                         case "amp": resolved.Append ("&"); break;
1761                                         case "quot": resolved.Append ("\""); break;
1762                                         case "apos": resolved.Append ("'"); break;
1763                                         // With respect to "Value", MS document is helpless
1764                                         // and the implemention returns inconsistent value
1765                                         // (e.g. XML: "&ent; &amp;ent;" ---> Value: "&ent; &ent;".)
1766                                         default: resolved.Append ("&" + entityName + ";"); break;
1767                                         }
1768                                 }
1769                                 pos = endPos + 1;
1770                                 if(pos > unresolved.Length)
1771                                         break;
1772                                 next = unresolved.IndexOf('&', pos);
1773                         }
1774                         resolved.Append (unresolved.Substring(pos));
1775
1776                         return resolved.ToString();
1777                 }
1778
1779                 #endregion
1780         }
1781 }