2003-02-03 Gonzalo Paniagua Javier <gonzalo@ximian.com>
[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 either well-formedness
20 //   or validity.
21 //
22 //   NameTables aren't being used everywhere yet.
23 //
24 //   Some thought needs to be given to performance. There's too many
25 //   strings being allocated.
26 //
27 //   Some of the MoveTo methods haven't been implemented yet.
28 //
29 //   LineNumber and LinePosition aren't being tracked.
30 //
31 //   xml:space, xml:lang, and xml:base aren't being tracked.
32 //
33
34 using System;
35 using System.Collections;
36 using System.IO;
37 using System.Text;
38
39 namespace System.Xml
40 {
41         public class XmlTextReader : XmlReader, IXmlLineInfo
42         {
43                 WhitespaceHandling whitespaceHandling = WhitespaceHandling.All;
44                 #region Constructors
45
46                 protected XmlTextReader ()
47                 {
48                 }
49
50                 public XmlTextReader (Stream input)
51                         : this (new StreamReader (input))
52                 {
53                 }
54
55                 public XmlTextReader (string url)
56                         : this(url, new NameTable ())
57                 {
58                 }
59
60                 public XmlTextReader (TextReader input)
61                         : this (input, new NameTable ())
62                 {
63                 }
64
65                 protected XmlTextReader (XmlNameTable nt)
66                         : this (String.Empty, null, XmlNodeType.None, null)
67                 {
68                 }
69
70                 public XmlTextReader (Stream input, XmlNameTable nt)
71                         : this(new StreamReader (input), nt)
72                 {
73                 }
74
75                 public XmlTextReader (string url, Stream input)
76                         : this (url, new StreamReader (input))
77                 {
78                 }
79
80                 public XmlTextReader (string url, TextReader input)
81                         : this (url, input, new NameTable ())
82                 {
83                 }
84
85                 [MonoTODO("Non-filename-url must be supported. Waiting for WebClient")]
86                 public XmlTextReader (string url, XmlNameTable nt)
87                         // : this(url, new StreamReader ((Stream)new XmlUrlResolver ().GetEntity (new Uri (url), null, typeof(Stream))), nt)
88                         : this (url, new StreamReader (url), nt)
89                 {
90                 }
91
92                 public XmlTextReader (TextReader input, XmlNameTable nt)
93                         : this(String.Empty, input, nt)
94                 {
95                 }
96
97                 public XmlTextReader (Stream xmlFragment, XmlNodeType fragType, XmlParserContext context)
98                         : this (String.Empty, new StreamReader (xmlFragment), fragType, context)
99                 {
100                 }
101
102                 public XmlTextReader (string url, Stream input, XmlNameTable nt)
103                         : this (url, new StreamReader (input), nt)
104                 {
105                 }
106
107                 public XmlTextReader (string url, TextReader input, XmlNameTable nt)
108                         : this (url, input, XmlNodeType.Document, new XmlParserContext (nt, new XmlNamespaceManager (nt), null, XmlSpace.None))
109                 {
110                 }
111
112                 [MonoTODO("TODO as same as private XmlTextReader(TextReader, XmlNodeType, XmlParserContext)")]
113                 public XmlTextReader (string xmlFragment, XmlNodeType fragType, XmlParserContext context)
114                         : this (String.Empty, new StringReader (xmlFragment), fragType, context)
115                 {
116                 }
117
118                 // TODO still remains as described at head of this file,
119                 // but it might not be TODO of the constructors...
120                 XmlTextReader (string url, TextReader fragment, XmlNodeType fragType, XmlParserContext context)
121                 {
122                         this.SetReaderContext(url, context);
123                         this.SetReaderFragment(fragment, fragType);
124                 }
125
126                 #endregion
127
128                 #region Properties
129
130                 public override int AttributeCount
131                 {
132                         get { return attributes.Count; }
133                 }
134
135                 public override string BaseURI
136                 {
137                         get { return parserContext.BaseURI; }
138                 }
139
140                 public override int Depth
141                 {
142                         get {
143                                 return elementDepth;
144                         }
145                 }
146
147                 public Encoding Encoding
148                 {
149                         get { return parserContext.Encoding; }
150                 }
151
152                 public override bool EOF
153                 {
154                         get
155                         {
156                                 return
157                                         readState == ReadState.EndOfFile ||
158                                         readState == ReadState.Closed;
159                         }
160                 }
161
162                 public override bool HasValue
163                 {
164                         get { return value != String.Empty;     }
165                 }
166
167                 public override bool IsDefault
168                 {
169                         get
170                         {
171                                 // XmlTextReader does not expand default attributes.
172                                 return false;
173                         }
174                 }
175
176                 public override bool IsEmptyElement
177                 {
178                         get { return isEmptyElement; }
179                 }
180
181                 public override string this [int i]
182                 {
183                         get { return GetAttribute (i); }
184                 }
185
186                 public override string this [string name]
187                 {
188                         get { return GetAttribute (name); }
189                 }
190
191                 public override string this [string localName, string namespaceName]
192                 {
193                         get { return GetAttribute (localName, namespaceName); }
194                 }
195
196                 public int LineNumber
197                 {
198                         get { return line; }
199                 }
200
201                 public int LinePosition
202                 {
203                         get { return column; }
204                 }
205
206                 public override string LocalName
207                 {
208                         get { return localName; }
209                 }
210
211                 public override string Name
212                 {
213                         get { return name; }
214                 }
215
216                 [MonoTODO]
217                 public bool Namespaces
218                 {
219                         get { throw new NotImplementedException (); }
220                         set { throw new NotImplementedException (); }
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 ResolveAttributeValue(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                 [MonoTODO]
289                 public XmlResolver XmlResolver
290                 {
291                         set { throw new NotImplementedException (); }
292                 }
293
294                 [MonoTODO]
295                 public override XmlSpace XmlSpace
296                 {
297                         get { throw new NotImplementedException (); }
298                 }
299
300                 #endregion
301
302                 #region Methods
303
304                 [MonoTODO]
305                 public override void Close ()
306                 {
307                         readState = ReadState.Closed;
308                 }
309
310                 [MonoTODO]
311                 public override string GetAttribute (int i)
312                 {
313                         if (i > attributes.Count)
314                                 throw new ArgumentOutOfRangeException ("i is smaller than AttributeCount");
315                         else
316                                 return ResolveAttributeValue (attributes [orderedAttributes [i]] as string);
317                 }
318
319                 public override string GetAttribute (string name)
320                 {
321                         return attributes.ContainsKey (name) ?
322                                 ResolveAttributeValue (attributes [name] as string) : String.Empty;
323                 }
324
325                 public override string GetAttribute (string localName, string namespaceURI)
326                 {
327                         foreach (DictionaryEntry entry in attributes)
328                         {
329                                 string thisName = entry.Key as string;
330
331                                 int indexOfColon = thisName.IndexOf (':');
332
333                                 if (indexOfColon != -1) {
334                                         string thisLocalName = thisName.Substring (indexOfColon + 1);
335
336                                         if (localName == thisLocalName) {
337                                                 string thisPrefix = thisName.Substring (0, indexOfColon);
338                                                 string thisNamespaceURI = LookupNamespace (thisPrefix);
339
340                                                 if (namespaceURI == thisNamespaceURI)
341                                                         return attributes.ContainsKey (thisName) ?
342                                                                 ResolveAttributeValue (attributes [thisName] as string) : String.Empty;
343                                         }
344                                 } else if (localName == "xmlns" && namespaceURI == "http://www.w3.org/2000/xmlns/" && thisName == "xmlns")
345                                         return attributes.ContainsKey (thisName) ? 
346                                                 ResolveAttributeValue (attributes [thisName] as string) : String.Empty;
347                         }
348
349                         return String.Empty;
350                 }
351
352                 [MonoTODO]
353                 public TextReader GetRemainder ()
354                 {
355                         throw new NotImplementedException ();
356                 }
357
358                 [MonoTODO]
359                 bool IXmlLineInfo.HasLineInfo ()
360                 {
361                         return false;
362                 }
363
364                 public override string LookupNamespace (string prefix)
365                 {
366                         return parserContext.NamespaceManager.LookupNamespace (prefix);
367                 }
368
369                 public override void MoveToAttribute (int i)
370                 {
371                         MoveToElement ();
372
373                         if (attributes == null || orderedAttributes.Count < i || i < 0)
374                                 throw new ArgumentOutOfRangeException ("attribute index out of range.");
375
376                         string name = orderedAttributes [i] as string;
377                         string value = attributes [name] as string;
378                         SetProperties (
379                                 XmlNodeType.Attribute, // nodeType
380                                 name, // name
381                                 false, // isEmptyElement
382                                 value, // value
383                                 false // clearAttributes
384                                 );
385                 }
386
387                 public override bool MoveToAttribute (string name)
388                 {
389                         MoveToElement ();
390                         bool match = false;
391
392                         if (attributes == null)
393                                 return false;
394
395                         if (orderedAttributesEnumerator == null) {
396                                 SaveProperties ();
397                                 orderedAttributesEnumerator = orderedAttributes.GetEnumerator ();
398                         }
399
400                         while (orderedAttributesEnumerator.MoveNext ()) {
401                                 if(name == orderedAttributesEnumerator.Current as string) {
402                                         match = true;
403                                         break;
404                                 }
405                         }
406
407                         if (match) {
408                                 string value = attributes [name] as string;
409                                 SetProperties (
410                                         XmlNodeType.Attribute, // nodeType
411                                         name, // name
412                                         false, // isEmptyElement
413                                         value, // value
414                                         false // clearAttributes
415                                 );
416                         }
417
418                         return match;
419                 }
420
421                 [MonoTODO]
422                 public override bool MoveToAttribute (string localName, string namespaceName)
423                 {
424                         throw new NotImplementedException ();
425                 }
426
427                 public override bool MoveToElement ()
428                 {
429                         if (orderedAttributesEnumerator != null) {
430                                 orderedAttributesEnumerator = null;
431                                 RestoreProperties ();
432                                 return true;
433                         }
434
435                         return false;
436                 }
437
438                 public override bool MoveToFirstAttribute ()
439                 {
440                         MoveToElement ();
441                         return MoveToNextAttribute ();
442                 }
443
444                 public override bool MoveToNextAttribute ()
445                 {
446                         if (attributes == null)
447                                 return false;
448
449                         if (orderedAttributesEnumerator == null) {
450                                 SaveProperties ();
451                                 orderedAttributesEnumerator = orderedAttributes.GetEnumerator ();
452                         }
453
454                         if (orderedAttributesEnumerator.MoveNext ()) {
455                                 string name = orderedAttributesEnumerator.Current as string;
456                                 string value = attributes [name] as string;
457                                 SetProperties (
458                                         XmlNodeType.Attribute, // nodeType
459                                         name, // name
460                                         false, // isEmptyElement
461                                         value, // value
462                                         false // clearAttributes
463                                 );
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                 [MonoTODO("This method should consider entity references")]
482                 public override bool ReadAttributeValue ()
483                 {
484                         // reading attribute value phase now stopped
485                         if(attributeStringCurrentPosition < 0 ||
486                                         attributeString.Length < attributeStringCurrentPosition) {
487                                 attributeStringCurrentPosition = 0;
488                                 attributeString = String.Empty;
489                                 return false;
490                         }
491
492                         // If not started, then initialize attributeString when parsing is at start.
493                         if(attributeStringCurrentPosition == 0)
494                                 attributeString =
495                                         value.Substring (1, value.Length - 2);
496
497                         bool returnEntity = false;
498                         value = String.Empty;
499                         int nextPosition = attributeString.IndexOf ('&',
500                                 attributeStringCurrentPosition);
501
502                         // if attribute string starts from '&' then it may be (unparsable) entity reference.
503                         if(nextPosition == 0) {
504                                 string parsed = ReadAttributeValueEntityReference ();
505                                 if(parsed == null) {
506                                         // return entity (It is only this case to return entity reference.)
507                                         int endEntityPosition = attributeString.IndexOf (';',
508                                                 attributeStringCurrentPosition);
509                                         SetProperties (XmlNodeType.EntityReference,
510                                                 attributeString.Substring (attributeStringCurrentPosition + 1,
511                                                 endEntityPosition - attributeStringCurrentPosition - 1),
512                                                 false,
513                                                 String.Empty,
514                                                 false);
515                                         attributeStringCurrentPosition = endEntityPosition + 1;
516
517                                         return true;
518                                 }
519                                 else
520                                         value += parsed;
521                         }
522
523                         // Other case always set text node.
524                         while(!returnEntity) {
525                                 nextPosition = attributeString.IndexOf ('&', attributeStringCurrentPosition);
526                                 if(nextPosition < 0) {
527                                         // Reached to the end of value string.
528                                         value += attributeString.Substring (attributeStringCurrentPosition);
529                                         attributeStringCurrentPosition = -1;
530                                         break;
531                                 } else if(nextPosition == attributeStringCurrentPosition) {
532                                         string parsed = ReadAttributeValueEntityReference ();
533                                         if(parsed != null)
534                                                 value += parsed;
535                                         else {
536                                                 // Found that an entity reference starts from this point.
537                                                 // Then once stop to parse attribute value and then return text.
538                                                 value += attributeString.Substring (attributeStringCurrentPosition,
539                                                         nextPosition - attributeStringCurrentPosition);
540                                                 break;
541                                         }
542                                 } else {
543                                         value += attributeString.Substring (attributeStringCurrentPosition,
544                                                 nextPosition - attributeStringCurrentPosition);
545                                         attributeStringCurrentPosition = nextPosition;
546                                         continue;
547                                 }
548                         }
549
550                         SetProperties(XmlNodeType.Text,
551                                 "#text",
552                                 false,
553                                 value,
554                                 false);
555
556                         return true;
557                 }
558
559                 [MonoTODO]
560                 public int ReadBase64 (byte [] buffer, int offset, int length)
561                 {
562                         throw new NotImplementedException ();
563                 }
564
565                 [MonoTODO]
566                 public int ReadBinHex (byte [] buffer, int offset, int length)
567                 {
568                         throw new NotImplementedException ();
569                 }
570
571                 [MonoTODO]
572                 public int ReadChars (char [] buffer, int offset, int length)
573                 {
574                         throw new NotImplementedException ();
575                 }
576
577                 [MonoTODO]
578                 public override string ReadInnerXml ()
579                 {
580                         // Still need a Well Formedness check.
581                         // Will wait for Validating reader ;-)
582                         if (NodeType == XmlNodeType.Attribute) {
583                                 return Value;
584                         } else {
585                                 saveToXmlBuffer = true;
586                                 string startname = this.Name;
587                                 string endname = string.Empty;
588                                 readState = ReadState.Interactive;
589
590                                 while (startname != endname) {
591                                         ReadContent ();
592                                         endname = this.Name;
593                                 }
594
595                                 xmlBuffer.Replace (currentTag.ToString (), "");
596                                 saveToXmlBuffer = false;
597                                 string InnerXml = xmlBuffer.ToString ();
598                                 xmlBuffer.Length = 0;
599                                 return InnerXml;
600                         }
601                 }
602
603                 [MonoTODO]
604                 public override string ReadOuterXml ()
605                 {
606                         if (NodeType == XmlNodeType.Attribute) {
607                                 return Name + "=\"" + Value.Replace ("\"", "&quot;") + "\"";
608                         } else {
609                                 saveToXmlBuffer = true;
610                                 xmlBuffer.Append (currentTag.ToString ());
611                                 int startDepth = Depth;
612                                 readState = ReadState.Interactive;
613
614                                 do {
615                                         ReadContent ();
616                                 } while (Depth > startDepth);
617
618                                 saveToXmlBuffer = false;
619                                 string OuterXml = xmlBuffer.ToString ();
620                                 xmlBuffer.Length = 0;
621                                 return OuterXml;
622                         }
623                 }
624
625                 [MonoTODO]
626                 public override string ReadString ()
627                 {
628                         throw new NotImplementedException ();
629                 }
630
631                 [MonoTODO]
632                 public void ResetState ()
633                 {
634                         throw new NotImplementedException ();
635                 }
636
637                 public override void ResolveEntity ()
638                 {
639                         // XmlTextReaders don't resolve entities.
640                         throw new InvalidOperationException ("XmlTextReader cannot resolve external entities.");
641                 }
642
643                 #endregion
644
645                 #region Internals
646                 internal string publicId;
647                 internal string systemId;
648
649                 internal void SetReaderContext (string url, XmlParserContext context)
650                 {
651                         parserContext = context;
652                         parserContext.BaseURI = url;
653                         Init ();
654                 }
655
656                 internal void SetReaderFragment(TextReader fragment, XmlNodeType fragType)
657                 {
658                         this.reader = fragment;
659                         can_seek = fragment != null && fragment.Peek () != -1;
660
661                         if (fragType == XmlNodeType.Attribute)
662                                 value = "''";
663 /*      for future use
664                         switch(fragType)
665                         {
666                         case XmlNodeType.Attribute:     // attribute content
667                                 parserContext.InputState = XmlParserInputState.AttributeValue;
668                                 break;
669                         case XmlNodeType.DocumentFragment:      // element content
670                                 parserContext.InputState = XmlParserInputState.Content;
671                                 break;
672                         case XmlNodeType.Element:       // one element
673                                 parserContext.InputState = XmlParserInputState.StartTag;
674                                 break;
675                         case XmlNodeType.Document:      // document content
676                                 parserContext.InputState = XmlParserInputState.Start;
677                                 break;
678                         default:
679                                 throw new InvalidOperationException("setting this xml node type not allowed.");
680                         }
681 */
682                 }
683                 #endregion
684
685                 #region Privates
686
687                 private XmlParserContext parserContext;
688
689                 private TextReader reader;
690                 private ReadState readState;
691
692                 private int depth;
693                 private int elementDepth;
694                 private bool depthDown;
695
696                 private bool popScope;
697
698                 private XmlNodeType nodeType;
699                 private string name;
700                 private string prefix;
701                 private string localName;
702                 private string namespaceURI;
703                 private bool isEmptyElement;
704                 private string value;
705
706                 private XmlNodeType saveNodeType;
707                 private string saveName;
708                 private string savePrefix;
709                 private string saveLocalName;
710                 private string saveNamespaceURI;
711                 private bool saveIsEmptyElement;
712
713                 private Hashtable attributes;
714                 private ArrayList orderedAttributes;
715                 private IEnumerator orderedAttributesEnumerator;
716
717                 private bool returnEntityReference;
718                 private string entityReferenceName;
719
720                 private char [] nameBuffer;
721                 private int nameLength;
722                 private int nameCapacity;
723                 private const int initialNameCapacity = 256;
724
725                 private char [] valueBuffer;
726                 private int valueLength;
727                 private int valueCapacity;
728                 private const int initialValueCapacity = 8192;
729
730                 private StringBuilder xmlBuffer; // This is for Read(Inner|Outer)Xml
731                 private StringBuilder currentTag; // A buffer for ReadContent for ReadOuterXml
732                 private bool saveToXmlBuffer;
733                 private int line = 1;
734                 private int column = 1;
735                 private bool has_peek;
736                 private bool can_seek;
737                 private int peek_char;
738
739                 private string attributeString = String.Empty;
740                 private int attributeStringCurrentPosition;
741
742                 private void Init ()
743                 {
744                         readState = ReadState.Initial;
745
746                         depth = 0;
747                         depthDown = false;
748
749                         popScope = false;
750
751                         nodeType = XmlNodeType.None;
752                         name = String.Empty;
753                         prefix = String.Empty;
754                         localName = string.Empty;
755                         isEmptyElement = false;
756                         value = String.Empty;
757
758                         attributes = new Hashtable ();
759                         orderedAttributes = new ArrayList ();
760                         orderedAttributesEnumerator = null;
761
762                         returnEntityReference = false;
763                         entityReferenceName = String.Empty;
764
765                         nameBuffer = new char [initialNameCapacity];
766                         nameLength = 0;
767                         nameCapacity = initialNameCapacity;
768
769                         valueBuffer = new char [initialValueCapacity];
770                         valueLength = 0;
771                         valueCapacity = initialValueCapacity;
772
773                         xmlBuffer = new StringBuilder ();
774                         currentTag = new StringBuilder ();
775                 }
776
777                 // Use this method rather than setting the properties
778                 // directly so that all the necessary properties can
779                 // be changed in harmony with each other. Maybe the
780                 // fields should be in a seperate class to help enforce
781                 // this.
782                 private void SetProperties (
783                         XmlNodeType nodeType,
784                         string name,
785                         bool isEmptyElement,
786                         string value,
787                         bool clearAttributes)
788                 {
789                         this.nodeType = nodeType;
790                         this.name = name;
791                         this.isEmptyElement = isEmptyElement;
792                         this.value = value;
793                         this.elementDepth = depth;
794
795                         if (clearAttributes)
796                                 ClearAttributes ();
797
798                         int indexOfColon = name.IndexOf (':');
799
800                         if (indexOfColon == -1) {
801                                 prefix = String.Empty;
802                                 localName = name;
803                         } else {
804                                 prefix = name.Substring (0, indexOfColon);
805                                 localName = name.Substring (indexOfColon + 1);
806                         }
807
808                         namespaceURI = LookupNamespace (prefix);
809                 }
810
811                 private void SaveProperties ()
812                 {
813                         saveNodeType = nodeType;
814                         saveName = name;
815                         savePrefix = prefix;
816                         saveLocalName = localName;
817                         saveNamespaceURI = namespaceURI;
818                         saveIsEmptyElement = isEmptyElement;
819                         // An element's value is always String.Empty.
820                 }
821
822                 private void RestoreProperties ()
823                 {
824                         nodeType = saveNodeType;
825                         name = saveName;
826                         prefix = savePrefix;
827                         localName = saveLocalName;
828                         namespaceURI = saveNamespaceURI;
829                         isEmptyElement = saveIsEmptyElement;
830                         value = String.Empty;
831                 }
832
833                 private void AddAttribute (string name, string value)
834                 {
835                         attributes.Add (name, value);
836                         orderedAttributes.Add (name);
837                 }
838
839                 private void ClearAttributes ()
840                 {
841                         if (attributes.Count > 0) {
842                                 attributes.Clear ();
843                                 orderedAttributes.Clear ();
844                         }
845
846                         orderedAttributesEnumerator = null;
847                 }
848
849                 private int PeekChar ()
850                 {
851                         if (can_seek)
852                                 return reader.Peek ();
853
854                         if (has_peek)
855                                 return peek_char;
856
857                         peek_char = reader.Read ();
858                         has_peek = true;
859                         return peek_char;
860                 }
861
862                 private int ReadChar ()
863                 {
864                         int ch;
865                         if (has_peek) {
866                                 ch = peek_char;
867                                 has_peek = false;
868                         } else {
869                                 ch = reader.Read ();
870                         }
871
872                         if (ch == '\n') {
873                                 line++;
874                                 column = 1;
875                         } else {
876                                 column++;
877                         }
878                         if (saveToXmlBuffer) {
879                                 xmlBuffer.Append ((char) ch);
880                         }
881                         currentTag.Append ((char) ch);
882                         return ch;
883                 }
884
885                 // This should really keep track of some state so
886                 // that it's not possible to have more than one document
887                 // element or text outside of the document element.
888                 private bool ReadContent ()
889                 {
890                         currentTag.Length = 0;
891                         if (popScope) {
892                                 parserContext.NamespaceManager.PopScope ();
893                                 popScope = false;
894                         }
895
896                         if (returnEntityReference) {
897                                 SetEntityReferenceProperties ();
898                         } else {
899                         switch (PeekChar ())
900                                 {
901                                 case '<':
902                                         ReadChar ();
903                                         ReadTag ();
904                                         break;
905                                 case '\r':
906                                         if (whitespaceHandling == WhitespaceHandling.All ||
907                                             whitespaceHandling == WhitespaceHandling.Significant)
908                                                 return ReadWhitespace ();
909
910                                         ReadChar ();
911                                         return ReadContent ();
912                                 case '\n':
913                                         if (whitespaceHandling == WhitespaceHandling.All ||
914                                             whitespaceHandling == WhitespaceHandling.Significant)
915                                                 return ReadWhitespace ();
916
917                                         ReadChar ();
918                                         return ReadContent ();
919                                 case ' ':
920                                         if (whitespaceHandling == WhitespaceHandling.All ||
921                                             whitespaceHandling == WhitespaceHandling.Significant)
922                                                 return ReadWhitespace ();
923
924                                         SkipWhitespace ();
925                                         return ReadContent ();
926                                 case -1:
927                                         readState = ReadState.EndOfFile;
928                                         SetProperties (
929                                                 XmlNodeType.None, // nodeType
930                                                 String.Empty, // name
931                                                 false, // isEmptyElement
932                                                 String.Empty, // value
933                                                 true // clearAttributes
934                                         );
935                                         break;
936                                 default:
937                                         ReadText (true);
938                                         break;
939                                 }
940                         }
941                         return this.ReadState != ReadState.EndOfFile;
942                 }
943
944                 private void SetEntityReferenceProperties ()
945                 {
946                         SetProperties (
947                                 XmlNodeType.EntityReference, // nodeType
948                                 entityReferenceName, // name
949                                 false, // isEmptyElement
950                                 String.Empty, // value
951                                 true // clearAttributes
952                         );
953
954                         returnEntityReference = false;
955                         entityReferenceName = String.Empty;
956                 }
957
958                 // The leading '<' has already been consumed.
959                 private void ReadTag ()
960                 {
961                         switch (PeekChar ())
962                         {
963                         case '/':
964                                 ReadChar ();
965                                 ReadEndTag ();
966                                 break;
967                         case '?':
968                                 ReadChar ();
969                                 ReadProcessingInstruction ();
970                                 break;
971                         case '!':
972                                 ReadChar ();
973                                 ReadDeclaration ();
974                                 break;
975                         default:
976                                 ReadStartTag ();
977                                 break;
978                         }
979                 }
980
981                 // The leading '<' has already been consumed.
982                 private void ReadStartTag ()
983                 {
984                         parserContext.NamespaceManager.PushScope ();
985
986                         string name = ReadName ();
987                         SkipWhitespace ();
988
989                         bool isEmptyElement = false;
990
991                         ClearAttributes ();
992
993                         if (XmlChar.IsFirstNameChar (PeekChar ()))
994                                 ReadAttributes ();
995
996                         if (PeekChar () == '/') {
997                                 ReadChar ();
998                                 isEmptyElement = true;
999                                 depthDown = true;
1000                                 popScope = true;
1001                         }
1002
1003                         Expect ('>');
1004
1005                         SetProperties (
1006                                 XmlNodeType.Element, // nodeType
1007                                 name, // name
1008                                 isEmptyElement, // isEmptyElement
1009                                 String.Empty, // value
1010                                 false // clearAttributes
1011                         );
1012
1013                         if (!depthDown)
1014                                 ++depth;
1015                         else
1016                                 depthDown = false;
1017
1018                 }
1019
1020                 // The reader is positioned on the first character
1021                 // of the element's name.
1022                 private void ReadEndTag ()
1023                 {
1024                         string name = ReadName ();
1025                         SkipWhitespace ();
1026                         Expect ('>');
1027
1028                         --depth;
1029
1030                         SetProperties (
1031                                 XmlNodeType.EndElement, // nodeType
1032                                 name, // name
1033                                 false, // isEmptyElement
1034                                 String.Empty, // value
1035                                 true // clearAttributes
1036                         );
1037
1038                         popScope = true;
1039                 }
1040
1041                 private void AppendNameChar (int ch)
1042                 {
1043                         CheckNameCapacity ();
1044                         nameBuffer [nameLength++] = (char)ch;
1045                 }
1046
1047                 private void CheckNameCapacity ()
1048                 {
1049                         if (nameLength == nameCapacity) {
1050                                 nameCapacity = nameCapacity * 2;
1051                                 char [] oldNameBuffer = nameBuffer;
1052                                 nameBuffer = new char [nameCapacity];
1053                                 Array.Copy (oldNameBuffer, nameBuffer, nameLength);
1054                         }
1055                 }
1056
1057                 private string CreateNameString ()
1058                 {
1059                         return new String (nameBuffer, 0, nameLength);
1060                 }
1061
1062                 private void AppendValueChar (int ch)
1063                 {
1064                         CheckValueCapacity ();
1065                         valueBuffer [valueLength++] = (char)ch;
1066                 }
1067
1068                 private void CheckValueCapacity ()
1069                 {
1070                         if (valueLength == valueCapacity) {
1071                                 valueCapacity = valueCapacity * 2;
1072                                 char [] oldValueBuffer = valueBuffer;
1073                                 valueBuffer = new char [valueCapacity];
1074                                 Array.Copy (oldValueBuffer, valueBuffer, valueLength);
1075                         }
1076                 }
1077
1078                 private string CreateValueString ()
1079                 {
1080                         return new String (valueBuffer, 0, valueLength);
1081                 }
1082
1083                 // The reader is positioned on the first character
1084                 // of the text.
1085                 private void ReadText (bool cleanValue)
1086                 {
1087                         if (cleanValue)
1088                                 valueLength = 0;
1089
1090                         int ch = PeekChar ();
1091
1092                         while (ch != '<' && ch != -1) {
1093                                 if (ch == '&') {
1094                                         ReadChar ();
1095                                         if (ReadReference (false))
1096                                                 break;
1097                                 } else
1098                                         AppendValueChar (ReadChar ());
1099
1100                                 ch = PeekChar ();
1101                         }
1102
1103                         if (returnEntityReference && valueLength == 0) {
1104                                 SetEntityReferenceProperties ();
1105                         } else {
1106                                 SetProperties (
1107                                         XmlNodeType.Text, // nodeType
1108                                         String.Empty, // name
1109                                         false, // isEmptyElement
1110                                         CreateValueString (), // value
1111                                         true // clearAttributes
1112                                 );
1113                         }
1114                 }
1115
1116                 // The leading '&' has already been consumed.
1117                 // Returns true if the entity reference isn't a simple
1118                 // character reference or one of the predefined entities.
1119                 // This allows the ReadText method to break so that the
1120                 // next call to Read will return the EntityReference node.
1121                 private bool ReadReference (bool ignoreEntityReferences)
1122                 {
1123                         if (PeekChar () == '#') {
1124                                 ReadChar ();
1125                                 ReadCharacterReference ();
1126                         } else
1127                                 ReadEntityReference (ignoreEntityReferences);
1128
1129                         return returnEntityReference;
1130                 }
1131
1132                 private void ReadCharacterReference ()
1133                 {
1134                         int value = 0;
1135
1136                         if (PeekChar () == 'x') {
1137                                 ReadChar ();
1138
1139                                 while (PeekChar () != ';' && PeekChar () != -1) {
1140                                         int ch = ReadChar ();
1141
1142                                         if (ch >= '0' && ch <= '9')
1143                                                 value = (value << 4) + ch - '0';
1144                                         else if (ch >= 'A' && ch <= 'F')
1145                                                 value = (value << 4) + ch - 'A' + 10;
1146                                         else if (ch >= 'a' && ch <= 'f')
1147                                                 value = (value << 4) + ch - 'a' + 10;
1148                                         else
1149                                                 throw new XmlException (
1150                                                         String.Format (
1151                                                                 "invalid hexadecimal digit: {0} (#x{1:X})",
1152                                                                 (char)ch,
1153                                                                 ch));
1154                                 }
1155                         } else {
1156                                 while (PeekChar () != ';' && PeekChar () != -1) {
1157                                         int ch = ReadChar ();
1158
1159                                         if (ch >= '0' && ch <= '9')
1160                                                 value = value * 10 + ch - '0';
1161                                         else
1162                                                 throw new XmlException (
1163                                                         String.Format (
1164                                                                 "invalid decimal digit: {0} (#x{1:X})",
1165                                                                 (char)ch,
1166                                                                 ch));
1167                                 }
1168                         }
1169
1170                         ReadChar (); // ';'
1171
1172                         AppendValueChar (value);
1173                 }
1174
1175                 private void ReadEntityReference (bool ignoreEntityReferences)
1176                 {
1177                         nameLength = 0;
1178
1179                         int ch = PeekChar ();
1180
1181                         while (ch != ';' && ch != -1) {
1182                                 AppendNameChar (ReadChar ());
1183                                 ch = PeekChar ();
1184                         }
1185
1186                         Expect (';');
1187
1188                         string name = CreateNameString ();
1189
1190                         switch (name)
1191                         {
1192                                 case "lt":
1193                                         AppendValueChar ('<');
1194                                         break;
1195                                 case "gt":
1196                                         AppendValueChar ('>');
1197                                         break;
1198                                 case "amp":
1199                                         AppendValueChar ('&');
1200                                         break;
1201                                 case "apos":
1202                                         AppendValueChar ('\'');
1203                                         break;
1204                                 case "quot":
1205                                         AppendValueChar ('"');
1206                                         break;
1207                                 default:
1208                                         if (ignoreEntityReferences) {
1209                                                 AppendValueChar ('&');
1210
1211                                                 foreach (char ch2 in name) {
1212                                                         AppendValueChar (ch2);
1213                                                 }
1214
1215                                                 AppendValueChar (';');
1216                                         } else {
1217                                                 returnEntityReference = true;
1218                                                 entityReferenceName = name;
1219                                         }
1220                                         break;
1221                         }
1222                 }
1223
1224                 // The reader is positioned on the first character of
1225                 // the attribute name.
1226                 private void ReadAttributes ()
1227                 {
1228                         do {
1229                                 string name = ReadName ();
1230                                 SkipWhitespace ();
1231                                 Expect ('=');
1232                                 SkipWhitespace ();
1233                                 string value = ReadAttribute ();
1234                                 SkipWhitespace ();
1235
1236                                 if (name == "xmlns")
1237                                         parserContext.NamespaceManager.AddNamespace (String.Empty, ResolveAttributeValue (value));
1238                                 else if (name.StartsWith ("xmlns:"))
1239                                         parserContext.NamespaceManager.AddNamespace (name.Substring (6), ResolveAttributeValue (value));
1240
1241                                 AddAttribute (name, value);
1242                         } while (PeekChar () != '/' && PeekChar () != '>' && PeekChar () != -1);
1243                 }
1244
1245                 // The reader is positioned on the quote character.
1246                 private string ReadAttribute ()
1247                 {
1248                         valueLength = 0;
1249
1250                         int quoteChar = ReadChar ();
1251
1252                         if (quoteChar != '\'' && quoteChar != '\"')
1253                                 throw new XmlException ("an attribute value was not quoted");
1254
1255                         // this keeps quote char to get QuoteChar property correctly.
1256                         AppendValueChar (quoteChar);
1257
1258                         while (PeekChar () != quoteChar) {
1259                                 int ch = ReadChar ();
1260
1261                                 switch (ch)
1262                                 {
1263                                 case '<':
1264                                         throw new XmlException ("attribute values cannot contain '<'");
1265 // expansion of entity now should be done at ResolveAttributeValue() method
1266 //                              case '&':
1267 //                                      ReadReference (true);
1268 //                                      break;
1269                                 case -1:
1270                                         throw new XmlException ("unexpected end of file in an attribute value");
1271                                 default:
1272                                         AppendValueChar (ch);
1273                                         break;
1274                                 }
1275                         }
1276
1277                         ReadChar (); // quoteChar
1278                         AppendValueChar (quoteChar);
1279
1280                         return CreateValueString ();
1281                 }
1282
1283                 // The reader is positioned on the first character
1284                 // of the target.
1285                 //
1286                 // Now it also reads XmlDeclaration, this method name became improper...
1287                 private void ReadProcessingInstruction ()
1288                 {
1289                         string target = ReadName ();
1290                         SkipWhitespace ();
1291
1292                         valueLength = 0;
1293
1294                         while (PeekChar () != -1) {
1295                                 int ch = ReadChar ();
1296
1297                                 if (ch == '?' && PeekChar () == '>') {
1298                                         ReadChar ();
1299                                         break;
1300                                 }
1301
1302                                 AppendValueChar ((char)ch);
1303                         }
1304
1305 /* for future use
1306                         if(target == "xml") && parserContext.InputState != XmlParserInputState.Start)
1307                                 throw new XmlException("Xml declaration is not allowed here.");
1308                         else {
1309                                 parserContext.InputState = XmlParserInputState.DTD; //for future use
1310                         }
1311 */
1312                         SetProperties (
1313                                 target == "xml" ?
1314                                 XmlNodeType.XmlDeclaration :
1315                                 XmlNodeType.ProcessingInstruction, // nodeType
1316                                 target, // name
1317                                 false, // isEmptyElement
1318                                 CreateValueString (), // value
1319                                 true // clearAttributes
1320                         );
1321                 }
1322
1323                 // The reader is positioned on the first character after
1324                 // the leading '<!'.
1325                 private void ReadDeclaration ()
1326                 {
1327                         int ch = PeekChar ();
1328
1329                         switch (ch)
1330                         {
1331                         case '-':
1332                                 Expect ("--");
1333                                 ReadComment ();
1334                                 break;
1335                         case '[':
1336                                 ReadChar ();
1337                                 Expect ("CDATA[");
1338                                 ReadCDATA ();
1339                                 break;
1340                         case 'D':
1341                                 Expect ("DOCTYPE");
1342                                 ReadDoctypeDecl ();
1343                                 break;
1344                         }
1345                 }
1346
1347                 // The reader is positioned on the first character after
1348                 // the leading '<!--'.
1349                 private void ReadComment ()
1350                 {
1351                         valueLength = 0;
1352
1353                         while (PeekChar () != -1) {
1354                                 int ch = ReadChar ();
1355
1356                                 if (ch == '-' && PeekChar () == '-') {
1357                                         ReadChar ();
1358
1359                                         if (PeekChar () != '>')
1360                                                 throw new XmlException ("comments cannot contain '--'");
1361
1362                                         ReadChar ();
1363                                         break;
1364                                 }
1365
1366                                 AppendValueChar ((char)ch);
1367                         }
1368
1369                         SetProperties (
1370                                 XmlNodeType.Comment, // nodeType
1371                                 String.Empty, // name
1372                                 false, // isEmptyElement
1373                                 CreateValueString (), // value
1374                                 true // clearAttributes
1375                         );
1376                 }
1377
1378                 // The reader is positioned on the first character after
1379                 // the leading '<![CDATA['.
1380                 private void ReadCDATA ()
1381                 {
1382                         valueLength = 0;
1383
1384                         while (PeekChar () != -1) {
1385                                 int ch = ReadChar ();
1386
1387                                 if (ch == ']' && PeekChar () == ']') {
1388                                         ch = ReadChar (); // ']'
1389
1390                                         if (PeekChar () == '>') {
1391                                                 ReadChar (); // '>'
1392                                                 break;
1393                                         } else {
1394                                                 AppendValueChar (']');
1395                                                 AppendValueChar (']');
1396                                                 ch = ReadChar ();
1397                                         }
1398                                 }
1399
1400                                 AppendValueChar ((char)ch);
1401                         }
1402
1403                         SetProperties (
1404                                 XmlNodeType.CDATA, // 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 '<!DOCTYPE'.
1414                 private void ReadDoctypeDecl ()
1415                 {
1416                         string doctypeName = null;
1417                         string publicId = String.Empty;
1418                         string systemId = String.Empty;
1419
1420                         SkipWhitespace ();
1421                         doctypeName = ReadName ();
1422                         SkipWhitespace ();
1423                         xmlBuffer.Length = 0;
1424                         switch(PeekChar ())
1425                         {
1426                         case 'S':
1427                                 systemId = ReadSystemLiteral (true);
1428                                 break;
1429                         case 'P':
1430                                 publicId = ReadPubidLiteral ();
1431                                 SkipWhitespace ();
1432                                 systemId = ReadSystemLiteral (false);
1433                                 break;
1434                         }
1435                         SkipWhitespace ();
1436
1437
1438                         if(PeekChar () == '[')
1439                         {
1440                                 // read markupdecl etc. or end of decl
1441                                 ReadChar ();
1442                                 xmlBuffer.Length = 0;
1443                                 saveToXmlBuffer = true;
1444                                 do {
1445                                         ReadDTDInternalSubset ();
1446                                 } while(nodeType != XmlNodeType.None);
1447                                 xmlBuffer.Remove (xmlBuffer.Length - 1, 1);     // cut off ']'
1448                                 saveToXmlBuffer = false;
1449                         }
1450                         // end of DOCTYPE decl.
1451                         SkipWhitespace ();
1452                         Expect ('>');
1453
1454                         parserContext.InternalSubset = xmlBuffer.ToString ();
1455
1456                         // set properties for <!DOCTYPE> node
1457                         SetProperties (
1458                                 XmlNodeType.DocumentType, // nodeType
1459                                 doctypeName, // name
1460                                 false, // isEmptyElement
1461                                 parserContext.InternalSubset, // value
1462                                 true // clearAttributes
1463                                 );
1464                 }
1465
1466                 // Read any one of following:
1467                 //   elementdecl, AttlistDecl, EntityDecl, NotationDecl,
1468                 //   PI, Comment, Parameter Entity, or doctype termination char(']')
1469                 //
1470                 // returns a node of some nodeType or null, setting nodeType.
1471                 //       (if None then ']' was found.)
1472                 private void ReadDTDInternalSubset()
1473                 {
1474                         SkipWhitespace ();
1475                         switch(ReadChar ())
1476                         {
1477                         case ']':
1478                                 nodeType = XmlNodeType.None;
1479                                 break;
1480                         case '%':
1481                                 string peName = ReadName ();
1482                                 Expect (';');
1483                                 nodeType = XmlNodeType.EntityReference; // It's chating a bit;-)
1484                                 break;
1485                         case '<':
1486                                 switch(ReadChar ())
1487                                 {
1488                                 case '?':
1489                                         ReadProcessingInstruction ();
1490                                         break;
1491                                 case '!':
1492                                         switch(ReadChar ())
1493                                         {
1494                                         case '-':
1495                                                 Expect ('-');
1496                                                 ReadComment ();
1497                                                 break;
1498                                         case 'E':
1499                                                 switch(ReadChar ())
1500                                                 {
1501                                                 case 'N':
1502                                                         Expect ("TITY");
1503                                                         ReadEntityDecl ();
1504                                                         break;
1505                                                 case 'L':
1506                                                         Expect ("EMENT");
1507                                                         ReadElementDecl ();
1508                                                         break;
1509                                                 default:
1510                                                         throw new XmlException ("Syntax Error after '<!E' (ELEMENT or ENTITY must be found)");
1511                                                 }
1512                                                 break;
1513                                         case 'A':
1514                                                 Expect ("TTLIST");
1515                                                 ReadAttListDecl ();
1516                                                 break;
1517                                         case 'N':
1518                                                 Expect ("OTATION");
1519                                                 ReadNotationDecl ();
1520                                                 break;
1521                                         default:
1522                                                 throw new XmlException ("Syntax Error after '<!' characters.");
1523                                         }
1524                                         break;
1525                                 default:
1526                                         throw new XmlException ("Syntax Error after '<' character.");
1527                                 }
1528                                 break;
1529                         default:
1530                                 throw new XmlException ("Syntax Error inside doctypedecl markup.");
1531                         }
1532                 }
1533
1534                 // The reader is positioned on the head of the name.
1535                 private void ReadElementDecl()
1536                 {
1537                         while(ReadChar () != '>');
1538                 }
1539
1540                 private void ReadEntityDecl()
1541                 {
1542                         while(ReadChar () != '>');
1543                 }
1544
1545                 private void ReadAttListDecl()
1546                 {
1547                         while(ReadChar () != '>');
1548                 }
1549
1550                 private void ReadNotationDecl()
1551                 {
1552                         while(ReadChar () != '>');
1553                 }
1554                 
1555                 // The reader is positioned on the first 'S' of "SYSTEM".
1556                 private string ReadSystemLiteral (bool expectSYSTEM)
1557                 {
1558                         if(expectSYSTEM)
1559                                 Expect ("SYSTEM");
1560                         SkipWhitespace ();
1561                         int quoteChar = ReadChar ();    // apos or quot
1562                         xmlBuffer.Length = 0;
1563                         saveToXmlBuffer = true;
1564                         int c = 0;
1565                         while(c != quoteChar) {
1566                                 c = ReadChar ();
1567                                 if(c < 0) throw new XmlException ("Unexpected end of stream in ExternalID.");
1568                         }
1569                         saveToXmlBuffer = false;
1570                         xmlBuffer.Remove (xmlBuffer.Length-1, 1);       // cut quoteChar
1571                         return xmlBuffer.ToString ();
1572                 }
1573
1574                 private string ReadPubidLiteral()
1575                 {
1576                         Expect ("PUBLIC");
1577                         SkipWhitespace ();
1578                         int quoteChar = ReadChar ();
1579                         xmlBuffer.Length = 0;
1580                         saveToXmlBuffer = true;
1581                         int c = 0;
1582                         while(c != quoteChar)
1583                         {
1584                                 c = ReadChar ();
1585                                 if(c < 0) throw new XmlException ("Unexpected end of stream in ExternalID.");
1586                                 if(c != quoteChar && !XmlChar.IsPubidChar (c))
1587                                         throw new XmlException("character '" + (char)c + "' not allowed for PUBLIC ID");
1588                         }
1589                         ReadChar();     // skips quoteChar
1590                         xmlBuffer.Remove (xmlBuffer.Length-1, 1);       // cut quoteChar
1591                         saveToXmlBuffer = false;
1592                         return xmlBuffer.ToString ();
1593                 }
1594
1595                 // The reader is positioned on the first character
1596                 // of the name.
1597                 private string ReadName ()
1598                 {
1599                         if (!XmlChar.IsFirstNameChar (PeekChar ()))
1600                                 throw new XmlException ("a name did not start with a legal character");
1601
1602                         nameLength = 0;
1603
1604                         AppendNameChar (ReadChar ());
1605
1606                         while (XmlChar.IsNameChar (PeekChar ())) {
1607                                 AppendNameChar (ReadChar ());
1608                         }
1609
1610                         return CreateNameString ();
1611                 }
1612
1613                 // Read the next character and compare it against the
1614                 // specified character.
1615                 private void Expect (int expected)
1616                 {
1617                         int ch = ReadChar ();
1618
1619                         if (ch != expected) {
1620                                 throw new XmlException (
1621                                         String.Format (
1622                                                 "expected '{0}' ({1:X}) but found '{2}' ({3:X})",
1623                                                 (char)expected,
1624                                                 expected,
1625                                                 (char)ch,
1626                                                 ch));
1627                         }
1628                 }
1629
1630                 private void Expect (string expected)
1631                 {
1632                         int len = expected.Length;
1633                         for(int i=0; i< len; i++)
1634                                 Expect (expected[i]);
1635                 }
1636
1637                 // Does not consume the first non-whitespace character.
1638                 private void SkipWhitespace ()
1639                 {
1640                         //FIXME: Should not skip if whitespaceHandling == WhiteSpaceHandling.None
1641                         while (XmlChar.IsWhitespace (PeekChar ()))
1642                                 ReadChar ();
1643                 }
1644
1645                 private bool ReadWhitespace ()
1646                 {
1647                         valueLength = 0;
1648                         int ch = PeekChar ();
1649                         do {
1650                                 AppendValueChar (ReadChar ());
1651                         } while ((ch = PeekChar ()) != -1 && XmlChar.IsWhitespace (ch));
1652
1653                         if (ch != -1 && ch != '<')
1654                                 ReadText (false);
1655                         else
1656                                 SetProperties (XmlNodeType.Whitespace,
1657                                                String.Empty,
1658                                                false,
1659                                                CreateValueString (),
1660                                                true);
1661
1662                         return (PeekChar () != -1);
1663                 }
1664
1665                 // read entity reference from attribute string and if parsable then return the value.
1666                 private string ReadAttributeValueEntityReference ()
1667                 {
1668                         int endEntityPosition = attributeString.IndexOf(';',
1669                                 attributeStringCurrentPosition);
1670                         string entityName = attributeString.Substring (attributeStringCurrentPosition + 1,
1671                                 endEntityPosition - attributeStringCurrentPosition - 1);
1672
1673                         attributeStringCurrentPosition = endEntityPosition + 1;
1674
1675                         if(entityName [0] == '#') {
1676                                 char c;
1677                                 // character entity
1678                                 if(entityName [1] == 'x') {
1679                                         // hexadecimal
1680                                         c = (char) int.Parse ("0" + entityName.Substring (2),
1681                                                 System.Globalization.NumberStyles.HexNumber);
1682                                 } else {
1683                                         // decimal
1684                                         c = (char) int.Parse (entityName.Substring (1));
1685                                 }
1686                                 return c.ToString();
1687                         }
1688                         else {
1689                                 switch(entityName)
1690                                 {
1691                                 case "lt": return "<";
1692                                 case "gt": return ">";
1693                                 case "amp": return "&";
1694                                 case "quot": return "\"";
1695                                 case "apos": return "'";
1696                                 default: return null;
1697                                 }
1698                         }
1699                 }
1700
1701                 private string ResolveAttributeValue (string unresolved)
1702                 {
1703                         if(unresolved == null) return null;
1704                         StringBuilder resolved = new StringBuilder();
1705                         int pos = 0;
1706
1707                         // trim start/end edge of quotation character.
1708                         unresolved = unresolved.Substring (1, unresolved.Length - 2);
1709
1710                         int next = unresolved.IndexOf ('&');
1711                         if(next < 0)
1712                                 return unresolved;
1713
1714                         while(next >= 0) {
1715                                 if(pos < next)
1716                                         resolved.Append (unresolved.Substring (pos, next - pos));// - 1);
1717                                 int endPos = unresolved.IndexOf (';', next+1);
1718                                 string entityName =
1719                                         unresolved.Substring (next + 1, endPos - next - 1);
1720                                 if(entityName [0] == '#') {
1721                                         char c;
1722                                         // character entity
1723                                         if(entityName [1] == 'x') {
1724                                                 // hexadecimal
1725                                                 c = (char) int.Parse ("0" + entityName.Substring (2),
1726                                                         System.Globalization.NumberStyles.HexNumber);
1727                                         } else {
1728                                                 // decimal
1729                                                 c = (char) int.Parse (entityName.Substring (1));
1730                                         }
1731                                         resolved.Append (c);
1732                                 } else {
1733                                         switch(entityName) {
1734                                         case "lt": resolved.Append ("<"); break;
1735                                         case "gt": resolved.Append (">"); break;
1736                                         case "amp": resolved.Append ("&"); break;
1737                                         case "quot": resolved.Append ("\""); break;
1738                                         case "apos": resolved.Append ("'"); break;
1739                                         // With respect to "Value", MS document is helpless
1740                                         // and the implemention returns inconsistent value
1741                                         // (e.g. XML: "&ent; &amp;ent;" ---> Value: "&ent; &ent;".)
1742                                         default: resolved.Append ("&" + entityName + ";"); break;
1743                                         }
1744                                 }
1745                                 pos = endPos + 1;
1746                                 if(pos > unresolved.Length)
1747                                         break;
1748                                 next = unresolved.IndexOf('&', pos);
1749                         }
1750                         resolved.Append (unresolved.Substring(pos));
1751
1752                         return resolved.ToString();
1753                 }
1754
1755                 #endregion
1756         }
1757 }