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