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