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