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