2007-09-27 Atsushi Enomoto <atsushi@ximian.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                 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 namespaceName)
381                 {
382                         return validatingReader == null ? null : validatingReader [localName, namespaceName];
383                 }
384
385                 XmlParserContext IHasXmlParserContext.ParserContext {
386                         get { return dtdReader != null ? dtdReader.ParserContext : null; }
387                 }
388
389 #if NET_2_0
390                 IDictionary<string, string> IXmlNamespaceResolver.GetNamespacesInScope (XmlNamespaceScope scope)
391                 {
392                         return ((IHasXmlParserContext) this).ParserContext.NamespaceManager.GetNamespacesInScope (scope);
393                 }
394 #endif
395
396 #if NET_2_0
397                 public bool HasLineInfo ()
398 #else
399                 bool IXmlLineInfo.HasLineInfo ()
400 #endif
401                 {
402                         IXmlLineInfo info = validatingReader as IXmlLineInfo;
403                         return info != null ? info.HasLineInfo () : false;
404                 }
405
406                 public override string LookupNamespace (string prefix)
407                 {
408                         if (validatingReader != null)
409                                 return validatingReader.LookupNamespace (prefix);
410                         else
411                                 return sourceReader.LookupNamespace (prefix);
412                 }
413
414 #if NET_2_0
415                 string IXmlNamespaceResolver.LookupPrefix (string ns)
416                 {
417                         IXmlNamespaceResolver res = null;
418                         if (validatingReader != null)
419                                 res = sourceReader as IXmlNamespaceResolver;
420                         else
421                                 res = validatingReader as IXmlNamespaceResolver;
422                         return res != null ?
423                                 res.LookupNamespace (ns) :
424                                 null;
425                 }
426 #endif
427
428
429                 public override void MoveToAttribute (int i)
430                 {
431                         if (validatingReader == null)
432                                 throw new IndexOutOfRangeException ("Reader is not started.");
433                         else
434                                 validatingReader.MoveToAttribute (i);
435                 }
436
437                 public override bool MoveToAttribute (string name)
438                 {
439                         if (validatingReader == null)
440                                 return false;
441                         return validatingReader.MoveToAttribute (name);
442                 }
443
444                 public override bool MoveToAttribute (string localName, string namespaceName)
445                 {
446                         if (validatingReader == null)
447                                 return false;
448                         return validatingReader.MoveToAttribute (localName, namespaceName);
449                 }
450
451                 public override bool MoveToElement ()
452                 {
453                         if (validatingReader == null)
454                                 return false;
455                         return validatingReader.MoveToElement ();
456                 }
457
458                 public override bool MoveToFirstAttribute ()
459                 {
460                         if (validatingReader == null)
461                                 return false;
462                         return validatingReader.MoveToFirstAttribute ();
463                 }
464
465                 public override bool MoveToNextAttribute ()
466                 {
467                         if (validatingReader == null)
468                                 return false;
469                         return validatingReader.MoveToNextAttribute ();
470                 }
471
472                 [MonoTODO]
473                 // We decided not to support XDR schema; it is obsolete.
474                 public override bool Read ()
475                 {
476                         if (validatingReader == null) {
477                                 switch (ValidationType) {
478                                 case ValidationType.Auto:
479                                 case ValidationType.None:
480                                         goto case ValidationType.Schema; // might be specified by xsi:schemaLocation.
481                                 case ValidationType.DTD:
482                                         validatingReader = dtdReader = new DTDValidatingReader (sourceReader, this);
483                                         dtdReader.XmlResolver = Resolver;
484                                         break;
485                                 case ValidationType.Schema:
486                                         dtdReader = new DTDValidatingReader (sourceReader, this);
487                                         XsdValidatingReader xsvr = new XsdValidatingReader (dtdReader);
488                                         xsvr.ValidationEventHandler +=
489                                                 new ValidationEventHandler (
490                                                         OnValidationEvent);
491                                         xsvr.ValidationType = ValidationType;
492                                         xsvr.Schemas = Schemas.SchemaSet;
493                                         xsvr.XmlResolver = Resolver;
494                                         validatingReader = xsvr;
495                                         dtdReader.XmlResolver = Resolver;
496                                         break;
497                                 case ValidationType.XDR:
498                                         throw new NotSupportedException ();
499                                 }
500                                 schemaInfo = validatingReader as IHasXmlSchemaInfo;
501                         }
502                         return validatingReader.Read ();
503                 }
504
505                 public override bool ReadAttributeValue ()
506                 {
507                         if (validatingReader == null)
508                                 return false;
509                         return validatingReader.ReadAttributeValue ();
510                 }
511
512                 public override string ReadString ()
513                 {
514                         return base.ReadString ();
515                 }
516
517                 public object ReadTypedValue ()
518                 {
519                         if (dtdReader == null)
520                                 return null;
521                         XmlSchemaDatatype dt = schemaInfo.SchemaType as XmlSchemaDatatype;
522                         if (dt == null) {
523                                 XmlSchemaType st = schemaInfo.SchemaType as XmlSchemaType;
524                                 if (st != null)
525                                         dt = st.Datatype;
526                         }
527                         if (dt == null)
528                                 return null;
529                         switch (NodeType) {
530                         case XmlNodeType.Element:
531                                 if (IsEmptyElement)
532                                         return null;
533
534                                 storedCharacters.Length = 0;
535                                 bool loop = true;
536                                 do {
537                                         Read ();
538                                         switch (NodeType) {
539                                         case XmlNodeType.Whitespace:
540                                         case XmlNodeType.SignificantWhitespace:
541                                         case XmlNodeType.Text:
542                                         case XmlNodeType.CDATA:
543                                                 storedCharacters.Append (Value);
544                                                 break;
545                                         case XmlNodeType.Comment:
546                                                 break;
547                                         default:
548                                                 loop = false;
549                                                 break;
550                                         }
551                                 } while (loop && !EOF);
552                                 return dt.ParseValue (storedCharacters.ToString (), NameTable, dtdReader.ParserContext.NamespaceManager);
553                         case XmlNodeType.Attribute:
554                                 return dt.ParseValue (Value, NameTable, dtdReader.ParserContext.NamespaceManager);
555                         }
556                         return null;
557                 }
558
559                 public override void ResolveEntity ()
560                 {
561                         validatingReader.ResolveEntity ();
562                 }
563
564                 // It should be "protected" as usual "event model"
565                 // methods are, but validation event is not exposed,
566                 // so it is no other way to make it "internal".
567                 internal void OnValidationEvent (object o, ValidationEventArgs e)
568                 {
569                         if (ValidationEventHandler != null)
570                                 ValidationEventHandler (o, e);
571                         else if (ValidationType != ValidationType.None && e.Severity == XmlSeverityType.Error)
572                                 throw e.Exception;
573                 }
574
575 #if NET_2_0
576                 [MonoTODO] // FIXME: Check how expanded entity is handled here.
577                 public override int ReadContentAsBase64 (byte [] buffer, int offset, int length)
578                 {
579                         if (validatingReader != null)
580                                 return validatingReader.ReadContentAsBase64 (buffer, offset, length);
581                         else
582                                 return sourceReader.ReadContentAsBase64 (buffer, offset, length);
583                 }
584
585                 [MonoTODO] // FIXME: Check how expanded entity is handled here.
586                 public override int ReadContentAsBinHex (byte [] buffer, int offset, int length)
587                 {
588                         if (validatingReader != null)
589                                 return validatingReader.ReadContentAsBinHex (buffer, offset, length);
590                         else
591                                 return sourceReader.ReadContentAsBinHex (buffer, offset, length);
592                 }
593
594                 [MonoTODO] // FIXME: Check how expanded entity is handled here.
595                 public override int ReadElementContentAsBase64 (byte [] buffer, int offset, int length)
596                 {
597                         if (validatingReader != null)
598                                 return validatingReader.ReadElementContentAsBase64 (buffer, offset, length);
599                         else
600                                 return sourceReader.ReadElementContentAsBase64 (buffer, offset, length);
601                 }
602
603                 [MonoTODO] // FIXME: Check how expanded entity is handled here.
604                 public override int ReadElementContentAsBinHex (byte [] buffer, int offset, int length)
605                 {
606                         if (validatingReader != null)
607                                 return validatingReader.ReadElementContentAsBinHex (buffer, offset, length);
608                         else
609                                 return sourceReader.ReadElementContentAsBinHex (buffer, offset, length);
610                 }
611 #endif
612                 #endregion // Methods
613
614                 #region Events and Delegates
615
616                 public event ValidationEventHandler ValidationEventHandler;
617
618                 #endregion // Events and Delegates
619         }
620 }