Merge pull request #1322 from StephenMcConnel/bug23532
[mono.git] / mcs / class / System.XML / System.Xml / XmlTextReader2.cs
1 //
2 // System.Xml.XmlTextReader2.cs - XmlTextReader for .NET 2.0
3 //
4 // Author:
5 //   Atsushi Enomoto  (ginga@kit.hi-ho.ne.jp)
6 //
7 // Copyright (C) 2004-2005 Novell, Inc (http://www.novell.com)
8 //
9 // Permission is hereby granted, free of charge, to any person obtaining
10 // a copy of this software and associated documentation files (the
11 // "Software"), to deal in the Software without restriction, including
12 // without limitation the rights to use, copy, modify, merge, publish,
13 // distribute, sublicense, and/or sell copies of the Software, and to
14 // permit persons to whom the Software is furnished to do so, subject to
15 // the following conditions:
16 // 
17 // The above copyright notice and this permission notice shall be
18 // included in all copies or substantial portions of the Software.
19 // 
20 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
21 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
22 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
23 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
24 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
25 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
26 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
27 //
28
29 #if NET_2_0
30
31 using XmlTextReaderImpl = Mono.Xml2.XmlTextReader;
32
33 using System.Collections.Generic;
34 using System.Globalization;
35 using System.IO;
36 using System.Security.Permissions;
37 using System.Text;
38 using System.Xml.Schema;
39 using Mono.Xml;
40
41 namespace System.Xml
42 {
43         // FIXME: this implementation requires somewhat significant change
44         // to expand entities and merge sequential text and entity references
45         // especially to handle whitespace-only entities (such as bug #372839).
46         //
47         // To do it, we have to read ahead the next node when the input is
48         // text, whitespace or significant whitespace and check if the next
49         // node is EntityReference. If it is entref, then it have to merge
50         // the input entity if it is a text.
51         //
52         // This "read ahead" operation may result in proceeding to the next
53         // element, which badly affects IXmlNamespaceResolverimplementation.
54         // So we cannot fix this in simple way.
55
56         [PermissionSet (SecurityAction.InheritanceDemand, Unrestricted = true)]
57         public class XmlTextReader : XmlReader,
58                 IXmlLineInfo, IXmlNamespaceResolver, IHasXmlParserContext
59         {
60                 XmlTextReader entity;
61                 XmlTextReaderImpl source; // dtd2xsd expects this field's existence.
62                 bool entityInsideAttribute;
63                 bool insideAttribute;
64                 Stack<string> entityNameStack;
65
66                 protected XmlTextReader ()
67                 {
68                 }
69
70                 public XmlTextReader (Stream input)
71                         : this (new XmlStreamReader (input))
72                 {
73                 }
74
75                 public XmlTextReader (string url)
76                         : this(url, new NameTable ())
77                 {
78                 }
79
80                 public XmlTextReader (TextReader input)
81                         : this (input, new NameTable ())
82                 {
83                 }
84
85                 protected XmlTextReader (XmlNameTable nt)
86                         : this (String.Empty, XmlNodeType.Element, null)
87                 {
88                 }
89
90                 public XmlTextReader (Stream input, XmlNameTable nt)
91                         : this(new XmlStreamReader (input), nt)
92                 {
93                 }
94
95                 public XmlTextReader (string url, Stream input)
96                         : this (url, new XmlStreamReader (input))
97                 {
98                 }
99
100                 public XmlTextReader (string url, TextReader input)
101                         : this (url, input, new NameTable ())
102                 {
103                 }
104
105                 public XmlTextReader (string url, XmlNameTable nt)
106                 {
107                         source = new XmlTextReaderImpl (url, nt);
108                 }
109
110                 public XmlTextReader (TextReader input, XmlNameTable nt)
111                         : this (String.Empty, input, nt)
112                 {
113                 }
114
115                 public XmlTextReader (Stream xmlFragment, XmlNodeType fragType, XmlParserContext context)
116                 {
117                         source = new XmlTextReaderImpl (xmlFragment, fragType, context);
118                 }
119
120                 public XmlTextReader (string url, Stream input, XmlNameTable nt)
121                         : this (url, new XmlStreamReader (input), nt)
122                 {
123                 }
124
125                 public XmlTextReader (string url, TextReader input, XmlNameTable nt)
126                 {
127                         source = new XmlTextReaderImpl (url, input, nt);
128                 }
129
130                 public XmlTextReader (string xmlFragment, XmlNodeType fragType, XmlParserContext context)
131                 {
132                         source = new XmlTextReaderImpl (xmlFragment, fragType, context);
133                 }
134
135                 internal XmlTextReader (string baseURI, TextReader xmlFragment, XmlNodeType fragType)
136                 {
137                         source = new XmlTextReaderImpl (baseURI, xmlFragment, fragType);
138                 }
139
140                 internal XmlTextReader (string baseURI, TextReader xmlFragment, XmlNodeType fragType, XmlParserContext context)
141                 {
142                         source = new XmlTextReaderImpl (baseURI, xmlFragment, fragType, context);
143                 }
144
145                 internal XmlTextReader (bool dummy, XmlResolver resolver, string url, XmlNodeType fragType, XmlParserContext context)
146                 {
147                         source = new XmlTextReaderImpl (dummy, resolver, url, fragType, context);
148                 }
149
150                 private XmlTextReader (XmlTextReaderImpl entityContainer, bool insideAttribute)
151                 {
152                         source = entityContainer;
153                         this.entityInsideAttribute = insideAttribute;
154                 }
155
156                 #region Properties
157
158                 private XmlReader Current {
159                         get { return entity != null && entity.ReadState != ReadState.Initial ? (XmlReader) entity : source; }
160                 }
161
162                 public override int AttributeCount {
163                         get { return Current.AttributeCount; }
164                 }
165
166                 public override string BaseURI {
167                         get { return Current.BaseURI; }
168                 }
169
170                 public override bool CanReadBinaryContent {
171                         get { return true; }
172                 }
173
174                 public override bool CanReadValueChunk {
175                         get { return true; }
176                 }
177
178                 public override bool CanResolveEntity {
179                         get { return true; }
180                 }
181
182                 public override int Depth {
183                         get {
184                                 // On EndEntity, depth is the same as that 
185                                 // of EntityReference.
186                                 if (entity != null && entity.ReadState == ReadState.Interactive)
187                                         return source.Depth + entity.Depth + 1;
188                                 else
189                                         return source.Depth;
190                         }
191                 }
192
193                 public override bool EOF {
194                         get { return source.EOF; }
195                 }
196
197                 public override bool HasValue {
198                         get { return Current.HasValue; }
199                 }
200
201                 public override bool IsDefault {
202                         get { return Current.IsDefault; }
203                 }
204
205                 public override bool IsEmptyElement {
206                         get { return Current.IsEmptyElement; }
207                 }
208
209                 public override string LocalName {
210                         get { return Current.LocalName; }
211                 }
212
213                 public override string Name {
214                         get { return Current.Name; }
215                 }
216
217                 public override string NamespaceURI {
218                         get { return Current.NamespaceURI; }
219                 }
220
221                 public override XmlNameTable NameTable {
222                         get { return Current.NameTable; }
223                 }
224
225                 public override XmlNodeType NodeType {
226                         get {
227                                 if (entity != null)
228                                         return entity.ReadState == ReadState.Initial ?
229                                                 source.NodeType :
230                                                 entity.EOF ? XmlNodeType.EndEntity :
231                                                 entity.NodeType;
232                                 else
233                                         return source.NodeType;
234                         }
235                 }
236
237                 internal XmlParserContext ParserContext {
238                         get { return ((IHasXmlParserContext) Current).ParserContext; }
239                 }
240
241                 XmlParserContext IHasXmlParserContext.ParserContext {
242                         get { return this.ParserContext; }
243                 }
244
245                 public override string Prefix {
246                         get { return Current.Prefix; }
247                 }
248
249                 public override char QuoteChar {
250                         get { return Current.QuoteChar; }
251                 }
252
253                 public override ReadState ReadState {
254                         get { return entity != null ? ReadState.Interactive : source.ReadState; }
255                 }
256
257 #if NET_4_0
258                 [MonoTODO]
259                 public DtdProcessing DtdProcessing { get; set; }
260 #endif
261
262 #if !NET_4_5
263                 public override XmlReaderSettings Settings {
264                         get { return base.Settings; }
265                 }
266 #endif
267
268                 public override string Value {
269                         get { return Current.Value; }
270                 }
271
272                 public override string XmlLang {
273                         get { return Current.XmlLang; }
274                 }
275
276                 public override XmlSpace XmlSpace {
277                         get { return Current.XmlSpace; }
278                 }
279
280                 // non-overrides
281
282                 internal bool CharacterChecking {
283                         get {
284                                 if (entity != null)
285                                         return entity.CharacterChecking;
286                                 else
287                                         return source.CharacterChecking;
288                         }
289                         set {
290                                 if (entity != null)
291                                         entity.CharacterChecking = value;
292                                 source.CharacterChecking = value;
293                         }
294                 }
295
296                 internal bool CloseInput {
297                         get {
298                                 if (entity != null)
299                                         return entity.CloseInput;
300                                 else
301                                         return source.CloseInput;
302                         }
303                         set {
304                                 if (entity != null)
305                                         entity.CloseInput = value;
306                                 source.CloseInput = value;
307                         }
308                 }
309
310                 internal ConformanceLevel Conformance {
311                         get { return source.Conformance; }
312                         set {
313                                 if (entity != null)
314                                         entity.Conformance = value;
315                                 source.Conformance = value;
316                         }
317                 }
318
319                 internal XmlResolver Resolver {
320                         get { return source.Resolver; }
321                 }
322
323                 private void CopyProperties (XmlTextReader other)
324                 {
325                         CharacterChecking = other.CharacterChecking;
326                         CloseInput = other.CloseInput;
327                         if (other.Settings != null)
328                                 Conformance = other.Settings.ConformanceLevel;
329                         XmlResolver = other.Resolver;
330                 }
331
332                 // public members
333
334                 public Encoding Encoding {
335                         get {
336                                 if (entity != null)
337                                         return entity.Encoding;
338                                 else
339                                         return source.Encoding;
340                         }
341                 }
342
343                 public EntityHandling EntityHandling {
344                         get { return source.EntityHandling; }
345                         set {
346                                 if (entity != null)
347                                         entity.EntityHandling = value;
348                                 source.EntityHandling = value;
349                         }
350                 }
351
352                 public int LineNumber {
353                         get {
354                                 if (entity != null)
355                                         return entity.LineNumber;
356                                 else
357                                         return source.LineNumber;
358                         }
359                 }
360
361                 public int LinePosition {
362                         get {
363                                 if (entity != null)
364                                         return entity.LinePosition;
365                                 else
366                                         return source.LinePosition;
367                         }
368                 }
369
370                 public bool Namespaces {
371                         get { return source.Namespaces; }
372                         set {
373                                 if (entity != null)
374                                         entity.Namespaces = value;
375                                 source.Namespaces = value;
376                         }
377                 }
378
379                 public bool Normalization {
380                         get { return source.Normalization; }
381                         set {
382                                 if (entity != null)
383                                         entity.Normalization = value;
384                                 source.Normalization = value;
385                         }
386                 }
387
388                 public bool ProhibitDtd {
389                         get { return source.ProhibitDtd; }
390                         set {
391                                 if (entity != null)
392                                         entity.ProhibitDtd = value;
393                                 source.ProhibitDtd = value;
394                         }
395                 }
396
397                 public WhitespaceHandling WhitespaceHandling {
398                         get { return source.WhitespaceHandling; }
399                         set {
400                                 if (entity != null)
401                                         entity.WhitespaceHandling = value;
402                                 source.WhitespaceHandling = value;
403                         }
404                 }
405
406                 public XmlResolver XmlResolver {
407                         set {
408                                 if (entity != null)
409                                         entity.XmlResolver = value;
410                                 source.XmlResolver = value;
411                         }
412                 }
413
414                 #endregion
415
416                 #region Methods
417
418                 internal void AdjustLineInfoOffset (int lineNumberOffset, int linePositionOffset)
419                 {
420                         if (entity != null)
421                                 entity.AdjustLineInfoOffset (lineNumberOffset, linePositionOffset);
422                         source.AdjustLineInfoOffset (lineNumberOffset, linePositionOffset);
423                 }
424
425                 internal void SetNameTable (XmlNameTable nameTable)
426                 {
427                         if (entity != null)
428                                 entity.SetNameTable (nameTable);
429                         source.SetNameTable (nameTable);
430                 }
431
432                 internal void SkipTextDeclaration ()
433                 {
434                         if (entity != null)
435                                 entity.SkipTextDeclaration ();
436                         else
437                                 source.SkipTextDeclaration ();
438                 }
439
440                 // overrides
441
442                 public override void Close ()
443                 {
444                         if (entity != null)
445                                 entity.Close ();
446                         source.Close ();
447                 }
448
449                 public override string GetAttribute (int i)
450                 {
451                         return Current.GetAttribute (i);
452                 }
453
454                 // MS.NET 1.0 msdn says that this method returns String.Empty
455                 // for absent attribute, but in fact it returns null.
456                 // This description is corrected in MS.NET 1.1 msdn.
457                 public override string GetAttribute (string name)
458                 {
459                         return Current.GetAttribute (name);
460                 }
461
462                 public override string GetAttribute (string localName, string namespaceURI)
463                 {
464                         return Current.GetAttribute (localName, namespaceURI);
465                 }
466
467                 public IDictionary<string, string> GetNamespacesInScope (XmlNamespaceScope scope)
468                 {
469                         return ((IXmlNamespaceResolver) Current).GetNamespacesInScope (scope);
470                 }
471
472                 IDictionary<string, string> IXmlNamespaceResolver.GetNamespacesInScope (XmlNamespaceScope scope)
473                 {
474                         return GetNamespacesInScope (scope);
475                 }
476
477                 public override string LookupNamespace (string prefix)
478                 {
479                         return Current.LookupNamespace (prefix);
480                 }
481
482                 string IXmlNamespaceResolver.LookupPrefix (string ns)
483                 {
484                         return ((IXmlNamespaceResolver) Current).LookupPrefix (ns);
485                 }
486
487                 public override void MoveToAttribute (int i)
488                 {
489                         if (entity != null && entityInsideAttribute)
490                                 CloseEntity ();
491                         Current.MoveToAttribute (i);
492                         insideAttribute = true;
493                 }
494
495                 public override bool MoveToAttribute (string name)
496                 {
497                         if (entity != null && !entityInsideAttribute)
498                                 return entity.MoveToAttribute (name);
499                         if (!source.MoveToAttribute (name))
500                                 return false;
501                         if (entity != null && entityInsideAttribute)
502                                 CloseEntity ();
503                         insideAttribute = true;
504                         return true;
505                 }
506
507                 public override bool MoveToAttribute (string localName, string namespaceURI)
508                 {
509                         if (entity != null && !entityInsideAttribute)
510                                 return entity.MoveToAttribute (localName, namespaceURI);
511                         if (!source.MoveToAttribute (localName, namespaceURI))
512                                 return false;
513                         if (entity != null && entityInsideAttribute)
514                                 CloseEntity ();
515                         insideAttribute = true;
516                         return true;
517                 }
518
519                 public override bool MoveToElement ()
520                 {
521                         if (entity != null && entityInsideAttribute)
522                                 CloseEntity ();
523                         if (!Current.MoveToElement ())
524                                 return false;
525                         insideAttribute = false;
526                         return true;
527                 }
528
529                 public override bool MoveToFirstAttribute ()
530                 {
531                         if (entity != null && !entityInsideAttribute)
532                                 return entity.MoveToFirstAttribute ();
533                         if (!source.MoveToFirstAttribute ())
534                                 return false;
535                         if (entity != null && entityInsideAttribute)
536                                 CloseEntity ();
537                         insideAttribute = true;
538                         return true;
539                 }
540
541                 public override bool MoveToNextAttribute ()
542                 {
543                         if (entity != null && !entityInsideAttribute)
544                                 return entity.MoveToNextAttribute ();
545                         if (!source.MoveToNextAttribute ())
546                                 return false;
547                         if (entity != null && entityInsideAttribute)
548                                 CloseEntity ();
549                         insideAttribute = true;
550                         return true;
551                 }
552
553                 public override bool Read ()
554                 {
555                         insideAttribute = false;
556
557                         if (entity != null && (entityInsideAttribute || entity.EOF))
558                                 CloseEntity ();
559                         if (entity != null) {
560                                 if (entity.Read ())
561                                         return true;
562                                 if (EntityHandling == EntityHandling.ExpandEntities) {
563                                         // EndEntity must be skipped
564                                         CloseEntity ();
565                                         return Read ();
566                                 }
567                                 else
568                                         return true; // either success or EndEntity
569                         }
570                         else {
571                                 if (!source.Read ())
572                                         return false;
573                                 if (EntityHandling == EntityHandling.ExpandEntities
574                                         && source.NodeType == XmlNodeType.EntityReference) {
575                                         ResolveEntity ();
576                                         return Read ();
577                                 }
578                                 return true;
579                         }
580                 }
581
582                 public override bool ReadAttributeValue ()
583                 {
584                         if (entity != null && entityInsideAttribute) {
585                                 if (entity.EOF)
586                                         CloseEntity ();
587                                 else {
588                                         entity.Read ();
589                                         return true; // either success or EndEntity
590                                 }
591                         }
592                         return Current.ReadAttributeValue ();
593                 }
594
595                 public override string ReadString ()
596                 {
597                         return base.ReadString ();
598                 }
599
600                 public void ResetState ()
601                 {
602                         if (entity != null)
603                                 CloseEntity ();
604                         source.ResetState ();
605                 }
606
607                 public override
608                 void ResolveEntity ()
609                 {
610                         if (entity != null)
611                                 entity.ResolveEntity ();
612                         else {
613                                 if (source.NodeType != XmlNodeType.EntityReference)
614                                         throw new InvalidOperationException ("The current node is not an Entity Reference");
615                                 XmlTextReaderImpl entReader = null;
616                                 if (ParserContext.Dtd != null)
617                                         entReader = ParserContext.Dtd.GenerateEntityContentReader (source.Name, ParserContext);
618                                 if (entReader == null)
619                                         throw new XmlException (this as IXmlLineInfo, this.BaseURI, String.Format ("Reference to undeclared entity '{0}'.", source.Name));
620                                 if (entityNameStack == null)
621                                         entityNameStack = new Stack<string> ();
622                                 else if (entityNameStack.Contains (Name))
623                                         throw new XmlException (String.Format ("General entity '{0}' has an invalid recursive reference to itself.", Name));
624                                 entityNameStack.Push (Name);
625                                 entity = new XmlTextReader (
626                                         entReader, insideAttribute);
627                                 entity.entityNameStack = entityNameStack;
628                                 entity.CopyProperties (this);
629                         }
630                 }
631
632                 void CloseEntity ()
633                 {
634                         entity.Close ();
635                         entity = null;
636                         entityNameStack.Pop ();
637                 }
638
639                 public override void Skip ()
640                 {
641                         base.Skip ();
642                 }
643
644                 [MonoTODO] // FIXME: Check how expanded entity is handled here.
645                 public TextReader GetRemainder ()
646                 {
647                         if (entity != null) {
648                                 entity.Close ();
649                                 entity = null;
650                                 entityNameStack.Pop ();
651                         }
652                         return source.GetRemainder ();
653                 }
654
655                 public bool HasLineInfo ()
656                 {
657                         return true;
658                 }
659
660                 [MonoTODO] // FIXME: Check how expanded entity is handled here.
661                 public int ReadBase64 (byte [] array, int offset, int len)
662                 {
663                         if (entity != null)
664                                 return entity.ReadBase64 (array, offset, len);
665                         else
666                                 return source.ReadBase64 (array, offset, len);
667                 }
668
669                 [MonoTODO] // FIXME: Check how expanded entity is handled here.
670                 public int ReadBinHex (byte [] array, int offset, int len)
671                 {
672                         if (entity != null)
673                                 return entity.ReadBinHex (array, offset, len);
674                         else
675                                 return source.ReadBinHex (array, offset, len);
676                 }
677
678                 [MonoTODO] // FIXME: Check how expanded entity is handled here.
679                 public int ReadChars (char [] buffer, int index, int count)
680                 {
681                         if (entity != null)
682                                 return entity.ReadChars (buffer, index, count);
683                         else
684                                 return source.ReadChars (buffer, index, count);
685                 }
686
687
688                 [MonoTODO] // FIXME: Check how expanded entity is handled here.
689                 public override int ReadContentAsBase64 (byte [] buffer, int index, int count)
690                 {
691                         if (entity != null)
692                                 return entity.ReadContentAsBase64 (buffer, index, count);
693                         else
694                                 return source.ReadContentAsBase64 (buffer, index, count);
695                 }
696
697                 [MonoTODO] // FIXME: Check how expanded entity is handled here.
698                 public override int ReadContentAsBinHex (byte [] buffer, int index, int count)
699                 {
700                         if (entity != null)
701                                 return entity.ReadContentAsBinHex (buffer, index, count);
702                         else
703                                 return source.ReadContentAsBinHex (buffer, index, count);
704                 }
705
706                 [MonoTODO] // FIXME: Check how expanded entity is handled here.
707                 public override int ReadElementContentAsBase64 (byte [] buffer, int index, int count)
708                 {
709                         if (entity != null)
710                                 return entity.ReadElementContentAsBase64 (buffer, index, count);
711                         else
712                                 return source.ReadElementContentAsBase64 (buffer, index, count);
713                 }
714
715                 [MonoTODO] // FIXME: Check how expanded entity is handled here.
716                 public override int ReadElementContentAsBinHex (byte [] buffer, int index, int count)
717                 {
718                         if (entity != null)
719                                 return entity.ReadElementContentAsBinHex (buffer, index, count);
720                         else
721                                 return source.ReadElementContentAsBinHex (buffer, index, count);
722                 }
723                 #endregion
724         }
725 }
726
727 #endif