2002-08-25 Tim Coleman <tim@timcoleman.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 //
17 //   There's also no checking being done for either well-formedness
18 //   or validity.
19 //
20 //   NameTables aren't being used everywhere yet.
21 //
22 //   Some thought needs to be given to performance. There's too many
23 //   strings being allocated.
24 //
25 //   Some of the MoveTo methods haven't been implemented yet.
26 //
27 //   LineNumber and LinePosition aren't being tracked.
28 //
29 //   xml:space, xml:lang, and xml:base aren't being tracked.
30 //
31
32 using System;
33 using System.Collections;
34 using System.IO;
35 using System.Text;
36
37 namespace System.Xml
38 {
39         public class XmlTextReader : XmlReader, IXmlLineInfo
40         {
41                 #region Constructors
42
43                 [MonoTODO]
44                 protected XmlTextReader ()
45                 {
46                         throw new NotImplementedException ();
47                 }
48
49                 [MonoTODO]
50                 public XmlTextReader (Stream input)
51                 {
52                         // We can share some code in the constructors (at least for this one and next 2)
53                         XmlNameTable nt = new NameTable ();
54                         XmlNamespaceManager nsMgr = new XmlNamespaceManager (nt);
55                         parserContext = new XmlParserContext (null, nsMgr, null, XmlSpace.None);
56                         Init ();
57                         reader = new StreamReader (input);
58                 }
59
60                 [MonoTODO]
61                 public XmlTextReader (string url)
62                 {
63                         XmlNameTable nt = new NameTable ();
64                         XmlNamespaceManager nsMgr = new XmlNamespaceManager (nt);
65                         parserContext = new XmlParserContext (null, nsMgr, null, XmlSpace.None);
66                         Init ();
67                         reader = new StreamReader(url);
68                 }
69
70                 [MonoTODO]
71                 public XmlTextReader (TextReader input)
72                 {
73                         XmlNameTable nt = new NameTable ();
74                         XmlNamespaceManager nsMgr = new XmlNamespaceManager (nt);
75                         parserContext = new XmlParserContext (null, nsMgr, null, XmlSpace.None);
76                         Init ();
77                         reader = input;
78                 }
79
80                 [MonoTODO]
81                 protected XmlTextReader (XmlNameTable nt)
82                 {
83                         throw new NotImplementedException ();
84                 }
85
86                 [MonoTODO]
87                 public XmlTextReader (Stream input, XmlNameTable nt)
88                 {
89                         throw new NotImplementedException ();
90                 }
91
92                 [MonoTODO]
93                 public XmlTextReader (string url, Stream input)
94                 {
95                         throw new NotImplementedException ();
96                 }
97
98                 [MonoTODO]
99                 public XmlTextReader (string url, TextReader input)
100                 {
101                         throw new NotImplementedException ();
102                 }
103
104                 [MonoTODO]
105                 public XmlTextReader (string url, XmlNameTable nt)
106                 {
107                         throw new NotImplementedException ();
108                 }
109
110                 [MonoTODO]
111                 public XmlTextReader (TextReader input, XmlNameTable nt)
112                 {
113                         throw new NotImplementedException ();
114                 }
115
116                 [MonoTODO]
117                 public XmlTextReader (Stream xmlFragment, XmlNodeType fragType, XmlParserContext context)
118                 {
119                         throw new NotImplementedException ();
120                 }
121
122                 [MonoTODO]
123                 public XmlTextReader (string url, Stream input, XmlNameTable nt)
124                 {
125                         throw new NotImplementedException ();
126                 }
127
128                 [MonoTODO]
129                 public XmlTextReader (string url, TextReader input, XmlNameTable nt)
130                 {
131                         throw new NotImplementedException ();
132                 }
133
134                 [MonoTODO]
135                 public XmlTextReader (string xmlFragment, XmlNodeType fragType, XmlParserContext context)
136                 {
137                         //Waiting for Validating reader for fragType rules.
138                         parserContext = context;
139                         Init ();
140                         reader = new StringReader(xmlFragment);
141                 }
142
143                 #endregion
144
145                 #region Properties
146
147                 public override int AttributeCount
148                 {
149                         get { return attributes.Count; }
150                 }
151
152                 [MonoTODO]
153                 public override string BaseURI
154                 {
155                         get { throw new NotImplementedException (); }
156                 }
157
158                 public override int Depth
159                 {
160                         get { return depth > 0 ? depth : 0; }
161                 }
162
163                 [MonoTODO]
164                 public Encoding Encoding
165                 {
166                         get { throw new NotImplementedException (); }
167                 }
168
169                 public override bool EOF
170                 {
171                         get
172                         {
173                                 return
174                                         readState == ReadState.EndOfFile ||
175                                         readState == ReadState.Closed;
176                         }
177                 }
178
179                 public override bool HasValue
180                 {
181                         get { return value != String.Empty;     }
182                 }
183
184                 public override bool IsDefault
185                 {
186                         get
187                         {
188                                 // XmlTextReader does not expand default attributes.
189                                 return false;
190                         }
191                 }
192
193                 public override bool IsEmptyElement
194                 {
195                         get { return isEmptyElement; }
196                 }
197
198                 public override string this [int i]
199                 {
200                         get { return GetAttribute (i); }
201                 }
202
203                 public override string this [string name]
204                 {
205                         get { return GetAttribute (name); }
206                 }
207
208                 public override string this [string localName, string namespaceName]
209                 {
210                         get { return GetAttribute (localName, namespaceName); }
211                 }
212
213                 public int LineNumber
214                 {
215                         get { return line; }
216                 }
217
218                 public int LinePosition
219                 {
220                         get { return column; }
221                 }
222
223                 public override string LocalName
224                 {
225                         get { return localName; }
226                 }
227
228                 public override string Name
229                 {
230                         get { return name; }
231                 }
232
233                 [MonoTODO]
234                 public bool Namespaces
235                 {
236                         get { throw new NotImplementedException (); }
237                         set { throw new NotImplementedException (); }
238                 }
239
240                 public override string NamespaceURI
241                 {
242                         get { return namespaceURI; }
243                 }
244
245                 public override XmlNameTable NameTable
246                 {
247                         get { return parserContext.NameTable; }
248                 }
249
250                 public override XmlNodeType NodeType
251                 {
252                         get { return nodeType; }
253                 }
254
255                 [MonoTODO]
256                 public bool Normalization
257                 {
258                         get { throw new NotImplementedException (); }
259                         set { throw new NotImplementedException (); }
260                 }
261
262                 public override string Prefix
263                 {
264                         get { return prefix; }
265                 }
266
267                 [MonoTODO]
268                 public override char QuoteChar
269                 {
270                         get { throw new NotImplementedException (); }
271                 }
272
273                 public override ReadState ReadState
274                 {
275                         get { return readState; }
276                 }
277
278                 public override string Value
279                 {
280                         get { return value; }
281                 }
282
283                 [MonoTODO]
284                 public WhitespaceHandling WhitespaceHandling
285                 {
286                         get { throw new NotImplementedException (); }
287                         set { throw new NotImplementedException (); }
288                 }
289
290                 [MonoTODO]
291                 public override string XmlLang
292                 {
293                         get { throw new NotImplementedException (); }
294                 }
295
296                 [MonoTODO]
297                 public XmlResolver XmlResolver
298                 {
299                         set { throw new NotImplementedException (); }
300                 }
301
302                 [MonoTODO]
303                 public override XmlSpace XmlSpace
304                 {
305                         get { throw new NotImplementedException (); }
306                 }
307
308                 #endregion
309
310                 #region Methods
311
312                 [MonoTODO]
313                 public override void Close ()
314                 {
315                         readState = ReadState.Closed;
316                 }
317
318                 [MonoTODO]
319                 public override string GetAttribute (int i)
320                 {
321                         throw new NotImplementedException ();
322                 }
323
324                 public override string GetAttribute (string name)
325                 {
326                         return attributes [name] as string;
327                 }
328
329                 public override string GetAttribute (string localName, string namespaceURI)
330                 {
331                         foreach (DictionaryEntry entry in attributes)
332                         {
333                                 string thisName = entry.Key as string;
334
335                                 int indexOfColon = thisName.IndexOf (':');
336
337                                 if (indexOfColon != -1) {
338                                         string thisLocalName = thisName.Substring (indexOfColon + 1);
339
340                                         if (localName == thisLocalName) {
341                                                 string thisPrefix = thisName.Substring (0, indexOfColon);
342                                                 string thisNamespaceURI = LookupNamespace (thisPrefix);
343
344                                                 if (namespaceURI == thisNamespaceURI)
345                                                         return attributes [thisName] as string;
346                                         }
347                                 } else if (localName == "xmlns" && namespaceURI == "http://www.w3.org/2000/xmlns/" && thisName == "xmlns")
348                                         return attributes [thisName] as string;
349                         }
350
351                         return String.Empty;
352                 }
353
354                 [MonoTODO]
355                 public TextReader GetRemainder ()
356                 {
357                         throw new NotImplementedException ();
358                 }
359
360                 [MonoTODO]
361                 bool IXmlLineInfo.HasLineInfo ()
362                 {
363                         return false;
364                 }
365
366                 public override string LookupNamespace (string prefix)
367                 {
368                         return parserContext.NamespaceManager.LookupNamespace (prefix);
369                 }
370
371                 [MonoTODO]
372                 public override void MoveToAttribute (int i)
373                 {
374                         throw new NotImplementedException ();
375                 }
376
377                 public override bool MoveToAttribute (string name)
378                 {
379                         MoveToElement ();
380                         bool match = false;
381
382                         if (attributes == null)
383                                 return false;
384
385                         if (orderedAttributesEnumerator == null) {
386                                 SaveProperties ();
387                                 orderedAttributesEnumerator = orderedAttributes.GetEnumerator ();
388                         }
389
390                         while (orderedAttributesEnumerator.MoveNext ()) {
391                                 if(name == orderedAttributesEnumerator.Current as string) {
392                                         match = true;
393                                         break;
394                                 }
395                         }
396
397                         if (match) {
398                                         
399                                 string value = attributes [name] as string;
400                                 SetProperties (
401                                         XmlNodeType.Attribute, // nodeType
402                                         name, // name
403                                         false, // isEmptyElement
404                                         value, // value
405                                         false // clearAttributes
406                                 );
407                         }
408                         
409                         return match;
410                 }
411
412                 [MonoTODO]
413                 public override bool MoveToAttribute (string localName, string namespaceName)
414                 {
415                         throw new NotImplementedException ();
416                 }
417
418                 public override bool MoveToElement ()
419                 {
420                         if (orderedAttributesEnumerator != null) {
421                                 orderedAttributesEnumerator = null;
422                                 RestoreProperties ();
423                                 return true;
424                         }
425
426                         return false;
427                 }
428
429                 public override bool MoveToFirstAttribute ()
430                 {
431                         MoveToElement ();
432                         return MoveToNextAttribute ();
433                 }
434
435                 public override bool MoveToNextAttribute ()
436                 {
437                         if (attributes == null)
438                                 return false;
439
440                         if (orderedAttributesEnumerator == null) {
441                                 SaveProperties ();
442                                 orderedAttributesEnumerator = orderedAttributes.GetEnumerator ();
443                         }
444
445                         if (orderedAttributesEnumerator.MoveNext ()) {
446                                 string name = orderedAttributesEnumerator.Current as string;
447                                 string value = attributes [name] as string;
448                                 SetProperties (
449                                         XmlNodeType.Attribute, // nodeType
450                                         name, // name
451                                         false, // isEmptyElement
452                                         value, // value
453                                         false // clearAttributes
454                                 );
455                                 return true;
456                         }
457
458                         return false;
459                 }
460
461                 public override bool Read ()
462                 {
463                         bool more = false;
464
465                         readState = ReadState.Interactive;
466
467                         more = ReadContent ();
468
469                         return more;
470                 }
471
472                 [MonoTODO]
473                 public override bool ReadAttributeValue ()
474                 {
475                         throw new NotImplementedException ();
476                 }
477
478                 [MonoTODO]
479                 public int ReadBase64 (byte [] buffer, int offset, int length)
480                 {
481                         throw new NotImplementedException ();
482                 }
483
484                 [MonoTODO]
485                 public int ReadBinHex (byte [] buffer, int offset, int length)
486                 {
487                         throw new NotImplementedException ();
488                 }
489
490                 [MonoTODO]
491                 public int ReadChars (char [] buffer, int offset, int length)
492                 {
493                         throw new NotImplementedException ();
494                 }
495
496                 [MonoTODO]
497                 public override string ReadInnerXml ()
498                 {
499                         // Still need a Well Formedness check.
500                         // Will wait for Validating reader ;-)
501                         if (NodeType == XmlNodeType.Attribute) {
502                                 return Value;
503                         } else {
504                                 saveToXmlBuffer = true;
505                                 string startname = this.Name;
506                                 string endname = string.Empty;
507                                 readState = ReadState.Interactive;
508
509                                 while (startname != endname) {
510                                         ReadContent ();
511                                 endname = this.Name;
512                                 }
513
514                                 xmlBuffer.Replace(currentTag.ToString (), "");
515                                 saveToXmlBuffer = false;
516                                 string InnerXml = xmlBuffer.ToString ();
517                                 xmlBuffer.Length = 0;
518                                 return InnerXml;
519                         }
520                 }
521
522                 [MonoTODO]
523                 public override string ReadOuterXml ()
524                 {
525                         // Still need a Well Formedness check.
526                         // Will wait for Validating reader ;-)
527                         if (NodeType == XmlNodeType.Attribute) {
528                                 return Name+"=\""+Value+"\"";
529                         } else {
530                                 saveToXmlBuffer = true;
531                                 xmlBuffer.Append(currentTag.ToString ());
532                                 string startname = this.Name;
533                                 string endname = string.Empty;
534                                 readState = ReadState.Interactive;
535
536                                 while (startname != endname) {
537                                         ReadContent ();
538                                 endname = this.Name;
539                                 }
540                                 saveToXmlBuffer = false;
541                                 string OuterXml = xmlBuffer.ToString ();
542                                 xmlBuffer.Length = 0;
543                                 return OuterXml;
544                         }
545                 }
546
547                 [MonoTODO]
548                 public override string ReadString ()
549                 {
550                         throw new NotImplementedException ();
551                 }
552
553                 [MonoTODO]
554                 public void ResetState ()
555                 {
556                         throw new NotImplementedException ();
557                 }
558
559                 public override void ResolveEntity ()
560                 {
561                         // XmlTextReaders don't resolve entities.
562                         throw new InvalidOperationException ("XmlTextReaders don't resolve entities.");
563                 }
564
565                 #endregion
566
567                 // privates
568
569                 private XmlParserContext parserContext;
570
571                 private TextReader reader;
572                 private ReadState readState;
573
574                 private int depth;
575                 private bool depthDown;
576
577                 private bool popScope;
578
579                 private XmlNodeType nodeType;
580                 private string name;
581                 private string prefix;
582                 private string localName;
583                 private string namespaceURI;
584                 private bool isEmptyElement;
585                 private string value;
586
587                 private XmlNodeType saveNodeType;
588                 private string saveName;
589                 private string savePrefix;
590                 private string saveLocalName;
591                 private string saveNamespaceURI;
592                 private bool saveIsEmptyElement;
593
594                 private Hashtable attributes;
595                 private ArrayList orderedAttributes;
596                 private IEnumerator orderedAttributesEnumerator;
597
598                 private bool returnEntityReference;
599                 private string entityReferenceName;
600
601                 private char [] nameBuffer;
602                 private int nameLength;
603                 private int nameCapacity;
604                 private const int initialNameCapacity = 256;
605
606                 private char [] valueBuffer;
607                 private int valueLength;
608                 private int valueCapacity;
609                 private const int initialValueCapacity = 8192;
610
611                 private StringBuilder xmlBuffer; // This is for Read(Inner|Outer)Xml
612                 private StringBuilder currentTag; // A buffer for ReadContent for ReadOuterXml
613                 private bool saveToXmlBuffer;
614                 private int line;
615                 private int column;
616
617                 private void Init ()
618                 {
619                         readState = ReadState.Initial;
620
621                         depth = -1;
622                         depthDown = false;
623
624                         popScope = false;
625
626                         nodeType = XmlNodeType.None;
627                         name = String.Empty;
628                         prefix = String.Empty;
629                         localName = string.Empty;
630                         isEmptyElement = false;
631                         value = String.Empty;
632
633                         attributes = new Hashtable ();
634                         orderedAttributes = new ArrayList ();
635                         orderedAttributesEnumerator = null;
636
637                         returnEntityReference = false;
638                         entityReferenceName = String.Empty;
639
640                         nameBuffer = new char [initialNameCapacity];
641                         nameLength = 0;
642                         nameCapacity = initialNameCapacity;
643
644                         valueBuffer = new char [initialValueCapacity];
645                         valueLength = 0;
646                         valueCapacity = initialValueCapacity;
647
648                         xmlBuffer = new StringBuilder ();
649                         currentTag = new StringBuilder ();
650                 }
651
652                 // Use this method rather than setting the properties
653                 // directly so that all the necessary properties can
654                 // be changed in harmony with each other. Maybe the
655                 // fields should be in a seperate class to help enforce
656                 // this.
657                 private void SetProperties (
658                         XmlNodeType nodeType,
659                         string name,
660                         bool isEmptyElement,
661                         string value,
662                         bool clearAttributes)
663                 {
664                         this.nodeType = nodeType;
665                         this.name = name;
666                         this.isEmptyElement = isEmptyElement;
667                         this.value = value;
668
669                         if (clearAttributes)
670                                 ClearAttributes ();
671
672                         int indexOfColon = name.IndexOf (':');
673
674                         if (indexOfColon == -1) {
675                                 prefix = String.Empty;
676                                 localName = name;
677                         } else {
678                                 prefix = name.Substring (0, indexOfColon);
679                                 localName = name.Substring (indexOfColon + 1);
680                         }
681
682                         namespaceURI = LookupNamespace (prefix);
683                 }
684
685                 private void SaveProperties ()
686                 {
687                         saveNodeType = nodeType;
688                         saveName = name;
689                         savePrefix = prefix;
690                         saveLocalName = localName;
691                         saveNamespaceURI = namespaceURI;
692                         saveIsEmptyElement = isEmptyElement;
693                         // An element's value is always String.Empty.
694                 }
695
696                 private void RestoreProperties ()
697                 {
698                         nodeType = saveNodeType;
699                         name = saveName;
700                         prefix = savePrefix;
701                         localName = saveLocalName;
702                         namespaceURI = saveNamespaceURI;
703                         isEmptyElement = saveIsEmptyElement;
704                         value = String.Empty;
705                 }
706
707                 private void AddAttribute (string name, string value)
708                 {
709                         attributes.Add (name, value);
710                         orderedAttributes.Add (name);
711                 }
712
713                 private void ClearAttributes ()
714                 {
715                         if (attributes.Count > 0) {
716                                 attributes.Clear ();
717                                 orderedAttributes.Clear ();
718                         }
719
720                         orderedAttributesEnumerator = null;
721                 }
722
723                 private int PeekChar ()
724                 {
725                         return reader.Peek ();
726                 }
727
728                 private int ReadChar ()
729                 {
730                         int ch = reader.Read ();
731                         if (ch == '\n') {
732                                 line++;
733                                 column = 1;
734                         } else {
735                                 column++;
736                         }
737                         if (saveToXmlBuffer) {
738                                 xmlBuffer.Append ((char) ch);
739                         }
740                         currentTag.Append ((char) ch);
741                         return ch;
742                 }
743
744                 // This should really keep track of some state so
745                 // that it's not possible to have more than one document
746                 // element or text outside of the document element.
747                 private bool ReadContent ()
748                 {
749                         bool more = false;
750                         currentTag.Length = 0;
751                         if (popScope) {
752                                 parserContext.NamespaceManager.PopScope ();
753                                 popScope = false;
754                         }
755
756                         if (depthDown)
757                                 --depth;
758
759                         if (returnEntityReference) {
760                                 ++depth;
761                                 SetEntityReferenceProperties ();
762                                 more = true;
763                         } else {
764                         switch (PeekChar ())
765                                 {
766                                 case '<':
767                                         ReadChar ();
768                                         ReadTag ();
769                                         more = true;
770                                         break;
771                                 case -1:
772                                         readState = ReadState.EndOfFile;
773                                         SetProperties (
774                                                 XmlNodeType.None, // nodeType
775                                                 String.Empty, // name
776                                                 false, // isEmptyElement
777                                                 String.Empty, // value
778                                                 true // clearAttributes
779                                         );
780                                         more = false;
781                                         break;
782                                 default:
783                                         ReadText ();
784                                         more = true;
785                                         break;
786                                 }
787                         }
788
789                         return more;
790                 }
791
792                 private void SetEntityReferenceProperties ()
793                 {
794                         SetProperties (
795                                 XmlNodeType.EntityReference, // nodeType
796                                 entityReferenceName, // name
797                                 false, // isEmptyElement
798                                 String.Empty, // value
799                                 true // clearAttributes
800                         );
801
802                         returnEntityReference = false;
803                         entityReferenceName = String.Empty;
804                 }
805
806                 // The leading '<' has already been consumed.
807                 private void ReadTag ()
808                 {
809                         switch (PeekChar ())
810                         {
811                         case '/':
812                                 ReadChar ();
813                                 ReadEndTag ();
814                                 break;
815                         case '?':
816                                 ReadChar ();
817                                 ReadProcessingInstruction ();
818                                 break;
819                         case '!':
820                                 ReadChar ();
821                                 ReadDeclaration ();
822                                 break;
823                         default:
824                                 ReadStartTag ();
825                                 break;
826                         }
827                 }
828
829                 // The leading '<' has already been consumed.
830                 private void ReadStartTag ()
831                 {
832                         parserContext.NamespaceManager.PushScope ();
833
834                         string name = ReadName ();
835                         SkipWhitespace ();
836
837                         bool isEmptyElement = false;
838
839                         ClearAttributes ();
840
841                         if (XmlChar.IsFirstNameChar (PeekChar ()))
842                                 ReadAttributes ();
843
844                         if (PeekChar () == '/') {
845                                 ReadChar ();
846                                 isEmptyElement = true;
847                                 depthDown = true;
848                                 popScope = true;
849                         }
850
851                         Expect ('>');
852
853                         ++depth;
854
855                         SetProperties (
856                                 XmlNodeType.Element, // nodeType
857                                 name, // name
858                                 isEmptyElement, // isEmptyElement
859                                 String.Empty, // value
860                                 false // clearAttributes
861                         );
862                 }
863
864                 // The reader is positioned on the first character
865                 // of the element's name.
866                 private void ReadEndTag ()
867                 {
868                         string name = ReadName ();
869                         SkipWhitespace ();
870                         Expect ('>');
871
872                         --depth;
873
874                         SetProperties (
875                                 XmlNodeType.EndElement, // nodeType
876                                 name, // name
877                                 false, // isEmptyElement
878                                 String.Empty, // value
879                                 true // clearAttributes
880                         );
881
882                         popScope = true;
883                 }
884
885                 private void AppendNameChar (int ch)
886                 {
887                         CheckNameCapacity ();
888                         nameBuffer [nameLength++] = (char)ch;
889                 }
890
891                 private void CheckNameCapacity ()
892                 {
893                         if (nameLength == nameCapacity) {
894                                 nameCapacity = nameCapacity * 2;
895                                 char [] oldNameBuffer = nameBuffer;
896                                 nameBuffer = new char [nameCapacity];
897                                 Array.Copy (oldNameBuffer, nameBuffer, nameLength);
898                         }
899                 }
900
901                 private string CreateNameString ()
902                 {
903                         return new String (nameBuffer, 0, nameLength);
904                 }
905
906                 private void AppendValueChar (int ch)
907                 {
908                         CheckValueCapacity ();
909                         valueBuffer [valueLength++] = (char)ch;
910                 }
911
912                 private void CheckValueCapacity ()
913                 {
914                         if (valueLength == valueCapacity) {
915                                 valueCapacity = valueCapacity * 2;
916                                 char [] oldValueBuffer = valueBuffer;
917                                 valueBuffer = new char [valueCapacity];
918                                 Array.Copy (oldValueBuffer, valueBuffer, valueLength);
919                         }
920                 }
921
922                 private string CreateValueString ()
923                 {
924                         return new String (valueBuffer, 0, valueLength);
925                 }
926
927                 // The reader is positioned on the first character
928                 // of the text.
929                 private void ReadText ()
930                 {
931                         valueLength = 0;
932
933                         int ch = PeekChar ();
934
935                         while (ch != '<' && ch != -1) {
936                                 if (ch == '&') {
937                                         ReadChar ();
938                                         if (ReadReference (false))
939                                                 break;
940                                 } else
941                                         AppendValueChar (ReadChar ());
942
943                                 ch = PeekChar ();
944                         }
945
946                         if (returnEntityReference && valueLength == 0) {
947                                 ++depth;
948                                 SetEntityReferenceProperties ();
949                         } else {
950                                 if (depth >= 0) {
951                                         ++depth;
952                                         depthDown = true;
953                                 }
954
955                                 SetProperties (
956                                         XmlNodeType.Text, // nodeType
957                                         String.Empty, // name
958                                         false, // isEmptyElement
959                                         CreateValueString (), // value
960                                         true // clearAttributes
961                                 );
962                         }
963                 }
964
965                 // The leading '&' has already been consumed.
966                 // Returns true if the entity reference isn't a simple
967                 // character reference or one of the predefined entities.
968                 // This allows the ReadText method to break so that the
969                 // next call to Read will return the EntityReference node.
970                 private bool ReadReference (bool ignoreEntityReferences)
971                 {
972                         if (PeekChar () == '#') {
973                                 ReadChar ();
974                                 ReadCharacterReference ();
975                         } else
976                                 ReadEntityReference (ignoreEntityReferences);
977
978                         return returnEntityReference;
979                 }
980
981                 private void ReadCharacterReference ()
982                 {
983                         int value = 0;
984
985                         if (PeekChar () == 'x') {
986                                 ReadChar ();
987
988                                 while (PeekChar () != ';' && PeekChar () != -1) {
989                                         int ch = ReadChar ();
990
991                                         if (ch >= '0' && ch <= '9')
992                                                 value = (value << 4) + ch - '0';
993                                         else if (ch >= 'A' && ch <= 'F')
994                                                 value = (value << 4) + ch - 'A' + 10;
995                                         else if (ch >= 'a' && ch <= 'f')
996                                                 value = (value << 4) + ch - 'a' + 10;
997                                         else
998                                                 throw new XmlException (
999                                                         String.Format (
1000                                                                 "invalid hexadecimal digit: {0} (#x{1:X})",
1001                                                                 (char)ch,
1002                                                                 ch));
1003                                 }
1004                         } else {
1005                                 while (PeekChar () != ';' && PeekChar () != -1) {
1006                                         int ch = ReadChar ();
1007
1008                                         if (ch >= '0' && ch <= '9')
1009                                                 value = value * 10 + ch - '0';
1010                                         else
1011                                                 throw new XmlException (
1012                                                         String.Format (
1013                                                                 "invalid decimal digit: {0} (#x{1:X})",
1014                                                                 (char)ch,
1015                                                                 ch));
1016                                 }
1017                         }
1018
1019                         ReadChar (); // ';'
1020
1021                         AppendValueChar (value);
1022                 }
1023
1024                 private void ReadEntityReference (bool ignoreEntityReferences)
1025                 {
1026                         nameLength = 0;
1027
1028                         int ch = PeekChar ();
1029
1030                         while (ch != ';' && ch != -1) {
1031                                 AppendNameChar (ReadChar ());
1032                                 ch = PeekChar ();
1033                         }
1034
1035                         Expect (';');
1036
1037                         string name = CreateNameString ();
1038
1039                         switch (name)
1040                         {
1041                                 case "lt":
1042                                         AppendValueChar ('<');
1043                                         break;
1044                                 case "gt":
1045                                         AppendValueChar ('>');
1046                                         break;
1047                                 case "amp":
1048                                         AppendValueChar ('&');
1049                                         break;
1050                                 case "apos":
1051                                         AppendValueChar ('\'');
1052                                         break;
1053                                 case "quot":
1054                                         AppendValueChar ('"');
1055                                         break;
1056                                 default:
1057                                         if (ignoreEntityReferences) {
1058                                                 AppendValueChar ('&');
1059
1060                                                 foreach (char ch2 in name) {
1061                                                         AppendValueChar (ch2);
1062                                                 }
1063
1064                                                 AppendValueChar (';');
1065                                         } else {
1066                                                 returnEntityReference = true;
1067                                                 entityReferenceName = name;
1068                                         }
1069                                         break;
1070                         }
1071                 }
1072
1073                 // The reader is positioned on the first character of
1074                 // the attribute name.
1075                 private void ReadAttributes ()
1076                 {
1077                         do {
1078                                 string name = ReadName ();
1079                                 SkipWhitespace ();
1080                                 Expect ('=');
1081                                 SkipWhitespace ();
1082                                 string value = ReadAttribute ();
1083                                 SkipWhitespace ();
1084
1085                                 if (name == "xmlns")
1086                                         parserContext.NamespaceManager.AddNamespace (String.Empty, value);
1087                                 else if (name.StartsWith ("xmlns:"))
1088                                         parserContext.NamespaceManager.AddNamespace (name.Substring (6), value);
1089
1090                                 AddAttribute (name, value);
1091                         } while (PeekChar () != '/' && PeekChar () != '>' && PeekChar () != -1);
1092                 }
1093
1094                 // The reader is positioned on the quote character.
1095                 private string ReadAttribute ()
1096                 {
1097                         int quoteChar = ReadChar ();
1098
1099                         if (quoteChar != '\'' && quoteChar != '\"')
1100                                 throw new XmlException ("an attribute value was not quoted");
1101
1102                         valueLength = 0;
1103
1104                         while (PeekChar () != quoteChar) {
1105                                 int ch = ReadChar ();
1106
1107                                 switch (ch)
1108                                 {
1109                                 case '<':
1110                                         throw new XmlException ("attribute values cannot contain '<'");
1111                                 case '&':
1112                                         ReadReference (true);
1113                                         break;
1114                                 case -1:
1115                                         throw new XmlException ("unexpected end of file in an attribute value");
1116                                 default:
1117                                         AppendValueChar (ch);
1118                                         break;
1119                                 }
1120                         }
1121
1122                         ReadChar (); // quoteChar
1123
1124                         return CreateValueString ();
1125                 }
1126
1127                 // The reader is positioned on the first character
1128                 // of the target.
1129                 private void ReadProcessingInstruction ()
1130                 {
1131                         string target = ReadName ();
1132                         SkipWhitespace ();
1133
1134                         valueLength = 0;
1135
1136                         while (PeekChar () != -1) {
1137                                 int ch = ReadChar ();
1138
1139                                 if (ch == '?' && PeekChar () == '>') {
1140                                         ReadChar ();
1141                                         break;
1142                                 }
1143
1144                                 AppendValueChar ((char)ch);
1145                         }
1146
1147                         SetProperties (
1148                                 XmlNodeType.ProcessingInstruction, // nodeType
1149                                 target, // name
1150                                 false, // isEmptyElement
1151                                 CreateValueString (), // value
1152                                 true // clearAttributes
1153                         );
1154                 }
1155
1156                 // The reader is positioned on the first character after
1157                 // the leading '<!'.
1158                 private void ReadDeclaration ()
1159                 {
1160                         int ch = PeekChar ();
1161
1162                         switch (ch)
1163                         {
1164                         case '-':
1165                                 Expect ('-');
1166                                 Expect ('-');
1167                                 ReadComment ();
1168                                 break;
1169                         case '[':
1170                                 ReadChar ();
1171                                 Expect ('C');
1172                                 Expect ('D');
1173                                 Expect ('A');
1174                                 Expect ('T');
1175                                 Expect ('A');
1176                                 Expect ('[');
1177                                 ReadCDATA ();
1178                                 break;
1179                         }
1180                 }
1181
1182                 // The reader is positioned on the first character after
1183                 // the leading '<!--'.
1184                 private void ReadComment ()
1185                 {
1186                         valueLength = 0;
1187
1188                         while (PeekChar () != -1) {
1189                                 int ch = ReadChar ();
1190
1191                                 if (ch == '-' && PeekChar () == '-') {
1192                                         ReadChar ();
1193
1194                                         if (PeekChar () != '>')
1195                                                 throw new XmlException ("comments cannot contain '--'");
1196
1197                                         ReadChar ();
1198                                         break;
1199                                 }
1200
1201                                 AppendValueChar ((char)ch);
1202                         }
1203
1204                         SetProperties (
1205                                 XmlNodeType.Comment, // nodeType
1206                                 String.Empty, // name
1207                                 false, // isEmptyElement
1208                                 CreateValueString (), // value
1209                                 true // clearAttributes
1210                         );
1211                 }
1212
1213                 // The reader is positioned on the first character after
1214                 // the leading '<![CDATA['.
1215                 private void ReadCDATA ()
1216                 {
1217                         valueLength = 0;
1218
1219                         while (PeekChar () != -1) {
1220                                 int ch = ReadChar ();
1221
1222                                 if (ch == ']' && PeekChar () == ']') {
1223                                         ch = ReadChar (); // ']'
1224
1225                                         if (PeekChar () == '>') {
1226                                                 ReadChar (); // '>'
1227                                                 break;
1228                                         } else {
1229                                                 AppendValueChar (']');
1230                                                 AppendValueChar (']');
1231                                                 ch = ReadChar ();
1232                                         }
1233                                 }
1234
1235                                 AppendValueChar ((char)ch);
1236                         }
1237
1238                         ++depth;
1239
1240                         SetProperties (
1241                                 XmlNodeType.CDATA, // nodeType
1242                                 String.Empty, // name
1243                                 false, // isEmptyElement
1244                                 CreateValueString (), // value
1245                                 true // clearAttributes
1246                         );
1247                 }
1248
1249                 // The reader is positioned on the first character
1250                 // of the name.
1251                 private string ReadName ()
1252                 {
1253                         if (!XmlChar.IsFirstNameChar (PeekChar ()))
1254                                 throw new XmlException ("a name did not start with a legal character");
1255
1256                         nameLength = 0;
1257
1258                         AppendNameChar (ReadChar ());
1259
1260                         while (XmlChar.IsNameChar (PeekChar ())) {
1261                                 AppendNameChar (ReadChar ());
1262                         }
1263
1264                         return CreateNameString ();
1265                 }
1266
1267                 // Read the next character and compare it against the
1268                 // specified character.
1269                 private void Expect (int expected)
1270                 {
1271                         int ch = ReadChar ();
1272
1273                         if (ch != expected) {
1274                                 throw new XmlException (
1275                                         String.Format (
1276                                                 "expected '{0}' ({1:X}) but found '{2}' ({3:X})",
1277                                                 (char)expected,
1278                                                 expected,
1279                                                 (char)ch,
1280                                                 ch));
1281                         }
1282                 }
1283
1284                 // Does not consume the first non-whitespace character.
1285                 private void SkipWhitespace ()
1286                 {
1287                         while (XmlChar.IsWhitespace (PeekChar ()))
1288                                 ReadChar ();
1289                 }
1290         }
1291 }