2005-12-05 Lluis Sanchez Gual <lluis@novell.com>
[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                 XmlSchemaSet 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 XmlSchemaSet ();
293                                 return schemas.SchemaCollection;
294                         }
295                 }
296
297                 internal void SetSchemas (XmlSchemaSet schemas)
298                 {
299                         this.schemas = schemas;
300                 }
301
302                 public object SchemaType {
303                         get { return schemaInfo.SchemaType; }
304                 }
305
306 #if NET_2_0
307                 [MonoTODO]
308                 public override XmlReaderSettings Settings {
309                         get { return validatingReader == null ? sourceReader.Settings : validatingReader.Settings; }
310                 }
311 #endif
312
313                 [MonoTODO ("We decided not to support XDR schema that spec is obsolete.")]
314                 public ValidationType ValidationType {
315                         get { return validationType; }
316                         set {
317                                 if (ReadState != ReadState.Initial)
318                                         throw new InvalidOperationException ("ValidationType cannot be set after the first call to Read method.");
319                                 switch (validationType) {
320                                 case ValidationType.Auto:
321                                 case ValidationType.DTD:
322                                 case ValidationType.None:
323                                 case ValidationType.Schema:
324                                         validationType = value; 
325                                         break;
326                                 case ValidationType.XDR:
327                                         throw new NotSupportedException ();
328                                 }
329                         }
330                 }
331
332                 public override string Value {
333                         get { return validatingReader == null ? String.Empty : validatingReader.Value; }
334                 }
335
336                 public override string XmlLang {
337                         get { return validatingReader == null ? String.Empty : validatingReader.XmlLang; }
338                 }
339
340                 public XmlResolver XmlResolver {
341                         set {
342                                 resolverSpecified = true;
343                                 resolver = value;
344                                 if (xmlTextReader != null)
345                                         xmlTextReader.XmlResolver = value;
346
347                                 XsdValidatingReader xsvr = validatingReader as XsdValidatingReader;
348                                 if (xsvr != null)
349                                         xsvr.XmlResolver = value;
350                                 DTDValidatingReader dvr = validatingReader as DTDValidatingReader;
351                                 if (dvr != null)
352                                         dvr.XmlResolver = value;
353                         }
354                 }
355
356                 public override XmlSpace XmlSpace {
357                         get { return validatingReader == null ? XmlSpace.None : validatingReader.XmlSpace; }
358                 }
359
360                 #endregion // Properties
361
362                 #region Methods
363
364                 public override void Close ()
365                 {
366                         if (validatingReader == null)
367                                 sourceReader.Close ();
368                         else
369                                 validatingReader.Close ();
370                 }
371
372                 public override string GetAttribute (int i)
373                 {
374                         if (validatingReader == null)
375                                 throw new IndexOutOfRangeException ("Reader is not started.");
376                         return validatingReader [i];
377                 }
378
379                 public override string GetAttribute (string name)
380                 {
381                         return validatingReader == null ? null : validatingReader [name];
382                 }
383
384                 public override string GetAttribute (string localName, string namespaceName)
385                 {
386                         return validatingReader == null ? null : validatingReader [localName, namespaceName];
387                 }
388
389                 XmlParserContext IHasXmlParserContext.ParserContext {
390                         get { return dtdReader != null ? dtdReader.ParserContext : null; }
391                 }
392
393 #if NET_2_0
394                 IDictionary<string, string> IXmlNamespaceResolver.GetNamespacesInScope (XmlNamespaceScope scope)
395                 {
396                         return ((IHasXmlParserContext) this).ParserContext.NamespaceManager.GetNamespacesInScope (scope);
397                 }
398 #endif
399
400 #if NET_2_0
401                 public bool HasLineInfo ()
402 #else
403                 bool IXmlLineInfo.HasLineInfo ()
404 #endif
405                 {
406                         IXmlLineInfo info = validatingReader as IXmlLineInfo;
407                         return info != null ? info.HasLineInfo () : false;
408                 }
409
410                 public override string LookupNamespace (string prefix)
411                 {
412                         if (validatingReader != null)
413                                 return validatingReader.LookupNamespace (prefix);
414                         else
415                                 return sourceReader.LookupNamespace (prefix);
416                 }
417
418 #if NET_2_0
419                 string IXmlNamespaceResolver.LookupPrefix (string ns)
420                 {
421                         IXmlNamespaceResolver res = null;
422                         if (validatingReader != null)
423                                 res = sourceReader as IXmlNamespaceResolver;
424                         else
425                                 res = validatingReader as IXmlNamespaceResolver;
426                         return res != null ?
427                                 res.LookupNamespace (ns) :
428                                 null;
429                 }
430 #endif
431
432
433                 public override void MoveToAttribute (int i)
434                 {
435                         if (validatingReader == null)
436                                 throw new IndexOutOfRangeException ("Reader is not started.");
437                         else
438                                 validatingReader.MoveToAttribute (i);
439                 }
440
441                 public override bool MoveToAttribute (string name)
442                 {
443                         if (validatingReader == null)
444                                 return false;
445                         return validatingReader.MoveToAttribute (name);
446                 }
447
448                 public override bool MoveToAttribute (string localName, string namespaceName)
449                 {
450                         if (validatingReader == null)
451                                 return false;
452                         return validatingReader.MoveToAttribute (localName, namespaceName);
453                 }
454
455                 public override bool MoveToElement ()
456                 {
457                         if (validatingReader == null)
458                                 return false;
459                         return validatingReader.MoveToElement ();
460                 }
461
462                 public override bool MoveToFirstAttribute ()
463                 {
464                         if (validatingReader == null)
465                                 return false;
466                         return validatingReader.MoveToFirstAttribute ();
467                 }
468
469                 public override bool MoveToNextAttribute ()
470                 {
471                         if (validatingReader == null)
472                                 return false;
473                         return validatingReader.MoveToNextAttribute ();
474                 }
475
476                 [MonoTODO ("We decided not to support XDR schema that spec is obsolete.")]
477                 public override bool Read ()
478                 {
479                         if (ReadState == ReadState.Initial) {
480                                 switch (ValidationType) {
481                                 case ValidationType.Auto:
482                                 case ValidationType.None:
483                                         goto case ValidationType.Schema; // might be specified by xsi:schemaLocation.
484                                 case ValidationType.DTD:
485                                         validatingReader = dtdReader = new DTDValidatingReader (sourceReader, this);
486                                         dtdReader.XmlResolver = Resolver;
487                                         break;
488                                 case ValidationType.Schema:
489                                         dtdReader = new DTDValidatingReader (sourceReader, this);
490                                         XsdValidatingReader xsvr = new XsdValidatingReader (dtdReader);
491                                         xsvr.ValidationEventHandler +=
492                                                 new ValidationEventHandler (
493                                                         OnValidationEvent);
494                                         xsvr.ValidationType = ValidationType;
495                                         xsvr.Schemas = Schemas.SchemaSet;
496                                         xsvr.XmlResolver = Resolver;
497                                         validatingReader = xsvr;
498                                         dtdReader.XmlResolver = Resolver;
499                                         break;
500                                 case ValidationType.XDR:
501                                         throw new NotSupportedException ();
502                                 }
503                                 schemaInfo = validatingReader as IHasXmlSchemaInfo;
504                         }
505                         return validatingReader.Read ();
506                 }
507
508                 public override bool ReadAttributeValue ()
509                 {
510                         if (validatingReader == null)
511                                 return false;
512                         return validatingReader.ReadAttributeValue ();
513                 }
514
515 #if NET_1_0
516                 // LAMESPEC: MS.NET 1.0 has critical bug here.
517                 // After calling these methods, validation does not work anymore!
518                 public override string ReadInnerXml ()
519                 {
520                         if (validatingReader == null)
521                                 return "";
522                         return validatingReader.ReadInnerXml ();
523                 }
524
525                 public override string ReadOuterXml ()
526                 {
527                         if (validatingReader == null)
528                                 return "";
529                         return validatingReader.ReadOuterXml ();
530                 }
531 #endif
532
533 #if NET_1_0
534                 public override string ReadString ()
535                 {
536                         return base.ReadStringInternal ();
537                 }
538 #else
539                 public override string ReadString ()
540                 {
541                         return base.ReadString ();
542                 }
543 #endif
544
545                 public object ReadTypedValue ()
546                 {
547                         if (dtdReader == null)
548                                 return null;
549                         XmlSchemaDatatype dt = schemaInfo.SchemaType as XmlSchemaDatatype;
550                         if (dt == null)
551                                 return null;
552                         switch (NodeType) {
553                         case XmlNodeType.Element:
554                                 if (IsEmptyElement)
555                                         return null;
556
557                                 storedCharacters.Length = 0;
558                                 bool loop = true;
559                                 do {
560                                         Read ();
561                                         switch (NodeType) {
562                                         case XmlNodeType.SignificantWhitespace:
563                                         case XmlNodeType.Text:
564                                         case XmlNodeType.CDATA:
565                                                 storedCharacters.Append (Value);
566                                                 break;
567                                         case XmlNodeType.Comment:
568                                                 break;
569                                         default:
570                                                 loop = false;
571                                                 break;
572                                         }
573                                 } while (loop && !EOF);
574                                 return dt.ParseValue (storedCharacters.ToString (), NameTable, dtdReader.ParserContext.NamespaceManager);
575                         case XmlNodeType.Attribute:
576                                 return dt.ParseValue (Value, NameTable, dtdReader.ParserContext.NamespaceManager);
577                         }
578                         return null;
579                 }
580
581                 public override void ResolveEntity ()
582                 {
583                         validatingReader.ResolveEntity ();
584                 }
585
586                 // It should be "protected" as usual "event model"
587                 // methods are, but validation event is not exposed,
588                 // so it is no other way to make it "internal".
589                 internal void OnValidationEvent (object o, ValidationEventArgs e)
590                 {
591                         if (ValidationEventHandler != null)
592                                 ValidationEventHandler (o, e);
593                         else if (ValidationType != ValidationType.None && e.Severity == XmlSeverityType.Error)
594                                 throw e.Exception;
595                 }
596
597 #if NET_2_0
598                 [MonoTODO ("Check how expanded entity is handled here.")]
599                 public override int ReadContentAsBase64 (byte [] buffer, int offset, int length)
600                 {
601                         if (validatingReader != null)
602                                 return validatingReader.ReadContentAsBase64 (buffer, offset, length);
603                         else
604                                 return sourceReader.ReadContentAsBase64 (buffer, offset, length);
605                 }
606
607                 [MonoTODO ("Check how expanded entity is handled here.")]
608                 public override int ReadContentAsBinHex (byte [] buffer, int offset, int length)
609                 {
610                         if (validatingReader != null)
611                                 return validatingReader.ReadContentAsBinHex (buffer, offset, length);
612                         else
613                                 return sourceReader.ReadContentAsBinHex (buffer, offset, length);
614                 }
615
616                 [MonoTODO ("Check how expanded entity is handled here.")]
617                 public override int ReadElementContentAsBase64 (byte [] buffer, int offset, int length)
618                 {
619                         if (validatingReader != null)
620                                 return validatingReader.ReadElementContentAsBase64 (buffer, offset, length);
621                         else
622                                 return sourceReader.ReadElementContentAsBase64 (buffer, offset, length);
623                 }
624
625                 [MonoTODO ("Check how expanded entity is handled here.")]
626                 public override int ReadElementContentAsBinHex (byte [] buffer, int offset, int length)
627                 {
628                         if (validatingReader != null)
629                                 return validatingReader.ReadElementContentAsBinHex (buffer, offset, length);
630                         else
631                                 return sourceReader.ReadElementContentAsBinHex (buffer, offset, length);
632                 }
633 #endif
634                 #endregion // Methods
635
636                 #region Events and Delegates
637
638                 public event ValidationEventHandler ValidationEventHandler;
639
640                 #endregion // Events and Delegates
641         }
642 }