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