Merge pull request #347 from JamesB7/master
[mono.git] / mcs / class / System.XML / System.Xml / XmlValidatingReader.cs
1 //
2 // System.Xml.XmlValidatingReader.cs
3 //
4 // Author:
5 //   Tim Coleman (tim@timcoleman.com)
6 //   Atsushi Enomoto (ginga@kit.hi-ho.ne.jp)
7 //
8 // Copyright (C) Tim Coleman, 2002
9 // (C)2003 Atsushi Enomoto
10 // Copyright (C) 2005 Novell, Inc (http://www.novell.com)
11 //
12 // Permission is hereby granted, free of charge, to any person obtaining
13 // a copy of this software and associated documentation files (the
14 // "Software"), to deal in the Software without restriction, including
15 // without limitation the rights to use, copy, modify, merge, publish,
16 // distribute, sublicense, and/or sell copies of the Software, and to
17 // permit persons to whom the Software is furnished to do so, subject to
18 // the following conditions:
19 // 
20 // The above copyright notice and this permission notice shall be
21 // included in all copies or substantial portions of the Software.
22 // 
23 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
24 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
25 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
26 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
27 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
28 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
29 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
30 //
31
32 #if NET_2_0
33 using System.Collections.Generic;
34 #endif
35 using System.IO;
36 using System.Security.Permissions;
37 using System.Text;
38 using System.Xml.Schema;
39 using Mono.Xml;
40 using Mono.Xml.Schema;
41
42 namespace System.Xml
43 {
44         [PermissionSet (SecurityAction.InheritanceDemand, Unrestricted = true)]
45 #if NET_2_0
46         [Obsolete("Use XmlReader created by XmlReader.Create() method using"
47                 + " appropriate XmlReaderSettings instead.")]
48         public class XmlValidatingReader : XmlReader, IXmlLineInfo, IXmlNamespaceResolver, IHasXmlParserContext
49 #else
50         public class XmlValidatingReader : XmlReader, IXmlLineInfo, IHasXmlParserContext
51 #endif
52         {
53
54                 #region Fields
55
56                 EntityHandling entityHandling;
57                 XmlReader sourceReader;
58                 XmlTextReader xmlTextReader;
59                 XmlReader validatingReader;
60                 XmlResolver resolver; // Only used to non-XmlTextReader XmlReader
61                 bool resolverSpecified;
62                 ValidationType validationType;
63                 // for 2.0: Now it is obsolete. It is allocated only when it is required
64                 XmlSchemaCollection schemas;
65                 DTDValidatingReader dtdReader;
66                 IHasXmlSchemaInfo schemaInfo;
67                 StringBuilder storedCharacters;
68
69                 #endregion // Fields
70
71                 #region Constructors
72
73                 public XmlValidatingReader (XmlReader reader)
74                 {
75                         sourceReader = reader;
76                         xmlTextReader = reader as XmlTextReader;
77                         if (xmlTextReader == null)
78                                 resolver = new XmlUrlResolver ();
79                         entityHandling = EntityHandling.ExpandEntities;
80                         validationType = ValidationType.Auto;
81                         storedCharacters = new StringBuilder ();
82                 }
83
84                 public XmlValidatingReader (Stream xmlFragment, XmlNodeType fragType, XmlParserContext context)
85                         : this (new XmlTextReader (xmlFragment, fragType, context))
86                 {
87                 }
88
89                 public XmlValidatingReader (string xmlFragment, XmlNodeType fragType, XmlParserContext context)
90                         : this (new XmlTextReader (xmlFragment, fragType, context))
91                 {
92                 }
93
94                 #endregion // Constructors
95
96                 #region Properties
97
98                 public override int AttributeCount {
99                         get { return validatingReader == null ? 0 : validatingReader.AttributeCount; }
100                 }
101
102                 public override string BaseURI {
103                         get { return validatingReader == null ? sourceReader.BaseURI : validatingReader.BaseURI; }
104                 }
105
106 #if NET_2_0
107                 public override bool CanReadBinaryContent {
108                         get { return true; }
109                 }
110 #endif
111
112                 // This property for this class always return true.
113                 public override bool CanResolveEntity {
114                         get { return true; }
115                 }
116
117                 public override int Depth { 
118                         get { return validatingReader == null ? 0 : validatingReader.Depth; }
119                 }
120
121                 public Encoding Encoding {
122                         get {
123                                 if (xmlTextReader != null)
124                                         return xmlTextReader.Encoding;
125                                 else
126                                         throw new NotSupportedException ("Encoding is supported only for XmlTextReader.");
127                         }
128                 }
129
130                 public EntityHandling EntityHandling {
131                         get { return entityHandling; }
132                         set {
133                                 entityHandling = value;
134                                 if (dtdReader != null)
135                                         dtdReader.EntityHandling = value;
136                         }
137                 }
138
139                 public override bool EOF { 
140                         get { return validatingReader == null ? false : validatingReader.EOF; }
141                 }
142
143 #if DTD_HANDLE_EVENTS
144                 internal bool HasValidationEvent {
145                         get { return ValidationEventHandler != null; }
146                 }
147 #endif
148
149                 public override bool HasValue { 
150                         get { return validatingReader == null ? false : validatingReader.HasValue; }
151                 }
152
153                 public override bool IsDefault {
154                         get { return validatingReader == null ? false : validatingReader.IsDefault; }
155                 }
156
157                 public override bool IsEmptyElement { 
158                         get { return validatingReader == null ? false : validatingReader.IsEmptyElement; }
159                 }
160
161 #if NET_2_0
162 #else
163                 public override string this [int i] { 
164                         get { return GetAttribute (i); }
165                 }
166
167                 public override string this [string name] { 
168                         get { return GetAttribute (name); }
169                 }
170
171                 public override string this [string localName, string namespaceName] { 
172                         get { return GetAttribute (localName, namespaceName); }
173                 }
174 #endif
175
176 #if NET_2_0
177                 public int LineNumber {
178 #else
179                 int IXmlLineInfo.LineNumber {
180 #endif
181                         get {
182                                 if (IsDefault)
183                                         return 0;
184                                 IXmlLineInfo info = validatingReader as IXmlLineInfo;
185                                 return info != null ? info.LineNumber : 0;
186                         }
187                 }
188
189 #if NET_2_0
190                 public int LinePosition {
191 #else
192                 int IXmlLineInfo.LinePosition {
193 #endif
194                         get {
195                                 if (IsDefault)
196                                         return 0;
197                                 IXmlLineInfo info = validatingReader as IXmlLineInfo;
198                                 return info != null ? info.LinePosition : 0;
199                         }
200                 }
201
202                 public override string LocalName { 
203                         get {
204                                 if (validatingReader == null)
205                                         return String.Empty;
206                                 else if (Namespaces)
207                                         return validatingReader.LocalName;
208                                 else
209                                         return validatingReader.Name;
210                         }
211                 }
212
213                 public override string Name {
214                         get { return validatingReader == null ? String.Empty : validatingReader.Name; }
215                 }
216
217                 public bool Namespaces {
218                         get {
219                                 if (xmlTextReader != null)
220                                         return xmlTextReader.Namespaces;
221                                 else
222                                         return true;
223                         }
224                         set {
225                                 if (ReadState != ReadState.Initial)
226                                         throw new InvalidOperationException ("Namespaces have to be set before reading.");
227
228                                 if (xmlTextReader != null)
229                                         xmlTextReader.Namespaces = value;
230                                 else
231                                         throw new NotSupportedException ("Property 'Namespaces' is supported only for XmlTextReader.");
232                         }
233                 }
234
235                 public override string NamespaceURI { 
236                         get {
237                                 if (validatingReader == null)
238                                         return String.Empty;
239                                 else if (Namespaces)
240                                         return validatingReader.NamespaceURI;
241                                 else
242                                         return String.Empty;
243                         }
244                 }
245
246                 public override XmlNameTable NameTable { 
247                         get { return validatingReader == null ? sourceReader.NameTable : validatingReader.NameTable; }
248                 }
249
250                 public override XmlNodeType NodeType { 
251                         get { return validatingReader == null ? XmlNodeType.None : validatingReader.NodeType; }
252                 }
253
254                 public override string Prefix {
255                         get { return validatingReader == null ? String.Empty : validatingReader.Prefix; }
256                 }
257
258                 public override char QuoteChar { 
259                         get { return validatingReader == null ? sourceReader.QuoteChar : validatingReader.QuoteChar; }
260                 }
261
262                 public XmlReader Reader {
263                         get { return sourceReader; }
264                 }
265
266                 public override ReadState ReadState { 
267                         get {
268                                 if (validatingReader == null)
269                                         return ReadState.Initial;
270                                 return validatingReader.ReadState; 
271                         }
272                 }
273
274                 internal XmlResolver Resolver {
275                         get {
276                                 // This is special rule... MS.NET shares the
277                                 // XmlResolver between XmlTextReader and
278                                 // XmlValidatingReader, so we mimick that
279                                 // silly behavior here.
280                                 if (this.xmlTextReader != null)
281                                         return this.xmlTextReader.Resolver;
282                                 else if (resolverSpecified)
283                                         return resolver;
284                                 else
285                                         return null;
286                         }
287                 }
288
289                 public XmlSchemaCollection Schemas {
290                         get {
291                                 if (schemas == null)
292                                         schemas = new XmlSchemaCollection (NameTable);
293                                 return schemas;
294                         }
295                 }
296
297                 public object SchemaType {
298                         get { return schemaInfo.SchemaType; }
299                 }
300
301 #if NET_2_0
302                 [MonoTODO]
303                 public override XmlReaderSettings Settings {
304                         get { return validatingReader == null ? sourceReader.Settings : validatingReader.Settings; }
305                 }
306 #endif
307
308                 [MonoTODO]
309                 // We decided not to support XDR schema; it is obsolete.
310                 public ValidationType ValidationType {
311                         get { return validationType; }
312                         set {
313                                 if (ReadState != ReadState.Initial)
314                                         throw new InvalidOperationException ("ValidationType cannot be set after the first call to Read method.");
315                                 switch (validationType) {
316                                 case ValidationType.Auto:
317                                 case ValidationType.DTD:
318                                 case ValidationType.None:
319                                 case ValidationType.Schema:
320                                         validationType = value; 
321                                         break;
322                                 case ValidationType.XDR:
323                                         throw new NotSupportedException ();
324                                 }
325                         }
326                 }
327
328                 public override string Value {
329                         get { return validatingReader == null ? String.Empty : validatingReader.Value; }
330                 }
331
332                 public override string XmlLang {
333                         get { return validatingReader == null ? String.Empty : validatingReader.XmlLang; }
334                 }
335
336                 public XmlResolver XmlResolver {
337                         set {
338                                 resolverSpecified = true;
339                                 resolver = value;
340                                 if (xmlTextReader != null)
341                                         xmlTextReader.XmlResolver = value;
342
343                                 XsdValidatingReader xsvr = validatingReader as XsdValidatingReader;
344                                 if (xsvr != null)
345                                         xsvr.XmlResolver = value;
346                                 DTDValidatingReader dvr = validatingReader as DTDValidatingReader;
347                                 if (dvr != null)
348                                         dvr.XmlResolver = value;
349                         }
350                 }
351
352                 public override XmlSpace XmlSpace {
353                         get { return validatingReader == null ? XmlSpace.None : validatingReader.XmlSpace; }
354                 }
355
356                 #endregion // Properties
357
358                 #region Methods
359
360                 public override void Close ()
361                 {
362                         if (validatingReader == null)
363                                 sourceReader.Close ();
364                         else
365                                 validatingReader.Close ();
366                 }
367
368                 public override string GetAttribute (int i)
369                 {
370                         if (validatingReader == null)
371                                 throw new IndexOutOfRangeException ("Reader is not started.");
372                         return validatingReader [i];
373                 }
374
375                 public override string GetAttribute (string name)
376                 {
377                         return validatingReader == null ? null : validatingReader [name];
378                 }
379
380                 public override string GetAttribute (string localName, string namespaceURI)
381                 {
382                         return validatingReader == null ? null : validatingReader [localName, namespaceURI];
383                 }
384
385                 XmlParserContext IHasXmlParserContext.ParserContext {
386                         get {
387                                 if (dtdReader != null)
388                                         return dtdReader.ParserContext;
389                                 IHasXmlParserContext i = sourceReader as IHasXmlParserContext;
390                                 return i != null ? i.ParserContext : null;
391                         }
392                 }
393
394 #if NET_2_0
395                 IDictionary<string, string> IXmlNamespaceResolver.GetNamespacesInScope (XmlNamespaceScope scope)
396                 {
397                         return ((IHasXmlParserContext) this).ParserContext.NamespaceManager.GetNamespacesInScope (scope);
398                 }
399 #endif
400
401 #if NET_2_0
402                 public bool HasLineInfo ()
403 #else
404                 bool IXmlLineInfo.HasLineInfo ()
405 #endif
406                 {
407                         IXmlLineInfo info = validatingReader as IXmlLineInfo;
408                         return info != null ? info.HasLineInfo () : false;
409                 }
410
411                 public override string LookupNamespace (string prefix)
412                 {
413                         if (validatingReader != null)
414                                 return validatingReader.LookupNamespace (prefix);
415                         else
416                                 return sourceReader.LookupNamespace (prefix);
417                 }
418
419 #if NET_2_0
420                 string IXmlNamespaceResolver.LookupPrefix (string ns)
421                 {
422                         IXmlNamespaceResolver res = null;
423                         if (validatingReader != null)
424                                 res = sourceReader as IXmlNamespaceResolver;
425                         else
426                                 res = validatingReader as IXmlNamespaceResolver;
427                         return res != null ?
428                                 res.LookupNamespace (ns) :
429                                 null;
430                 }
431 #endif
432
433
434                 public override void MoveToAttribute (int i)
435                 {
436                         if (validatingReader == null)
437                                 throw new IndexOutOfRangeException ("Reader is not started.");
438                         else
439                                 validatingReader.MoveToAttribute (i);
440                 }
441
442                 public override bool MoveToAttribute (string name)
443                 {
444                         if (validatingReader == null)
445                                 return false;
446                         return validatingReader.MoveToAttribute (name);
447                 }
448
449                 public override bool MoveToAttribute (string localName, string namespaceURI)
450                 {
451                         if (validatingReader == null)
452                                 return false;
453                         return validatingReader.MoveToAttribute (localName, namespaceURI);
454                 }
455
456                 public override bool MoveToElement ()
457                 {
458                         if (validatingReader == null)
459                                 return false;
460                         return validatingReader.MoveToElement ();
461                 }
462
463                 public override bool MoveToFirstAttribute ()
464                 {
465                         if (validatingReader == null)
466                                 return false;
467                         return validatingReader.MoveToFirstAttribute ();
468                 }
469
470                 public override bool MoveToNextAttribute ()
471                 {
472                         if (validatingReader == null)
473                                 return false;
474                         return validatingReader.MoveToNextAttribute ();
475                 }
476
477                 [MonoTODO]
478                 // We decided not to support XDR schema; it is obsolete.
479                 public override bool Read ()
480                 {
481                         if (validatingReader == null) {
482                                 switch (ValidationType) {
483                                 case ValidationType.Auto:
484                                 case ValidationType.None:
485                                         goto case ValidationType.Schema; // might be specified by xsi:schemaLocation.
486                                 case ValidationType.DTD:
487                                         validatingReader = dtdReader = new DTDValidatingReader (sourceReader, this);
488                                         dtdReader.XmlResolver = Resolver;
489                                         break;
490                                 case ValidationType.Schema:
491                                         dtdReader = new DTDValidatingReader (sourceReader, this);
492                                         XsdValidatingReader xsvr = new XsdValidatingReader (dtdReader);
493                                         xsvr.ValidationEventHandler +=
494                                                 new ValidationEventHandler (
495                                                         OnValidationEvent);
496                                         xsvr.ValidationType = ValidationType;
497                                         xsvr.Schemas = Schemas.SchemaSet;
498                                         xsvr.XmlResolver = Resolver;
499                                         validatingReader = xsvr;
500                                         dtdReader.XmlResolver = Resolver;
501                                         break;
502                                 case ValidationType.XDR:
503                                         throw new NotSupportedException ();
504                                 }
505                                 schemaInfo = validatingReader as IHasXmlSchemaInfo;
506                         }
507                         return validatingReader.Read ();
508                 }
509
510                 public override bool ReadAttributeValue ()
511                 {
512                         if (validatingReader == null)
513                                 return false;
514                         return validatingReader.ReadAttributeValue ();
515                 }
516
517                 public override string ReadString ()
518                 {
519                         return base.ReadString ();
520                 }
521
522                 public object ReadTypedValue ()
523                 {
524                         if (dtdReader == null)
525                                 return null;
526                         XmlSchemaDatatype dt = schemaInfo.SchemaType as XmlSchemaDatatype;
527                         if (dt == null) {
528                                 XmlSchemaType st = schemaInfo.SchemaType as XmlSchemaType;
529                                 if (st != null)
530                                         dt = st.Datatype;
531                         }
532                         if (dt == null)
533                                 return null;
534                         switch (NodeType) {
535                         case XmlNodeType.Element:
536                                 if (IsEmptyElement)
537                                         return null;
538
539                                 storedCharacters.Length = 0;
540                                 bool loop = true;
541                                 do {
542                                         Read ();
543                                         switch (NodeType) {
544                                         case XmlNodeType.Whitespace:
545                                         case XmlNodeType.SignificantWhitespace:
546                                         case XmlNodeType.Text:
547                                         case XmlNodeType.CDATA:
548                                                 storedCharacters.Append (Value);
549                                                 break;
550                                         case XmlNodeType.Comment:
551                                                 break;
552                                         default:
553                                                 loop = false;
554                                                 break;
555                                         }
556                                 } while (loop && !EOF);
557                                 return dt.ParseValue (storedCharacters.ToString (), NameTable, dtdReader.ParserContext.NamespaceManager);
558                         case XmlNodeType.Attribute:
559                                 return dt.ParseValue (Value, NameTable, dtdReader.ParserContext.NamespaceManager);
560                         }
561                         return null;
562                 }
563
564                 public override void ResolveEntity ()
565                 {
566                         validatingReader.ResolveEntity ();
567                 }
568
569                 // It should be "protected" as usual "event model"
570                 // methods are, but validation event is not exposed,
571                 // so it is no other way to make it "internal".
572                 internal void OnValidationEvent (object o, ValidationEventArgs e)
573                 {
574                         if (ValidationEventHandler != null)
575                                 ValidationEventHandler (o, e);
576                         else if (ValidationType != ValidationType.None && e.Severity == XmlSeverityType.Error)
577                                 throw e.Exception;
578                 }
579
580 #if NET_2_0
581                 [MonoTODO] // FIXME: Check how expanded entity is handled here.
582                 public override int ReadContentAsBase64 (byte [] buffer, int index, int count)
583                 {
584                         if (validatingReader != null)
585                                 return validatingReader.ReadContentAsBase64 (buffer, index, count);
586                         else
587                                 return sourceReader.ReadContentAsBase64 (buffer, index, count);
588                 }
589
590                 [MonoTODO] // FIXME: Check how expanded entity is handled here.
591                 public override int ReadContentAsBinHex (byte [] buffer, int index, int count)
592                 {
593                         if (validatingReader != null)
594                                 return validatingReader.ReadContentAsBinHex (buffer, index, count);
595                         else
596                                 return sourceReader.ReadContentAsBinHex (buffer, index, count);
597                 }
598
599                 [MonoTODO] // FIXME: Check how expanded entity is handled here.
600                 public override int ReadElementContentAsBase64 (byte [] buffer, int index, int count)
601                 {
602                         if (validatingReader != null)
603                                 return validatingReader.ReadElementContentAsBase64 (buffer, index, count);
604                         else
605                                 return sourceReader.ReadElementContentAsBase64 (buffer, index, count);
606                 }
607
608                 [MonoTODO] // FIXME: Check how expanded entity is handled here.
609                 public override int ReadElementContentAsBinHex (byte [] buffer, int index, int count)
610                 {
611                         if (validatingReader != null)
612                                 return validatingReader.ReadElementContentAsBinHex (buffer, index, count);
613                         else
614                                 return sourceReader.ReadElementContentAsBinHex (buffer, index, count);
615                 }
616 #endif
617                 #endregion // Methods
618
619                 #region Events and Delegates
620
621                 public event ValidationEventHandler ValidationEventHandler;
622
623                 #endregion // Events and Delegates
624         }
625 }