cc736ed587036736571722c1214e2015de179640
[mono.git] / mcs / class / System.XML / System.Xml / XmlTextReader.cs
1 // -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*-\r
2 //\r
3 // System.Xml.XmlTextReader.cs\r
4 //\r
5 // Author:\r
6 //   Jason Diamond (jason@injektilo.org)\r
7 //\r
8 // (C) 2001 Jason Diamond  http://injektilo.org/\r
9 //\r
10 \r
11 // FIXME:\r
12 //   This can only parse basic XML: elements, attributes, processing\r
13 //   instructions, and comments are OK but there's no support for\r
14 //   entity/character references or namespaces yet.\r
15 //\r
16 //   It barfs on DOCTYPE declarations and CDATA sections.\r
17 //\r
18 //   There's also no checking being done for either well-formedness\r
19 //   or validity.\r
20 //\r
21 //   ParserContext and NameTables aren't being used yet.\r
22 //\r
23 //   The XmlTextReader-specific properties and methods have yet to\r
24 //   be added or implemented.\r
25 //\r
26 //   Some thought needs to be given to performance. There's too many\r
27 //   strings and string builders being allocated.\r
28 //\r
29 //   None of the MoveTo methods have been implemented yet.\r
30 //\r
31 //   LineNumber and LinePosition aren't being tracked.\r
32 //\r
33 //   xml:space, xml:lang, and xml:base aren't being tracked.\r
34 //\r
35 //   Depth isn't being tracked.\r
36 \r
37 using System;\r
38 using System.Collections;\r
39 using System.IO;\r
40 using System.Net;\r
41 using System.Text;\r
42 \r
43 namespace System.Xml\r
44 {\r
45         public class XmlTextReader : XmlReader\r
46         {\r
47                 // constructors\r
48 \r
49                 protected XmlTextReader()\r
50                 {\r
51                         Init();\r
52                 }\r
53 \r
54                 public XmlTextReader(Stream input)\r
55                 {\r
56                         Init();\r
57                         reader = new StreamReader(\r
58                                 input,\r
59                                 Encoding.UTF8,\r
60                                 true);\r
61                 }\r
62 \r
63                 public XmlTextReader(string url)\r
64                 {\r
65                         Init();\r
66                         WebClient client = new WebClient();\r
67                         reader = new StreamReader(\r
68                                 client.OpenRead(url),\r
69                                 Encoding.UTF8,\r
70                                 true);\r
71                 }\r
72 \r
73                 public XmlTextReader(TextReader input)\r
74                 {\r
75                         Init();\r
76                         reader = input;\r
77                 }\r
78 \r
79                 public XmlTextReader(Stream input, XmlNameTable nameTable)\r
80                 {\r
81                         // TODO: implement me.\r
82                         throw new NotImplementedException();\r
83                 }\r
84 \r
85                 public XmlTextReader(string baseURI, Stream input)\r
86                 {\r
87                         // TODO: implement me.\r
88                         throw new NotImplementedException();\r
89                 }\r
90 \r
91                 public XmlTextReader(string baseURI, TextReader input)\r
92                 {\r
93                         // TODO: implement me.\r
94                         throw new NotImplementedException();\r
95                 }\r
96 \r
97                 public XmlTextReader(string url, XmlNameTable nameTable)\r
98                 {\r
99                         // TODO: implement me.\r
100                         throw new NotImplementedException();\r
101                 }\r
102 \r
103                 public XmlTextReader(\r
104                         TextReader input,\r
105                         XmlNameTable nameTable)\r
106                 {\r
107                         // TODO: implement me.\r
108                         throw new NotImplementedException();\r
109                 }\r
110 \r
111                 public XmlTextReader(\r
112                         Stream inputFragment,\r
113                         XmlNodeType fragmentType,\r
114                         XmlParserContext context)\r
115                 {\r
116                         // TODO: implement me.\r
117                         throw new NotImplementedException();\r
118                 }\r
119 \r
120                 public XmlTextReader(\r
121                         string baseURI,\r
122                         Stream input,\r
123                         XmlNameTable nameTable)\r
124                 {\r
125                         // TODO: implement me.\r
126                         throw new NotImplementedException();\r
127                 }\r
128 \r
129                 public XmlTextReader(\r
130                         string baseURI,\r
131                         TextReader input,\r
132                         XmlNameTable nameTable)\r
133                 {\r
134                         // TODO: implement me.\r
135                         throw new NotImplementedException();\r
136                 }\r
137 \r
138                 public XmlTextReader(\r
139                         string fragment,\r
140                         XmlNodeType fragmentType,\r
141                         XmlParserContext context)\r
142                 {\r
143                         // TODO: implement me.\r
144                         throw new NotImplementedException();\r
145                 }\r
146 \r
147                 // properties\r
148 \r
149                 public override int AttributeCount\r
150                 {\r
151                         get\r
152                         {\r
153                                 return attributes.Count;\r
154                         }\r
155                 }\r
156 \r
157                 public override string BaseURI\r
158                 {\r
159                         get\r
160                         {\r
161                                 // TODO: implement me.\r
162                                 return null;\r
163                         }\r
164                 }\r
165 \r
166                 public override bool CanResolveEntity\r
167                 {\r
168                         get\r
169                         {\r
170                                 // TODO: implement me.\r
171                                 return false;\r
172                         }\r
173                 }\r
174 \r
175                 public override int Depth\r
176                 {\r
177                         get\r
178                         {\r
179                                 // TODO: implement me.\r
180                                 return 0;\r
181                         }\r
182                 }\r
183 \r
184                 public override bool EOF\r
185                 {\r
186                         get\r
187                         {\r
188                                 return\r
189                                         readState == ReadState.EndOfFile ||\r
190                                         readState == ReadState.Closed;\r
191                         }\r
192                 }\r
193 \r
194                 public override bool HasValue\r
195                 {\r
196                         get\r
197                         {\r
198                                 return value != String.Empty;\r
199                         }\r
200                 }\r
201 \r
202                 public override bool IsDefault\r
203                 {\r
204                         get\r
205                         {\r
206                                 // TODO: implement me.\r
207                                 return false;\r
208                         }\r
209                 }\r
210 \r
211                 public override bool IsEmptyElement\r
212                 {\r
213                         get\r
214                         {\r
215                                 return isEmptyElement;\r
216                         }\r
217                 }\r
218 \r
219                 public override string this[int i]\r
220                 {\r
221                         get\r
222                         {\r
223                                 return GetAttribute(i);\r
224                         }\r
225                 }\r
226 \r
227                 public override string this[string name]\r
228                 {\r
229                         get\r
230                         {\r
231                                 return GetAttribute(name);\r
232                         }\r
233                 }\r
234 \r
235                 public override string this[\r
236                         string localName,\r
237                         string namespaceName]\r
238                 {\r
239                         get\r
240                         {\r
241                                 return GetAttribute(localName, namespaceName);\r
242                         }\r
243                 }\r
244 \r
245                 public override string LocalName\r
246                 {\r
247                         get\r
248                         {\r
249                                 // TODO: implement me.\r
250                                 return null;\r
251                         }\r
252                 }\r
253 \r
254                 public override string Name\r
255                 {\r
256                         get\r
257                         {\r
258                                 return name;\r
259                         }\r
260                 }\r
261 \r
262                 public override string NamespaceURI\r
263                 {\r
264                         get\r
265                         {\r
266                                 // TODO: implement me.\r
267                                 return null;\r
268                         }\r
269                 }\r
270 \r
271                 public override XmlNameTable NameTable\r
272                 {\r
273                         get\r
274                         {\r
275                                 // TODO: implement me.\r
276                                 return null;\r
277                         }\r
278                 }\r
279 \r
280                 public override XmlNodeType NodeType\r
281                 {\r
282                         get\r
283                         {\r
284                                 return nodeType;\r
285                         }\r
286                 }\r
287 \r
288                 public override string Prefix\r
289                 {\r
290                         get\r
291                         {\r
292                                 // TODO: implement me.\r
293                                 return null;\r
294                         }\r
295                 }\r
296 \r
297                 public override char QuoteChar\r
298                 {\r
299                         get\r
300                         {\r
301                                 // TODO: implement me.\r
302                                 return '"';\r
303                         }\r
304                 }\r
305 \r
306                 public override ReadState ReadState\r
307                 {\r
308                         get\r
309                         {\r
310                                 return readState;\r
311                         }\r
312                 }\r
313 \r
314                 public override string Value\r
315                 {\r
316                         get\r
317                         {\r
318                                 return value;\r
319                         }\r
320                 }\r
321 \r
322                 public override string XmlLang\r
323                 {\r
324                         get\r
325                         {\r
326                                 // TODO: implement me.\r
327                                 return null;\r
328                         }\r
329                 }\r
330 \r
331                 public override XmlSpace XmlSpace\r
332                 {\r
333                         get\r
334                         {\r
335                                 // TODO: implement me.\r
336                                 return XmlSpace.Default;\r
337                         }\r
338                 }\r
339 \r
340                 // methods\r
341 \r
342                 public override void Close()\r
343                 {\r
344                         readState = ReadState.Closed;\r
345                 }\r
346 \r
347                 public override string GetAttribute(int i)\r
348                 {\r
349                         // TODO: implement me.\r
350                         return null;\r
351                 }\r
352 \r
353                 public override string GetAttribute(string name)\r
354                 {\r
355                         return (string)attributes[name];\r
356                 }\r
357 \r
358                 public override string GetAttribute(\r
359                         string localName,\r
360                         string namespaceName)\r
361                 {\r
362                         // TODO: implement me.\r
363                         return null;\r
364                 }\r
365 \r
366                 public override string LookupNamespace(string prefix)\r
367                 {\r
368                         // TODO: implement me.\r
369                         return null;\r
370                 }\r
371 \r
372                 public override void MoveToAttribute(int i)\r
373                 {\r
374                         // TODO: implement me.\r
375                 }\r
376 \r
377                 public override bool MoveToAttribute(string name)\r
378                 {\r
379                         // TODO: implement me.\r
380                         return false;\r
381                 }\r
382 \r
383                 public override bool MoveToAttribute(\r
384                         string localName,\r
385                         string namespaceName)\r
386                 {\r
387                         // TODO: implement me.\r
388                         return false;\r
389                 }\r
390 \r
391                 public override bool MoveToElement()\r
392                 {\r
393                         // TODO: implement me.\r
394                         return false;\r
395                 }\r
396 \r
397                 public override bool MoveToFirstAttribute()\r
398                 {\r
399                         // TODO: implement me.\r
400                         return false;\r
401                 }\r
402 \r
403                 public override bool MoveToNextAttribute()\r
404                 {\r
405                         // TODO: implement me.\r
406                         return false;\r
407                 }\r
408 \r
409                 public override bool Read()\r
410                 {\r
411                         bool more = false;\r
412 \r
413                         readState = ReadState.Interactive;\r
414 \r
415                         more = ReadContent();\r
416 \r
417                         return more;\r
418                 }\r
419 \r
420                 public override bool ReadAttributeValue()\r
421                 {\r
422                         // TODO: implement me.\r
423                         return false;\r
424                 }\r
425 \r
426                 public override string ReadInnerXml()\r
427                 {\r
428                         // TODO: implement me.\r
429                         return null;\r
430                 }\r
431 \r
432                 public override string ReadOuterXml()\r
433                 {\r
434                         // TODO: implement me.\r
435                         return null;\r
436                 }\r
437 \r
438                 public override string ReadString()\r
439                 {\r
440                         // TODO: implement me.\r
441                         return null;\r
442                 }\r
443 \r
444                 public override void ResolveEntity()\r
445                 {\r
446                         // TODO: implement me.\r
447                 }\r
448 \r
449                 // privates\r
450 \r
451                 private TextReader reader;\r
452                 private ReadState readState;\r
453 \r
454                 private XmlNodeType nodeType;\r
455                 private string name;\r
456                 private bool isEmptyElement;\r
457                 private string value;\r
458                 private Hashtable attributes;\r
459 \r
460                 private void Init()\r
461                 {\r
462                         readState = ReadState.Initial;\r
463 \r
464                         nodeType = XmlNodeType.None;\r
465                         name = String.Empty;\r
466                         isEmptyElement = false;\r
467                         value = String.Empty;\r
468                         attributes = new Hashtable();\r
469                 }\r
470 \r
471                 // Use this method rather than setting the properties\r
472                 // directly so that all the necessary properties can\r
473                 // be changed in harmony with each other. Maybe the\r
474                 // fields should be in a seperate class to help enforce\r
475                 // this.\r
476                 private void SetProperties(\r
477                         XmlNodeType nodeType,\r
478                         string name,\r
479                         bool isEmptyElement,\r
480                         string value,\r
481                         bool clearAttributes)\r
482                 {\r
483                         this.nodeType = nodeType;\r
484                         this.name = name;\r
485                         this.isEmptyElement = isEmptyElement;\r
486                         this.value = value;\r
487 \r
488                         if (clearAttributes)\r
489                         {\r
490                                 ClearAttributes();\r
491                         }\r
492                 }\r
493 \r
494                 private void AddAttribute(string name, string value)\r
495                 {\r
496                         attributes.Add(name, value);\r
497                 }\r
498 \r
499                 private void ClearAttributes()\r
500                 {\r
501                         attributes.Clear();\r
502                 }\r
503 \r
504                 // This should really keep track of some state so\r
505                 // that it's not possible to have more than one document\r
506                 // element or text outside of the document element.\r
507                 private bool ReadContent()\r
508                 {\r
509                         bool more = false;\r
510 \r
511                         switch (reader.Peek())\r
512                         {\r
513                         case '<':\r
514                                 reader.Read();\r
515                                 ReadTag();\r
516                                 more = true;\r
517                                 break;\r
518                         case -1:\r
519                                 readState = ReadState.EndOfFile;\r
520                                 SetProperties(\r
521                                         XmlNodeType.None, // nodeType\r
522                                         String.Empty, // name\r
523                                         false, // isEmptyElement\r
524                                         String.Empty, // value\r
525                                         true // clearAttributes\r
526                                 );\r
527                                 more = false;\r
528                                 break;\r
529                         default:\r
530                                 ReadText();\r
531                                 more = true;\r
532                                 break;\r
533                         }\r
534 \r
535                         return more;\r
536                 }\r
537 \r
538                 // The leading '<' has already been consumed.\r
539                 private void ReadTag()\r
540                 {\r
541                         switch (reader.Peek())\r
542                         {\r
543                         case '/':\r
544                                 reader.Read();\r
545                                 ReadEndTag();\r
546                                 break;\r
547                         case '?':\r
548                                 reader.Read();\r
549                                 ReadProcessingInstruction();\r
550                                 break;\r
551                         case '!':\r
552                                 reader.Read();\r
553                                 ReadComment();\r
554                                 break;\r
555                         default:\r
556                                 ReadStartTag();\r
557                                 break;\r
558                         }\r
559                 }\r
560 \r
561                 // The leading '<' has already been consumed.\r
562                 private void ReadStartTag()\r
563                 {\r
564                         string name = ReadName();\r
565                         SkipWhitespace();\r
566 \r
567                         bool isEmptyElement = false;\r
568 \r
569                         ClearAttributes();\r
570 \r
571                         if (XmlChar.IsFirstNameChar(reader.Peek()))\r
572                         {\r
573                                 ReadAttributes();\r
574                         }\r
575 \r
576                         if (reader.Peek() == '/')\r
577                         {\r
578                                 reader.Read();\r
579                                 isEmptyElement = true;\r
580                         }\r
581 \r
582                         Expect('>');\r
583 \r
584                         SetProperties(\r
585                                 XmlNodeType.Element, // nodeType\r
586                                 name, // name\r
587                                 isEmptyElement, // isEmptyElement\r
588                                 String.Empty, // value\r
589                                 false // clearAttributes\r
590                         );\r
591                 }\r
592 \r
593                 // The reader is positioned on the first character\r
594                 // of the element's name.\r
595                 private void ReadEndTag()\r
596                 {\r
597                         string name = ReadName();\r
598                         SkipWhitespace();\r
599                         Expect('>');\r
600 \r
601                         SetProperties(\r
602                                 XmlNodeType.EndElement, // nodeType\r
603                                 name, // name\r
604                                 false, // isEmptyElement\r
605                                 String.Empty, // value\r
606                                 true // clearAttributes\r
607                         );\r
608                 }\r
609 \r
610                 // The reader is positioned on the first character\r
611                 // of the text.\r
612                 private void ReadText()\r
613                 {\r
614                         StringBuilder text = new StringBuilder();\r
615                         text.Append((char)reader.Read());\r
616 \r
617                         while (reader.Peek() != '<' && reader.Peek() != -1)\r
618                         {\r
619                                 text.Append((char)reader.Read());\r
620                         }\r
621 \r
622                         SetProperties(\r
623                                 XmlNodeType.Text, // nodeType\r
624                                 String.Empty, // name\r
625                                 false, // isEmptyElement\r
626                                 text.ToString(), // value\r
627                                 true // clearAttributes\r
628                         );\r
629                 }\r
630 \r
631                 // The reader is positioned on the first character of\r
632                 // the attribute name.\r
633                 private void ReadAttributes()\r
634                 {\r
635                         do\r
636                         {\r
637                                 string name = ReadName();\r
638                                 SkipWhitespace();\r
639                                 Expect('=');\r
640                                 SkipWhitespace();\r
641                                 string value = ReadAttribute();\r
642                                 SkipWhitespace();\r
643 \r
644                                 AddAttribute(name, value);\r
645                         }\r
646                         while (reader.Peek() != '/' && reader.Peek() != '>' && reader.Peek() != -1);\r
647                 }\r
648 \r
649                 // The reader is positioned on the quote character.\r
650                 private string ReadAttribute()\r
651                 {\r
652                         int quoteChar = reader.Read();\r
653 \r
654                         if (quoteChar != '\'' && quoteChar != '\"')\r
655                         {\r
656                                 throw new Exception("an attribute value was not quoted");\r
657                         }\r
658 \r
659                         StringBuilder valueBuilder = new StringBuilder();\r
660 \r
661                         while (reader.Peek() != quoteChar)\r
662                         {\r
663                                 int ch = reader.Read();\r
664 \r
665                                 switch (ch)\r
666                                 {\r
667                                 case '<':\r
668                                         throw new Exception("attribute values cannot contain '<'");\r
669                                 case -1:\r
670                                         throw new Exception("unexpected end of file in an attribute value");\r
671                                 }\r
672 \r
673                                 valueBuilder.Append((char)ch);\r
674                         }\r
675 \r
676                         reader.Read();\r
677 \r
678                         return valueBuilder.ToString();\r
679                 }\r
680 \r
681                 // The reader is positioned on the first character\r
682                 // of the target.\r
683                 private void ReadProcessingInstruction()\r
684                 {\r
685                         string target = ReadName();\r
686                         SkipWhitespace();\r
687 \r
688                         StringBuilder valueBuilder = new StringBuilder();\r
689 \r
690                         while (reader.Peek() != -1)\r
691                         {\r
692                                 int ch = reader.Read();\r
693 \r
694                                 if (ch == '?' && reader.Peek() == '>')\r
695                                 {\r
696                                         reader.Read();\r
697                                         break;\r
698                                 }\r
699 \r
700                                 valueBuilder.Append((char)ch);\r
701                         }\r
702 \r
703                         SetProperties(\r
704                                 XmlNodeType.ProcessingInstruction, // nodeType\r
705                                 target, // name\r
706                                 false, // isEmptyElement\r
707                                 valueBuilder.ToString(), // value\r
708                                 true // clearAttributes\r
709                         );\r
710                 }\r
711 \r
712                 // The reader is positioned on the first character after\r
713                 // the leading '<!'.\r
714                 private void ReadComment()\r
715                 {\r
716                         Expect('-');\r
717                         Expect('-');\r
718 \r
719                         StringBuilder valueBuilder = new StringBuilder();\r
720 \r
721                         while (reader.Peek() != -1)\r
722                         {\r
723                                 int ch = reader.Read();\r
724 \r
725                                 if (ch == '-' && reader.Peek() == '-')\r
726                                 {\r
727                                         reader.Read();\r
728 \r
729                                         if (reader.Peek() != '>')\r
730                                         {\r
731                                                 throw new Exception("comments cannot contain '--'");\r
732                                         }\r
733 \r
734                                         reader.Read();\r
735                                         break;\r
736                                 }\r
737 \r
738                                 valueBuilder.Append((char)ch);\r
739                         }\r
740 \r
741                         SetProperties(\r
742                                 XmlNodeType.Comment, // nodeType\r
743                                 String.Empty, // name\r
744                                 false, // isEmptyElement\r
745                                 valueBuilder.ToString(), // value\r
746                                 true // clearAttributes\r
747                         );\r
748                 }\r
749 \r
750                 // The reader is positioned on the first character\r
751                 // of the name.\r
752                 private string ReadName()\r
753                 {\r
754                         if (!XmlChar.IsFirstNameChar(reader.Peek()))\r
755                         {\r
756                                 throw new Exception("a name did not start with a legal character");\r
757                         }\r
758 \r
759                         StringBuilder nameBuilder = new StringBuilder();\r
760 \r
761                         nameBuilder.Append((char)reader.Read());\r
762 \r
763                         while (XmlChar.IsNameChar(reader.Peek()))\r
764                         {\r
765                                 nameBuilder.Append((char)reader.Read());\r
766                         }\r
767 \r
768                         return nameBuilder.ToString();\r
769                 }\r
770 \r
771                 // Read the next character and compare it against the\r
772                 // specified character.\r
773                 private void Expect(int expected)\r
774                 {\r
775                         int ch = reader.Read();\r
776 \r
777                         if (ch != expected)\r
778                         {\r
779                                 throw new Exception(String.Format(\r
780                                         "expected '{0}' ({1:X}) but found '{2}' ({3:X})",\r
781                                         (char)expected,\r
782                                         expected,\r
783                                         (char)ch,\r
784                                         ch));\r
785                         }\r
786                 }\r
787 \r
788                 // Does not consume the first non-whitespace character.\r
789                 private void SkipWhitespace()\r
790                 {\r
791                         while (XmlChar.IsWhitespace(reader.Peek()))\r
792                         {\r
793                                 reader.Read();\r
794                         }\r
795                 }\r
796         }\r
797 }\r