Fixed MoveToParent issues with the navigator and attributes.
[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                 [MonoTODO]
214                 public int LineNumber
215                 {
216                         get { throw new NotImplementedException (); }
217                 }
218
219                 [MonoTODO]
220                 public int LinePosition
221                 {
222                         get { throw new NotImplementedException (); }
223                 }
224
225                 public override string LocalName
226                 {
227                         get { return localName; }
228                 }
229
230                 public override string Name
231                 {
232                         get { return name; }
233                 }
234
235                 [MonoTODO]
236                 public bool Namespaces
237                 {
238                         get { throw new NotImplementedException (); }
239                         set { throw new NotImplementedException (); }
240                 }
241
242                 public override string NamespaceURI
243                 {
244                         get { return namespaceURI; }
245                 }
246
247                 public override XmlNameTable NameTable
248                 {
249                         get { return parserContext.NameTable; }
250                 }
251
252                 public override XmlNodeType NodeType
253                 {
254                         get { return nodeType; }
255                 }
256
257                 [MonoTODO]
258                 public bool Normalization
259                 {
260                         get { throw new NotImplementedException (); }
261                         set { throw new NotImplementedException (); }
262                 }
263
264                 public override string Prefix
265                 {
266                         get { return prefix; }
267                 }
268
269                 [MonoTODO]
270                 public override char QuoteChar
271                 {
272                         get { throw new NotImplementedException (); }
273                 }
274
275                 public override ReadState ReadState
276                 {
277                         get { return readState; }
278                 }
279
280                 public override string Value
281                 {
282                         get { return value; }
283                 }
284
285                 [MonoTODO]
286                 public WhitespaceHandling WhitespaceHandling
287                 {
288                         get { throw new NotImplementedException (); }
289                         set { throw new NotImplementedException (); }
290                 }
291
292                 [MonoTODO]
293                 public override string XmlLang
294                 {
295                         get { throw new NotImplementedException (); }
296                 }
297
298                 [MonoTODO]
299                 public XmlResolver XmlResolver
300                 {
301                         set { throw new NotImplementedException (); }
302                 }
303
304                 [MonoTODO]
305                 public override XmlSpace XmlSpace
306                 {
307                         get { throw new NotImplementedException (); }
308                 }
309
310                 #endregion
311
312                 #region Methods
313
314                 [MonoTODO]
315                 public override void Close ()
316                 {
317                         readState = ReadState.Closed;
318                 }
319
320                 [MonoTODO]
321                 public override string GetAttribute (int i)
322                 {
323                         throw new NotImplementedException ();
324                 }
325
326                 public override string GetAttribute (string name)
327                 {
328                         return attributes [name] as string;
329                 }
330
331                 public override string GetAttribute (string localName, string namespaceURI)
332                 {
333                         foreach (DictionaryEntry entry in attributes)
334                         {
335                                 string thisName = entry.Key as string;
336
337                                 int indexOfColon = thisName.IndexOf (':');
338
339                                 if (indexOfColon != -1) {
340                                         string thisLocalName = thisName.Substring (indexOfColon + 1);
341
342                                         if (localName == thisLocalName) {
343                                                 string thisPrefix = thisName.Substring (0, indexOfColon);
344                                                 string thisNamespaceURI = LookupNamespace (thisPrefix);
345
346                                                 if (namespaceURI == thisNamespaceURI)
347                                                         return attributes [thisName] as string;
348                                         }
349                                 } else if (localName == "xmlns" && namespaceURI == "http://www.w3.org/2000/xmlns/" && thisName == "xmlns")
350                                         return attributes [thisName] as string;
351                         }
352
353                         return String.Empty;
354                 }
355
356                 [MonoTODO]
357                 public TextReader GetRemainder ()
358                 {
359                         throw new NotImplementedException ();
360                 }
361
362                 [MonoTODO]
363                 bool IXmlLineInfo.HasLineInfo ()
364                 {
365                         return false;
366                 }
367
368                 public override string LookupNamespace (string prefix)
369                 {
370                         return parserContext.NamespaceManager.LookupNamespace (prefix);
371                 }
372
373                 [MonoTODO]
374                 public override void MoveToAttribute (int i)
375                 {
376                         throw new NotImplementedException ();
377                 }
378
379                 public override bool MoveToAttribute (string name)
380                 {
381                         MoveToElement ();
382                         bool match = false;
383
384                         if (attributes == null)
385                                 return false;
386
387                         if (orderedAttributesEnumerator == null) {
388                                 SaveProperties ();
389                                 orderedAttributesEnumerator = orderedAttributes.GetEnumerator ();
390                         }
391
392                         while (orderedAttributesEnumerator.MoveNext ()) {
393                                 if(name == orderedAttributesEnumerator.Current as string) {
394                                         match = true;
395                                         break;
396                                 }
397                         }
398
399                         if (match) {
400                                         
401                                 string value = attributes [name] as string;
402                                 SetProperties (
403                                         XmlNodeType.Attribute, // nodeType
404                                         name, // name
405                                         false, // isEmptyElement
406                                         value, // value
407                                         false // clearAttributes
408                                 );
409                         }
410                         
411                         return match;
412                 }
413
414                 [MonoTODO]
415                 public override bool MoveToAttribute (string localName, string namespaceName)
416                 {
417                         throw new NotImplementedException ();
418                 }
419
420                 public override bool MoveToElement ()
421                 {
422                         if (orderedAttributesEnumerator != null) {
423                                 orderedAttributesEnumerator = null;
424                                 RestoreProperties ();
425                                 return true;
426                         }
427
428                         return false;
429                 }
430
431                 public override bool MoveToFirstAttribute ()
432                 {
433                         MoveToElement ();
434                         return MoveToNextAttribute ();
435                 }
436
437                 public override bool MoveToNextAttribute ()
438                 {
439                         if (attributes == null)
440                                 return false;
441
442                         if (orderedAttributesEnumerator == null) {
443                                 SaveProperties ();
444                                 orderedAttributesEnumerator = orderedAttributes.GetEnumerator ();
445                         }
446
447                         if (orderedAttributesEnumerator.MoveNext ()) {
448                                 string name = orderedAttributesEnumerator.Current as string;
449                                 string value = attributes [name] as string;
450                                 SetProperties (
451                                         XmlNodeType.Attribute, // nodeType
452                                         name, // name
453                                         false, // isEmptyElement
454                                         value, // value
455                                         false // clearAttributes
456                                 );
457                                 return true;
458                         }
459
460                         return false;
461                 }
462
463                 public override bool Read ()
464                 {
465                         bool more = false;
466
467                         readState = ReadState.Interactive;
468
469                         more = ReadContent ();
470
471                         return more;
472                 }
473
474                 [MonoTODO]
475                 public override bool ReadAttributeValue ()
476                 {
477                         throw new NotImplementedException ();
478                 }
479
480                 [MonoTODO]
481                 public int ReadBase64 (byte [] buffer, int offset, int length)
482                 {
483                         throw new NotImplementedException ();
484                 }
485
486                 [MonoTODO]
487                 public int ReadBinHex (byte [] buffer, int offset, int length)
488                 {
489                         throw new NotImplementedException ();
490                 }
491
492                 [MonoTODO]
493                 public int ReadChars (char [] buffer, int offset, int length)
494                 {
495                         throw new NotImplementedException ();
496                 }
497
498                 [MonoTODO]
499                 public override string ReadInnerXml ()
500                 {
501                         // Still need a Well Formedness check.
502                         // Will wait for Validating reader ;-)
503                         if (NodeType == XmlNodeType.Attribute) {
504                                 return Value;
505                         } else {
506                                 saveToXmlBuffer = true;
507                                 string startname = this.Name;
508                                 string endname = string.Empty;
509                                 readState = ReadState.Interactive;
510
511                                 while (startname != endname) {
512                                         ReadContent ();
513                                 endname = this.Name;
514                                 }
515
516                                 xmlBuffer.Replace(currentTag.ToString (), "");
517                                 saveToXmlBuffer = false;
518                                 string InnerXml = xmlBuffer.ToString ();
519                                 xmlBuffer.Length = 0;
520                                 return InnerXml;
521                         }
522                 }
523
524                 [MonoTODO]
525                 public override string ReadOuterXml ()
526                 {
527                         // Still need a Well Formedness check.
528                         // Will wait for Validating reader ;-)
529                         if (NodeType == XmlNodeType.Attribute) {
530                                 return Name+"=\""+Value+"\"";
531                         } else {
532                                 saveToXmlBuffer = true;
533                                 xmlBuffer.Append(currentTag.ToString ());
534                                 string startname = this.Name;
535                                 string endname = string.Empty;
536                                 readState = ReadState.Interactive;
537
538                                 while (startname != endname) {
539                                         ReadContent ();
540                                 endname = this.Name;
541                                 }
542                                 saveToXmlBuffer = false;
543                                 string OuterXml = xmlBuffer.ToString ();
544                                 xmlBuffer.Length = 0;
545                                 return OuterXml;
546                         }
547                 }
548
549                 [MonoTODO]
550                 public override string ReadString ()
551                 {
552                         throw new NotImplementedException ();
553                 }
554
555                 [MonoTODO]
556                 public void ResetState ()
557                 {
558                         throw new NotImplementedException ();
559                 }
560
561                 public override void ResolveEntity ()
562                 {
563                         // XmlTextReaders don't resolve entities.
564                         throw new InvalidOperationException ("XmlTextReaders don't resolve entities.");
565                 }
566
567                 #endregion
568
569                 // privates
570
571                 private XmlParserContext parserContext;
572
573                 private TextReader reader;
574                 private ReadState readState;
575
576                 private int depth;
577                 private bool depthDown;
578
579                 private bool popScope;
580
581                 private XmlNodeType nodeType;
582                 private string name;
583                 private string prefix;
584                 private string localName;
585                 private string namespaceURI;
586                 private bool isEmptyElement;
587                 private string value;
588
589                 private XmlNodeType saveNodeType;
590                 private string saveName;
591                 private string savePrefix;
592                 private string saveLocalName;
593                 private string saveNamespaceURI;
594                 private bool saveIsEmptyElement;
595
596                 private Hashtable attributes;
597                 private ArrayList orderedAttributes;
598                 private IEnumerator orderedAttributesEnumerator;
599
600                 private bool returnEntityReference;
601                 private string entityReferenceName;
602
603                 private char [] nameBuffer;
604                 private int nameLength;
605                 private int nameCapacity;
606                 private const int initialNameCapacity = 256;
607
608                 private char [] valueBuffer;
609                 private int valueLength;
610                 private int valueCapacity;
611                 private const int initialValueCapacity = 8192;
612
613                 private StringBuilder xmlBuffer; // This is for Read(Inner|Outer)Xml
614                 private StringBuilder currentTag; // A buffer for ReadContent for ReadOuterXml
615                 private bool saveToXmlBuffer;
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 (saveToXmlBuffer) {
732                                 xmlBuffer.Append ((char) ch);
733                         }
734                         currentTag.Append ((char) ch);
735                         return ch;
736                 }
737
738                 // This should really keep track of some state so
739                 // that it's not possible to have more than one document
740                 // element or text outside of the document element.
741                 private bool ReadContent ()
742                 {
743                         bool more = false;
744                         currentTag.Length = 0;
745                         if (popScope) {
746                                 parserContext.NamespaceManager.PopScope ();
747                                 popScope = false;
748                         }
749
750                         if (depthDown)
751                                 --depth;
752
753                         if (returnEntityReference) {
754                                 ++depth;
755                                 SetEntityReferenceProperties ();
756                                 more = true;
757                         } else {
758                         switch (PeekChar ())
759                                 {
760                                 case '<':
761                                         ReadChar ();
762                                         ReadTag ();
763                                         more = true;
764                                         break;
765                                 case -1:
766                                         readState = ReadState.EndOfFile;
767                                         SetProperties (
768                                                 XmlNodeType.None, // nodeType
769                                                 String.Empty, // name
770                                                 false, // isEmptyElement
771                                                 String.Empty, // value
772                                                 true // clearAttributes
773                                         );
774                                         more = false;
775                                         break;
776                                 default:
777                                         ReadText ();
778                                         more = true;
779                                         break;
780                                 }
781                         }
782
783                         return more;
784                 }
785
786                 private void SetEntityReferenceProperties ()
787                 {
788                         SetProperties (
789                                 XmlNodeType.EntityReference, // nodeType
790                                 entityReferenceName, // name
791                                 false, // isEmptyElement
792                                 String.Empty, // value
793                                 true // clearAttributes
794                         );
795
796                         returnEntityReference = false;
797                         entityReferenceName = String.Empty;
798                 }
799
800                 // The leading '<' has already been consumed.
801                 private void ReadTag ()
802                 {
803                         switch (PeekChar ())
804                         {
805                         case '/':
806                                 ReadChar ();
807                                 ReadEndTag ();
808                                 break;
809                         case '?':
810                                 ReadChar ();
811                                 ReadProcessingInstruction ();
812                                 break;
813                         case '!':
814                                 ReadChar ();
815                                 ReadDeclaration ();
816                                 break;
817                         default:
818                                 ReadStartTag ();
819                                 break;
820                         }
821                 }
822
823                 // The leading '<' has already been consumed.
824                 private void ReadStartTag ()
825                 {
826                         parserContext.NamespaceManager.PushScope ();
827
828                         string name = ReadName ();
829                         SkipWhitespace ();
830
831                         bool isEmptyElement = false;
832
833                         ClearAttributes ();
834
835                         if (XmlChar.IsFirstNameChar (PeekChar ()))
836                                 ReadAttributes ();
837
838                         if (PeekChar () == '/') {
839                                 ReadChar ();
840                                 isEmptyElement = true;
841                                 depthDown = true;
842                                 popScope = true;
843                         }
844
845                         Expect ('>');
846
847                         ++depth;
848
849                         SetProperties (
850                                 XmlNodeType.Element, // nodeType
851                                 name, // name
852                                 isEmptyElement, // isEmptyElement
853                                 String.Empty, // value
854                                 false // clearAttributes
855                         );
856                 }
857
858                 // The reader is positioned on the first character
859                 // of the element's name.
860                 private void ReadEndTag ()
861                 {
862                         string name = ReadName ();
863                         SkipWhitespace ();
864                         Expect ('>');
865
866                         --depth;
867
868                         SetProperties (
869                                 XmlNodeType.EndElement, // nodeType
870                                 name, // name
871                                 false, // isEmptyElement
872                                 String.Empty, // value
873                                 true // clearAttributes
874                         );
875
876                         popScope = true;
877                 }
878
879                 private void AppendNameChar (int ch)
880                 {
881                         CheckNameCapacity ();
882                         nameBuffer [nameLength++] = (char)ch;
883                 }
884
885                 private void CheckNameCapacity ()
886                 {
887                         if (nameLength == nameCapacity) {
888                                 nameCapacity = nameCapacity * 2;
889                                 char [] oldNameBuffer = nameBuffer;
890                                 nameBuffer = new char [nameCapacity];
891                                 Array.Copy (oldNameBuffer, nameBuffer, nameLength);
892                         }
893                 }
894
895                 private string CreateNameString ()
896                 {
897                         return new String (nameBuffer, 0, nameLength);
898                 }
899
900                 private void AppendValueChar (int ch)
901                 {
902                         CheckValueCapacity ();
903                         valueBuffer [valueLength++] = (char)ch;
904                 }
905
906                 private void CheckValueCapacity ()
907                 {
908                         if (valueLength == valueCapacity) {
909                                 valueCapacity = valueCapacity * 2;
910                                 char [] oldValueBuffer = valueBuffer;
911                                 valueBuffer = new char [valueCapacity];
912                                 Array.Copy (oldValueBuffer, valueBuffer, valueLength);
913                         }
914                 }
915
916                 private string CreateValueString ()
917                 {
918                         return new String (valueBuffer, 0, valueLength);
919                 }
920
921                 // The reader is positioned on the first character
922                 // of the text.
923                 private void ReadText ()
924                 {
925                         valueLength = 0;
926
927                         int ch = PeekChar ();
928
929                         while (ch != '<' && ch != -1) {
930                                 if (ch == '&') {
931                                         ReadChar ();
932                                         if (ReadReference (false))
933                                                 break;
934                                 } else
935                                         AppendValueChar (ReadChar ());
936
937                                 ch = PeekChar ();
938                         }
939
940                         if (returnEntityReference && valueLength == 0) {
941                                 ++depth;
942                                 SetEntityReferenceProperties ();
943                         } else {
944                                 if (depth >= 0) {
945                                         ++depth;
946                                         depthDown = true;
947                                 }
948
949                                 SetProperties (
950                                         XmlNodeType.Text, // nodeType
951                                         String.Empty, // name
952                                         false, // isEmptyElement
953                                         CreateValueString (), // value
954                                         true // clearAttributes
955                                 );
956                         }
957                 }
958
959                 // The leading '&' has already been consumed.
960                 // Returns true if the entity reference isn't a simple
961                 // character reference or one of the predefined entities.
962                 // This allows the ReadText method to break so that the
963                 // next call to Read will return the EntityReference node.
964                 private bool ReadReference (bool ignoreEntityReferences)
965                 {
966                         if (PeekChar () == '#') {
967                                 ReadChar ();
968                                 ReadCharacterReference ();
969                         } else
970                                 ReadEntityReference (ignoreEntityReferences);
971
972                         return returnEntityReference;
973                 }
974
975                 private void ReadCharacterReference ()
976                 {
977                         int value = 0;
978
979                         if (PeekChar () == 'x') {
980                                 ReadChar ();
981
982                                 while (PeekChar () != ';' && PeekChar () != -1) {
983                                         int ch = ReadChar ();
984
985                                         if (ch >= '0' && ch <= '9')
986                                                 value = (value << 4) + ch - '0';
987                                         else if (ch >= 'A' && ch <= 'F')
988                                                 value = (value << 4) + ch - 'A' + 10;
989                                         else if (ch >= 'a' && ch <= 'f')
990                                                 value = (value << 4) + ch - 'a' + 10;
991                                         else
992                                                 throw new XmlException (
993                                                         String.Format (
994                                                                 "invalid hexadecimal digit: {0} (#x{1:X})",
995                                                                 (char)ch,
996                                                                 ch));
997                                 }
998                         } else {
999                                 while (PeekChar () != ';' && PeekChar () != -1) {
1000                                         int ch = ReadChar ();
1001
1002                                         if (ch >= '0' && ch <= '9')
1003                                                 value = value * 10 + ch - '0';
1004                                         else
1005                                                 throw new XmlException (
1006                                                         String.Format (
1007                                                                 "invalid decimal digit: {0} (#x{1:X})",
1008                                                                 (char)ch,
1009                                                                 ch));
1010                                 }
1011                         }
1012
1013                         ReadChar (); // ';'
1014
1015                         AppendValueChar (value);
1016                 }
1017
1018                 private void ReadEntityReference (bool ignoreEntityReferences)
1019                 {
1020                         nameLength = 0;
1021
1022                         int ch = PeekChar ();
1023
1024                         while (ch != ';' && ch != -1) {
1025                                 AppendNameChar (ReadChar ());
1026                                 ch = PeekChar ();
1027                         }
1028
1029                         Expect (';');
1030
1031                         string name = CreateNameString ();
1032
1033                         switch (name)
1034                         {
1035                                 case "lt":
1036                                         AppendValueChar ('<');
1037                                         break;
1038                                 case "gt":
1039                                         AppendValueChar ('>');
1040                                         break;
1041                                 case "amp":
1042                                         AppendValueChar ('&');
1043                                         break;
1044                                 case "apos":
1045                                         AppendValueChar ('\'');
1046                                         break;
1047                                 case "quot":
1048                                         AppendValueChar ('"');
1049                                         break;
1050                                 default:
1051                                         if (ignoreEntityReferences) {
1052                                                 AppendValueChar ('&');
1053
1054                                                 foreach (char ch2 in name) {
1055                                                         AppendValueChar (ch2);
1056                                                 }
1057
1058                                                 AppendValueChar (';');
1059                                         } else {
1060                                                 returnEntityReference = true;
1061                                                 entityReferenceName = name;
1062                                         }
1063                                         break;
1064                         }
1065                 }
1066
1067                 // The reader is positioned on the first character of
1068                 // the attribute name.
1069                 private void ReadAttributes ()
1070                 {
1071                         do {
1072                                 string name = ReadName ();
1073                                 SkipWhitespace ();
1074                                 Expect ('=');
1075                                 SkipWhitespace ();
1076                                 string value = ReadAttribute ();
1077                                 SkipWhitespace ();
1078
1079                                 if (name == "xmlns")
1080                                         parserContext.NamespaceManager.AddNamespace (String.Empty, value);
1081                                 else if (name.StartsWith ("xmlns:"))
1082                                         parserContext.NamespaceManager.AddNamespace (name.Substring (6), value);
1083
1084                                 AddAttribute (name, value);
1085                         } while (PeekChar () != '/' && PeekChar () != '>' && PeekChar () != -1);
1086                 }
1087
1088                 // The reader is positioned on the quote character.
1089                 private string ReadAttribute ()
1090                 {
1091                         int quoteChar = ReadChar ();
1092
1093                         if (quoteChar != '\'' && quoteChar != '\"')
1094                                 throw new XmlException ("an attribute value was not quoted");
1095
1096                         valueLength = 0;
1097
1098                         while (PeekChar () != quoteChar) {
1099                                 int ch = ReadChar ();
1100
1101                                 switch (ch)
1102                                 {
1103                                 case '<':
1104                                         throw new XmlException ("attribute values cannot contain '<'");
1105                                 case '&':
1106                                         ReadReference (true);
1107                                         break;
1108                                 case -1:
1109                                         throw new XmlException ("unexpected end of file in an attribute value");
1110                                 default:
1111                                         AppendValueChar (ch);
1112                                         break;
1113                                 }
1114                         }
1115
1116                         ReadChar (); // quoteChar
1117
1118                         return CreateValueString ();
1119                 }
1120
1121                 // The reader is positioned on the first character
1122                 // of the target.
1123                 private void ReadProcessingInstruction ()
1124                 {
1125                         string target = ReadName ();
1126                         SkipWhitespace ();
1127
1128                         valueLength = 0;
1129
1130                         while (PeekChar () != -1) {
1131                                 int ch = ReadChar ();
1132
1133                                 if (ch == '?' && PeekChar () == '>') {
1134                                         ReadChar ();
1135                                         break;
1136                                 }
1137
1138                                 AppendValueChar ((char)ch);
1139                         }
1140
1141                         SetProperties (
1142                                 XmlNodeType.ProcessingInstruction, // nodeType
1143                                 target, // name
1144                                 false, // isEmptyElement
1145                                 CreateValueString (), // value
1146                                 true // clearAttributes
1147                         );
1148                 }
1149
1150                 // The reader is positioned on the first character after
1151                 // the leading '<!'.
1152                 private void ReadDeclaration ()
1153                 {
1154                         int ch = PeekChar ();
1155
1156                         switch (ch)
1157                         {
1158                         case '-':
1159                                 Expect ('-');
1160                                 Expect ('-');
1161                                 ReadComment ();
1162                                 break;
1163                         case '[':
1164                                 ReadChar ();
1165                                 Expect ('C');
1166                                 Expect ('D');
1167                                 Expect ('A');
1168                                 Expect ('T');
1169                                 Expect ('A');
1170                                 Expect ('[');
1171                                 ReadCDATA ();
1172                                 break;
1173                         }
1174                 }
1175
1176                 // The reader is positioned on the first character after
1177                 // the leading '<!--'.
1178                 private void ReadComment ()
1179                 {
1180                         valueLength = 0;
1181
1182                         while (PeekChar () != -1) {
1183                                 int ch = ReadChar ();
1184
1185                                 if (ch == '-' && PeekChar () == '-') {
1186                                         ReadChar ();
1187
1188                                         if (PeekChar () != '>')
1189                                                 throw new XmlException ("comments cannot contain '--'");
1190
1191                                         ReadChar ();
1192                                         break;
1193                                 }
1194
1195                                 AppendValueChar ((char)ch);
1196                         }
1197
1198                         SetProperties (
1199                                 XmlNodeType.Comment, // nodeType
1200                                 String.Empty, // name
1201                                 false, // isEmptyElement
1202                                 CreateValueString (), // value
1203                                 true // clearAttributes
1204                         );
1205                 }
1206
1207                 // The reader is positioned on the first character after
1208                 // the leading '<![CDATA['.
1209                 private void ReadCDATA ()
1210                 {
1211                         valueLength = 0;
1212
1213                         while (PeekChar () != -1) {
1214                                 int ch = ReadChar ();
1215
1216                                 if (ch == ']' && PeekChar () == ']') {
1217                                         ch = ReadChar (); // ']'
1218
1219                                         if (PeekChar () == '>') {
1220                                                 ReadChar (); // '>'
1221                                                 break;
1222                                         } else {
1223                                                 AppendValueChar (']');
1224                                                 AppendValueChar (']');
1225                                                 ch = ReadChar ();
1226                                         }
1227                                 }
1228
1229                                 AppendValueChar ((char)ch);
1230                         }
1231
1232                         ++depth;
1233
1234                         SetProperties (
1235                                 XmlNodeType.CDATA, // nodeType
1236                                 String.Empty, // name
1237                                 false, // isEmptyElement
1238                                 CreateValueString (), // value
1239                                 true // clearAttributes
1240                         );
1241                 }
1242
1243                 // The reader is positioned on the first character
1244                 // of the name.
1245                 private string ReadName ()
1246                 {
1247                         if (!XmlChar.IsFirstNameChar (PeekChar ()))
1248                                 throw new XmlException ("a name did not start with a legal character");
1249
1250                         nameLength = 0;
1251
1252                         AppendNameChar (ReadChar ());
1253
1254                         while (XmlChar.IsNameChar (PeekChar ())) {
1255                                 AppendNameChar (ReadChar ());
1256                         }
1257
1258                         return CreateNameString ();
1259                 }
1260
1261                 // Read the next character and compare it against the
1262                 // specified character.
1263                 private void Expect (int expected)
1264                 {
1265                         int ch = ReadChar ();
1266
1267                         if (ch != expected) {
1268                                 throw new XmlException (
1269                                         String.Format (
1270                                                 "expected '{0}' ({1:X}) but found '{2}' ({3:X})",
1271                                                 (char)expected,
1272                                                 expected,
1273                                                 (char)ch,
1274                                                 ch));
1275                         }
1276                 }
1277
1278                 // Does not consume the first non-whitespace character.
1279                 private void SkipWhitespace ()
1280                 {
1281                         while (XmlChar.IsWhitespace (PeekChar ()))
1282                                 ReadChar ();
1283                 }
1284         }
1285 }