svn path=/branches/mono-1-1-9/mcs/; revision=51207
[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                 // This property for this class always return true.
107                 public override bool CanResolveEntity {
108                         get { return true; }
109                 }
110
111                 public override int Depth { 
112                         get { return validatingReader == null ? 0 : validatingReader.Depth; }
113                 }
114
115                 public Encoding Encoding {
116                         get {
117                                 if (xmlTextReader != null)
118                                         return xmlTextReader.Encoding;
119                                 else
120                                         throw new NotSupportedException ("Encoding is supported only for XmlTextReader.");
121                         }
122                 }
123
124                 public EntityHandling EntityHandling {
125                         get { return entityHandling; }
126                         set {
127                                 entityHandling = value;
128                                 if (dtdReader != null)
129                                         dtdReader.EntityHandling = value;
130                         }
131                 }
132
133                 public override bool EOF { 
134                         get { return validatingReader == null ? false : validatingReader.EOF; }
135                 }
136
137 #if DTD_HANDLE_EVENTS
138                 internal bool HasValidationEvent {
139                         get { return ValidationEventHandler != null; }
140                 }
141 #endif
142
143                 public override bool HasValue { 
144                         get { return validatingReader == null ? false : validatingReader.HasValue; }
145                 }
146
147                 public override bool IsDefault {
148                         get { return validatingReader == null ? false : validatingReader.IsDefault; }
149                 }
150
151                 public override bool IsEmptyElement { 
152                         get { return validatingReader == null ? false : validatingReader.IsEmptyElement; }
153                 }
154
155 #if NET_2_0
156 #else
157                 public override string this [int i] { 
158                         get { return GetAttribute (i); }
159                 }
160
161                 public override string this [string name] { 
162                         get { return GetAttribute (name); }
163                 }
164
165                 public override string this [string localName, string namespaceName] { 
166                         get { return GetAttribute (localName, namespaceName); }
167                 }
168 #endif
169
170 #if NET_2_0
171                 public int LineNumber {
172 #else
173                 int IXmlLineInfo.LineNumber {
174 #endif
175                         get {
176                                 if (IsDefault)
177                                         return 0;
178                                 IXmlLineInfo info = validatingReader as IXmlLineInfo;
179                                 return info != null ? info.LineNumber : 0;
180                         }
181                 }
182
183 #if NET_2_0
184                 public int LinePosition {
185 #else
186                 int IXmlLineInfo.LinePosition {
187 #endif
188                         get {
189                                 if (IsDefault)
190                                         return 0;
191                                 IXmlLineInfo info = validatingReader as IXmlLineInfo;
192                                 return info != null ? info.LinePosition : 0;
193                         }
194                 }
195
196                 public override string LocalName { 
197                         get {
198                                 if (validatingReader == null)
199                                         return String.Empty;
200                                 else if (Namespaces)
201                                         return validatingReader.LocalName;
202                                 else
203                                         return validatingReader.Name;
204                         }
205                 }
206
207                 public override string Name {
208                         get { return validatingReader == null ? String.Empty : validatingReader.Name; }
209                 }
210
211                 public bool Namespaces {
212                         get {
213                                 if (xmlTextReader != null)
214                                         return xmlTextReader.Namespaces;
215                                 else
216                                         return true;
217                         }
218                         set {
219                                 if (ReadState != ReadState.Initial)
220                                         throw new InvalidOperationException ("Namespaces have to be set before reading.");
221
222                                 if (xmlTextReader != null)
223                                         xmlTextReader.Namespaces = value;
224                                 else
225                                         throw new NotSupportedException ("Property 'Namespaces' is supported only for XmlTextReader.");
226                         }
227                 }
228
229                 public override string NamespaceURI { 
230                         get {
231                                 if (validatingReader == null)
232                                         return String.Empty;
233                                 else if (Namespaces)
234                                         return validatingReader.NamespaceURI;
235                                 else
236                                         return String.Empty;
237                         }
238                 }
239
240                 public override XmlNameTable NameTable { 
241                         get { return validatingReader == null ? sourceReader.NameTable : validatingReader.NameTable; }
242                 }
243
244                 public override XmlNodeType NodeType { 
245                         get { return validatingReader == null ? XmlNodeType.None : validatingReader.NodeType; }
246                 }
247
248                 public override string Prefix {
249                         get { return validatingReader == null ? String.Empty : validatingReader.Prefix; }
250                 }
251
252                 public override char QuoteChar { 
253                         get { return validatingReader == null ? sourceReader.QuoteChar : validatingReader.QuoteChar; }
254                 }
255
256                 public XmlReader Reader {
257                         get { return sourceReader; }
258                 }
259
260                 public override ReadState ReadState { 
261                         get {
262                                 if (validatingReader == null)
263                                         return ReadState.Initial;
264                                 return validatingReader.ReadState; 
265                         }
266                 }
267
268                 internal XmlResolver Resolver {
269                         get {
270                                 // This is special rule... MS.NET shares the
271                                 // XmlResolver between XmlTextReader and
272                                 // XmlValidatingReader, so we mimick that
273                                 // silly behavior here.
274                                 if (this.xmlTextReader != null)
275                                         return this.xmlTextReader.Resolver;
276                                 else if (resolverSpecified)
277                                         return resolver;
278                                 else
279                                         return null;
280                         }
281                 }
282
283                 public XmlSchemaCollection Schemas {
284                         get {
285                                 if (schemas == null)
286                                         schemas = new XmlSchemaSet ();
287                                 return schemas.SchemaCollection;
288                         }
289                 }
290
291                 internal void SetSchemas (XmlSchemaSet schemas)
292                 {
293                         this.schemas = schemas;
294                 }
295
296                 public object SchemaType {
297                         get { return schemaInfo.SchemaType; }
298                 }
299
300 #if NET_2_0
301                 [MonoTODO]
302                 public override XmlReaderSettings Settings {
303                         get { return validatingReader == null ? sourceReader.Settings : validatingReader.Settings; }
304                 }
305 #endif
306
307                 [MonoTODO ("We decided not to support XDR schema that spec is obsolete.")]
308                 public ValidationType ValidationType {
309                         get { return validationType; }
310                         set {
311                                 if (ReadState != ReadState.Initial)
312                                         throw new InvalidOperationException ("ValidationType cannot be set after the first call to Read method.");
313                                 switch (validationType) {
314                                 case ValidationType.Auto:
315                                 case ValidationType.DTD:
316                                 case ValidationType.None:
317                                 case ValidationType.Schema:
318                                         validationType = value; 
319                                         break;
320                                 case ValidationType.XDR:
321                                         throw new NotSupportedException ();
322                                 }
323                         }
324                 }
325
326                 public override string Value {
327                         get { return validatingReader == null ? String.Empty : validatingReader.Value; }
328                 }
329
330                 public override string XmlLang {
331                         get { return validatingReader == null ? String.Empty : validatingReader.XmlLang; }
332                 }
333
334                 public XmlResolver XmlResolver {
335                         set {
336                                 resolverSpecified = true;
337                                 resolver = value;
338                                 if (xmlTextReader != null)
339                                         xmlTextReader.XmlResolver = value;
340
341                                 XsdValidatingReader xsvr = validatingReader as XsdValidatingReader;
342                                 if (xsvr != null)
343                                         xsvr.XmlResolver = value;
344                                 DTDValidatingReader dvr = validatingReader as DTDValidatingReader;
345                                 if (dvr != null)
346                                         dvr.XmlResolver = value;
347                         }
348                 }
349
350                 public override XmlSpace XmlSpace {
351                         get { return validatingReader == null ? XmlSpace.None : validatingReader.XmlSpace; }
352                 }
353
354                 #endregion // Properties
355
356                 #region Methods
357
358                 public override void Close ()
359                 {
360                         if (validatingReader == null)
361                                 sourceReader.Close ();
362                         else
363                                 validatingReader.Close ();
364                 }
365
366                 public override string GetAttribute (int i)
367                 {
368                         if (validatingReader == null)
369                                 throw new IndexOutOfRangeException ("Reader is not started.");
370                         return validatingReader [i];
371                 }
372
373                 public override string GetAttribute (string name)
374                 {
375                         return validatingReader == null ? null : validatingReader [name];
376                 }
377
378                 public override string GetAttribute (string localName, string namespaceName)
379                 {
380                         return validatingReader == null ? null : validatingReader [localName, namespaceName];
381                 }
382
383                 XmlParserContext IHasXmlParserContext.ParserContext {
384                         get { return dtdReader != null ? dtdReader.ParserContext : null; }
385                 }
386
387 #if NET_2_0
388                 IDictionary<string, string> IXmlNamespaceResolver.GetNamespacesInScope (XmlNamespaceScope scope)
389                 {
390                         return ((IHasXmlParserContext) this).ParserContext.NamespaceManager.GetNamespacesInScope (scope);
391                 }
392 #endif
393
394 #if NET_2_0
395                 public bool HasLineInfo ()
396 #else
397                 bool IXmlLineInfo.HasLineInfo ()
398 #endif
399                 {
400                         IXmlLineInfo info = validatingReader as IXmlLineInfo;
401                         return info != null ? info.HasLineInfo () : false;
402                 }
403
404                 public override string LookupNamespace (string prefix)
405                 {
406                         if (validatingReader != null)
407                                 return validatingReader.LookupNamespace (prefix);
408                         else
409                                 return sourceReader.LookupNamespace (prefix);
410                 }
411
412 #if NET_2_0
413                 string IXmlNamespaceResolver.LookupPrefix (string ns)
414                 {
415                         IXmlNamespaceResolver res = null;
416                         if (validatingReader != null)
417                                 res = sourceReader as IXmlNamespaceResolver;
418                         else
419                                 res = validatingReader as IXmlNamespaceResolver;
420                         return res != null ?
421                                 res.LookupNamespace (ns) :
422                                 null;
423                 }
424 #endif
425
426
427                 public override void MoveToAttribute (int i)
428                 {
429                         if (validatingReader == null)
430                                 throw new IndexOutOfRangeException ("Reader is not started.");
431                         else
432                                 validatingReader.MoveToAttribute (i);
433                 }
434
435                 public override bool MoveToAttribute (string name)
436                 {
437                         if (validatingReader == null)
438                                 return false;
439                         return validatingReader.MoveToAttribute (name);
440                 }
441
442                 public override bool MoveToAttribute (string localName, string namespaceName)
443                 {
444                         if (validatingReader == null)
445                                 return false;
446                         return validatingReader.MoveToAttribute (localName, namespaceName);
447                 }
448
449                 public override bool MoveToElement ()
450                 {
451                         if (validatingReader == null)
452                                 return false;
453                         return validatingReader.MoveToElement ();
454                 }
455
456                 public override bool MoveToFirstAttribute ()
457                 {
458                         if (validatingReader == null)
459                                 return false;
460                         return validatingReader.MoveToFirstAttribute ();
461                 }
462
463                 public override bool MoveToNextAttribute ()
464                 {
465                         if (validatingReader == null)
466                                 return false;
467                         return validatingReader.MoveToNextAttribute ();
468                 }
469
470                 [MonoTODO ("We decided not to support XDR schema that spec is obsolete.")]
471                 public override bool Read ()
472                 {
473                         if (ReadState == ReadState.Initial) {
474                                 switch (ValidationType) {
475                                 case ValidationType.Auto:
476                                 case ValidationType.None:
477                                         goto case ValidationType.Schema; // might be specified by xsi:schemaLocation.
478                                 case ValidationType.DTD:
479                                         validatingReader = dtdReader = new DTDValidatingReader (sourceReader, this);
480                                         dtdReader.XmlResolver = Resolver;
481                                         break;
482                                 case ValidationType.Schema:
483                                         dtdReader = new DTDValidatingReader (sourceReader, this);
484                                         XsdValidatingReader xsvr = new XsdValidatingReader (dtdReader);
485                                         xsvr.ValidationEventHandler +=
486                                                 new ValidationEventHandler (
487                                                         OnValidationEvent);
488                                         xsvr.ValidationType = ValidationType;
489                                         xsvr.Schemas = Schemas.SchemaSet;
490                                         xsvr.XmlResolver = Resolver;
491                                         validatingReader = xsvr;
492                                         dtdReader.XmlResolver = Resolver;
493                                         break;
494                                 case ValidationType.XDR:
495                                         throw new NotSupportedException ();
496                                 }
497                                 schemaInfo = validatingReader as IHasXmlSchemaInfo;
498                         }
499                         return validatingReader.Read ();
500                 }
501
502                 public override bool ReadAttributeValue ()
503                 {
504                         if (validatingReader == null)
505                                 return false;
506                         return validatingReader.ReadAttributeValue ();
507                 }
508
509 #if NET_1_0
510                 // LAMESPEC: MS.NET 1.0 has critical bug here.
511                 // After calling these methods, validation does not work anymore!
512                 public override string ReadInnerXml ()
513                 {
514                         if (validatingReader == null)
515                                 return "";
516                         return validatingReader.ReadInnerXml ();
517                 }
518
519                 public override string ReadOuterXml ()
520                 {
521                         if (validatingReader == null)
522                                 return "";
523                         return validatingReader.ReadOuterXml ();
524                 }
525 #endif
526
527 #if NET_1_0
528                 public override string ReadString ()
529                 {
530                         return base.ReadStringInternal ();
531                 }
532 #else
533                 public override string ReadString ()
534                 {
535                         return base.ReadString ();
536                 }
537 #endif
538
539 #if NET_2_0
540                 [Obsolete]
541                 public override object ReadTypedValue ()
542 #else
543                 public object ReadTypedValue ()
544 #endif
545                 {
546                         if (dtdReader == null)
547                                 return null;
548                         XmlSchemaDatatype dt = schemaInfo.SchemaType as XmlSchemaDatatype;
549                         if (dt == null)
550                                 return null;
551                         switch (NodeType) {
552                         case XmlNodeType.Element:
553                                 if (IsEmptyElement)
554                                         return null;
555
556                                 storedCharacters.Length = 0;
557                                 bool loop = true;
558                                 do {
559                                         Read ();
560                                         switch (NodeType) {
561                                         case XmlNodeType.SignificantWhitespace:
562                                         case XmlNodeType.Text:
563                                         case XmlNodeType.CDATA:
564                                                 storedCharacters.Append (Value);
565                                                 break;
566                                         case XmlNodeType.Comment:
567                                                 break;
568                                         default:
569                                                 loop = false;
570                                                 break;
571                                         }
572                                 } while (loop && !EOF);
573                                 return dt.ParseValue (storedCharacters.ToString (), NameTable, dtdReader.ParserContext.NamespaceManager);
574                         case XmlNodeType.Attribute:
575                                 return dt.ParseValue (Value, NameTable, dtdReader.ParserContext.NamespaceManager);
576                         }
577                         return null;
578                 }
579
580                 public override void ResolveEntity ()
581                 {
582                         validatingReader.ResolveEntity ();
583                 }
584
585                 // It should be "protected" as usual "event model"
586                 // methods are, but validation event is not exposed,
587                 // so it is no other way to make it "internal".
588                 internal void OnValidationEvent (object o, ValidationEventArgs e)
589                 {
590                         if (ValidationEventHandler != null)
591                                 ValidationEventHandler (o, e);
592                         else if (ValidationType != ValidationType.None && e.Severity == XmlSeverityType.Error)
593                                 throw e.Exception;
594                 }
595
596 #if NET_2_0
597                 [MonoTODO ("Check how expanded entity is handled here.")]
598                 public override int ReadContentAsBase64 (byte [] buffer, int offset, int length)
599                 {
600                         if (validatingReader != null)
601                                 return validatingReader.ReadContentAsBase64 (buffer, offset, length);
602                         else
603                                 return sourceReader.ReadContentAsBase64 (buffer, offset, length);
604                 }
605
606                 [MonoTODO ("Check how expanded entity is handled here.")]
607                 public override int ReadContentAsBinHex (byte [] buffer, int offset, int length)
608                 {
609                         if (validatingReader != null)
610                                 return validatingReader.ReadContentAsBinHex (buffer, offset, length);
611                         else
612                                 return sourceReader.ReadContentAsBinHex (buffer, offset, length);
613                 }
614
615                 [MonoTODO ("Check how expanded entity is handled here.")]
616                 public override int ReadElementContentAsBase64 (byte [] buffer, int offset, int length)
617                 {
618                         if (validatingReader != null)
619                                 return validatingReader.ReadElementContentAsBase64 (buffer, offset, length);
620                         else
621                                 return sourceReader.ReadElementContentAsBase64 (buffer, offset, length);
622                 }
623
624                 [MonoTODO ("Check how expanded entity is handled here.")]
625                 public override int ReadElementContentAsBinHex (byte [] buffer, int offset, int length)
626                 {
627                         if (validatingReader != null)
628                                 return validatingReader.ReadElementContentAsBinHex (buffer, offset, length);
629                         else
630                                 return sourceReader.ReadElementContentAsBinHex (buffer, offset, length);
631                 }
632 #endif
633                 #endregion // Methods
634
635                 #region Events and Delegates
636
637                 public event ValidationEventHandler ValidationEventHandler;
638
639                 #endregion // Events and Delegates
640         }
641 }