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