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