2005-06-14 Atsushi Enomoto <atsushi@ximian.com>
[mono.git] / mcs / class / System.XML / System.Xml / DTDValidatingReader.cs
1 //
2 // DTDValidatingReader.cs
3 //
4 // Author:
5 //   Atsushi Enomoto  (ginga@kit.hi-ho.ne.jp)
6 //
7 // (C)2003 Atsushi Enomoto
8 // (C)2004 Novell Inc.
9 //
10
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 using System;
32 using System.Collections;
33 using System.IO;
34 using System.Text;
35 using System.Xml;
36 using System.Xml.Schema;
37
38 #if NET_2_0
39 using XmlTextReaderImpl = Mono.Xml2.XmlTextReader;
40 #else
41 using XmlTextReaderImpl = System.Xml.XmlTextReader;
42 #endif
43
44
45 namespace Mono.Xml
46 {
47 #if NET_2_0
48         internal class DTDValidatingReader : XmlReader, IXmlLineInfo, IHasXmlParserContext, IHasXmlSchemaInfo, IXmlNamespaceResolver
49 #else
50         internal class DTDValidatingReader : XmlReader, IXmlLineInfo, IHasXmlParserContext, IHasXmlSchemaInfo
51 #endif
52         {
53                 public DTDValidatingReader (XmlReader reader)
54                         : this (reader, null)
55                 {
56                 }
57
58                 internal DTDValidatingReader (XmlReader reader,
59                         XmlValidatingReader validatingReader)
60                 {
61                         entityReaderStack = new Stack ();
62                         entityReaderDepthStack = new Stack ();
63                         this.reader = reader;
64                         this.sourceTextReader = reader as XmlTextReader;
65                         elementStack = new Stack ();
66                         automataStack = new Stack ();
67                         attributes = new ArrayList ();
68                         attributeValues = new Hashtable ();
69                         attributeLocalNames = new Hashtable ();
70                         attributeNamespaces = new Hashtable ();
71                         attributePrefixes = new Hashtable ();
72                         nsdecls = new Hashtable ();
73                         this.validatingReader = validatingReader;
74                         valueBuilder = new StringBuilder ();
75                         idList = new ArrayList ();
76                         missingIDReferences = new ArrayList ();
77                         XmlTextReader xtReader = reader as XmlTextReader;
78                         if (xtReader != null) {
79                                 resolver = xtReader.Resolver;
80                         }
81                         else
82                                 resolver = new XmlUrlResolver ();
83                 }
84
85                 Stack entityReaderStack;
86                 Stack entityReaderDepthStack;
87                 XmlReader reader;
88                 XmlTextReader sourceTextReader;
89                 XmlTextReader nextEntityReader;
90                 DTDObjectModel dtd;
91                 Stack elementStack;
92                 Stack automataStack;
93                 string currentElement;
94                 string currentAttribute;
95                 string currentTextValue;
96                 string constructingTextValue;
97                 bool shouldResetCurrentTextValue;
98                 bool consumedAttribute;
99                 bool insideContent;
100                 DTDAutomata currentAutomata;
101                 DTDAutomata previousAutomata;
102                 bool isStandalone;
103                 ArrayList attributes;
104                 Hashtable attributeValues;
105                 Hashtable attributeLocalNames;
106                 Hashtable attributeNamespaces;
107                 Hashtable attributePrefixes;
108                 Hashtable nsdecls;
109                 StringBuilder valueBuilder;
110                 ArrayList idList;
111                 ArrayList missingIDReferences;
112                 XmlResolver resolver;
113                 EntityHandling currentEntityHandling;
114                 bool isSignificantWhitespace;
115                 bool isWhitespace;
116                 bool isText;
117                 bool dontResetTextType;
118
119                 // This field is used to get properties and to raise events.
120                 XmlValidatingReader validatingReader;
121
122                 public DTDObjectModel DTD {
123                         get { return dtd; }
124                 }
125
126                 public EntityHandling EntityHandling {
127                         get { return currentEntityHandling; }
128                         set { currentEntityHandling = value; }
129                 }
130
131                 public override void Close ()
132                 {
133                         reader.Close ();
134                 }
135
136                 // We had already done attribute validation, so can ignore name.
137                 public override string GetAttribute (int i)
138                 {
139                         if (currentTextValue != null)
140                                 throw new IndexOutOfRangeException ("Specified index is out of range: " + i);
141
142                         if (dtd == null)
143                                 return reader.GetAttribute (i);
144
145                         if (attributes.Count <= i)
146                                 throw new IndexOutOfRangeException ("Specified index is out of range: " + i);
147
148                         string attrName = (string) attributes [i];
149                         return FilterNormalization (attrName, (string) attributeValues [attrName]);
150                 }
151
152                 public override string GetAttribute (string name)
153                 {
154                         if (currentTextValue != null)
155                                 return null;
156
157                         if (dtd == null)
158                                 return reader.GetAttribute (name);
159
160                         return FilterNormalization (name, (string) attributeValues [name]);
161                 }
162
163                 public override string GetAttribute (string name, string ns)
164                 {
165                         if (currentTextValue != null)
166                                 return null;
167
168                         if (dtd == null)
169                                 return reader.GetAttribute (name, ns);
170
171                         return reader.GetAttribute ((string) attributeLocalNames [name], ns);
172                 }
173
174 #if NET_2_0
175                 IDictionary IXmlNamespaceResolver.GetNamespacesInScope (XmlNamespaceScope scope)
176                 {
177                         IXmlNamespaceResolver res = reader as IXmlNamespaceResolver;
178                         return res != null ? res.GetNamespacesInScope (scope) : new Hashtable ();
179                 }
180 #endif
181
182                 bool IXmlLineInfo.HasLineInfo ()
183                 {
184                         IXmlLineInfo ixli = reader as IXmlLineInfo;
185                         if (ixli != null)
186                                 return ixli.HasLineInfo ();
187                         else
188                                 return false;
189                 }
190
191                 public override string LookupNamespace (string prefix)
192                 {
193                         if (nsdecls.Count > 0 && nsdecls [prefix] != null)
194                                 return (string) nsdecls [prefix];
195                         return reader.LookupNamespace (prefix);
196                 }
197
198 #if NET_2_0
199                 string IXmlNamespaceResolver.LookupPrefix (string ns)
200                 {
201                         IXmlNamespaceResolver res = reader as IXmlNamespaceResolver;
202                         return res != null ? res.LookupPrefix (ns) : null;
203                 }
204 #endif
205
206                 public override void MoveToAttribute (int i)
207                 {
208                         if (currentTextValue != null)
209                                 throw new IndexOutOfRangeException ("The index is out of range.");
210
211                         if (dtd == null) {
212                                 reader.MoveToAttribute (i);
213                                 currentAttribute = reader.Name;
214                                 consumedAttribute = false;
215                                 return;
216                         }
217
218                         if (currentElement == null)
219                                 throw new IndexOutOfRangeException ("The index is out of range.");
220
221                         if (attributes.Count > i) {
222                                 if (reader.AttributeCount > i)
223                                         reader.MoveToAttribute (i);
224                                 currentAttribute = (string) attributes [i];
225                                 consumedAttribute = false;
226                                 return;
227                         } else
228                                 throw new IndexOutOfRangeException ("The index is out of range.");
229                 }
230
231                 public override bool MoveToAttribute (string name)
232                 {
233                         if (currentTextValue != null)
234                                 return false;
235
236                         if (dtd == null) {
237                                 bool b = reader.MoveToAttribute (name);
238                                 if (b) {
239                                         currentAttribute = reader.Name;
240                                         consumedAttribute = false;
241                                 }
242                                 return b;
243                         }
244
245                         if (currentElement == null)
246                                 return false;
247
248                         int idx = attributes.IndexOf (name);
249                         if (idx >= 0) {
250                                 currentAttribute = name;
251                                 consumedAttribute = false;
252                                 return true;
253                         }
254                         return false;
255                 }
256
257                 public override bool MoveToAttribute (string name, string ns)
258                 {
259                         if (currentTextValue != null)
260                                 return false;
261
262                         if (dtd == null) {
263                                 bool b = reader.MoveToAttribute (name, ns);
264                                 if (b) {
265                                         currentAttribute = reader.Name;
266                                         consumedAttribute = false;
267                                 }
268                                 return b;
269                         }
270
271                         if (reader.MoveToAttribute (name, ns)) {
272                                 currentAttribute = reader.Name;
273                                 consumedAttribute = false;
274                                 return true;
275                         }
276
277                         for (int i = 0; i < attributes.Count; i++) {
278                                 string iter = (string) attributes [i];
279                                 if ((string) attributeLocalNames [iter] == name)
280                                         return MoveToAttribute (iter);
281                         }
282                         return false;
283                 }
284
285                 public override bool MoveToElement ()
286                 {
287                         if (currentTextValue != null)
288                                 return false;
289
290                         bool b = reader.MoveToElement ();
291                         if (!b && !IsDefault)
292                                 return false;
293                         currentAttribute = null;
294                         consumedAttribute = false;
295                         return true;
296                 }
297
298                 public override bool MoveToFirstAttribute ()
299                 {
300                         if (currentTextValue != null)
301                                 return false;
302
303                         if (dtd == null) {
304                                 bool b = reader.MoveToFirstAttribute ();
305                                 if (b) {
306                                         currentAttribute = reader.Name;
307                                         consumedAttribute = false;
308                                 }
309                                 return b;
310                         }
311
312                         if (attributes.Count == 0)
313                                 return false;
314                         currentAttribute = (string) attributes [0];
315                         reader.MoveToAttribute (currentAttribute);
316                         consumedAttribute = false;
317                         return true;
318                 }
319
320                 public override bool MoveToNextAttribute ()
321                 {
322                         if (currentTextValue != null)
323                                 return false;
324
325                         if (dtd == null) {
326                                 bool b = reader.MoveToNextAttribute ();
327                                 if (b) {
328                                         currentAttribute = reader.Name;
329                                         consumedAttribute = false;
330                                 }
331                                 return b;
332                         }
333
334                         if (currentAttribute == null)
335                                 return MoveToFirstAttribute ();
336
337                         int idx = attributes.IndexOf (currentAttribute);
338                         if (idx + 1 < attributes.Count) {
339                                 currentAttribute = (string) attributes [idx + 1];
340                                 reader.MoveToAttribute (currentAttribute);
341                                 consumedAttribute = false;
342                                 return true;
343                         } else
344                                 return false;
345                 }
346
347                 /*
348                 private void OnValidationEvent (object o, ValidationEventArgs e)
349                 {
350                         this.HandleError (e.Exception, e.Severity);
351                 }
352                 */
353
354                 public override bool Read ()
355                 {
356                         if (currentTextValue != null)
357                                 shouldResetCurrentTextValue = true;
358
359                         MoveToElement ();
360
361                         currentElement = null;
362                         currentAttribute = null;
363                         consumedAttribute = false;
364                         attributes.Clear ();
365                         attributeLocalNames.Clear ();
366                         attributeValues.Clear ();
367                         attributeNamespaces.Clear ();
368                         attributePrefixes.Clear ();
369                         nsdecls.Clear ();
370                         isWhitespace = false;
371                         isSignificantWhitespace = false;
372                         isText = false;
373                         dontResetTextType = false;
374
375                         bool b = ReadContent () || currentTextValue != null;
376                         if (!b && this.missingIDReferences.Count > 0) {
377                                 this.HandleError ("Missing ID reference was found: " +
378                                         String.Join (",", missingIDReferences.ToArray (typeof (string)) as string []),
379                                         XmlSeverityType.Error);
380                                 // Don't output the same errors so many times.
381                                 this.missingIDReferences.Clear ();
382                         }
383                         if (validatingReader != null)
384                                 EntityHandling = validatingReader.EntityHandling;
385                         return b;
386                 }
387
388                 private bool ReadContent ()
389                 {
390                         if (nextEntityReader != null) {
391                                 if (DTD == null || DTD.EntityDecls [reader.Name] == null)
392                                         throw NotWFError (String.Format ("Entity '{0}' was not declared.", reader.Name));
393                                 entityReaderStack.Push (reader);
394                                 entityReaderDepthStack.Push (Depth);
395                                 reader = sourceTextReader = nextEntityReader;
396                                 nextEntityReader = null;
397                                 return ReadContent ();
398                         } else if (reader.EOF && entityReaderStack.Count > 0) {
399                                 reader.Close ();
400                                 reader = entityReaderStack.Pop () as XmlReader;
401                                 entityReaderDepthStack.Pop ();
402                                 sourceTextReader = reader as XmlTextReader;
403                                 return ReadContent ();
404                         }
405
406                         bool b = !reader.EOF;
407                         if (shouldResetCurrentTextValue) {
408                                 currentTextValue = null;
409                                 shouldResetCurrentTextValue = false;
410                         }
411                         else
412                                 b = reader.Read ();
413
414                         if (!insideContent && reader.NodeType == XmlNodeType.Element) {
415                                 insideContent = true;
416                                 if (dtd == null)
417                                         currentAutomata = null;
418                                 else
419                                         currentAutomata = dtd.RootAutomata;
420                         }
421
422                         if (!b) {
423                                 if (entityReaderStack.Count > 0) {
424                                         if (validatingReader.EntityHandling == EntityHandling.ExpandEntities)
425                                                 return ReadContent ();
426                                         else
427                                                 return true;    // EndEntity
428                                 }
429
430                                 if (elementStack.Count != 0)
431                                         throw new InvalidOperationException ("Unexpected end of XmlReader.");
432                                 return false;
433                         }
434
435                         DTDElementDeclaration elem = null;
436
437                         switch (reader.NodeType) {
438                         case XmlNodeType.XmlDeclaration:
439                                 if (GetAttribute ("standalone") == "yes")
440                                         isStandalone = true;
441                                 ValidateAttributes (null, false);
442                                 break;
443
444                         case XmlNodeType.DocumentType:
445 //                              XmlTextReader xmlTextReader = reader as XmlTextReader;
446                                 IHasXmlParserContext ctx = reader as IHasXmlParserContext;
447                                 if (ctx != null)
448                                         dtd = ctx.ParserContext.Dtd;
449                                 if (dtd == null) {
450                                         XmlTextReaderImpl xmlTextReader = new XmlTextReaderImpl ("", XmlNodeType.Document, null);
451                                         xmlTextReader.XmlResolver = resolver;
452                                         xmlTextReader.GenerateDTDObjectModel (reader.Name,
453                                                 reader ["PUBLIC"], reader ["SYSTEM"], reader.Value);
454                                         dtd = xmlTextReader.DTD;
455                                 }
456
457                                 // Validity Constraints Check.
458                                 if (DTD.Errors.Length > 0)
459                                         for (int i = 0; i < DTD.Errors.Length; i++)
460                                                 HandleError (DTD.Errors [i].Message, XmlSeverityType.Error);
461
462                                 // NData target exists.
463                                 foreach (DTDEntityDeclaration ent in dtd.EntityDecls.Values)
464                                         if (ent.NotationName != null && dtd.NotationDecls [ent.NotationName] == null)
465                                                 this.HandleError ("Target notation was not found for NData in entity declaration " + ent.Name + ".",
466                                                         XmlSeverityType.Error);
467                                 // NOTATION exists for attribute default values
468                                 foreach (DTDAttListDeclaration attListIter in dtd.AttListDecls.Values)
469                                         foreach (DTDAttributeDefinition def in attListIter.Definitions)
470                                                 if (def.Datatype.TokenizedType == XmlTokenizedType.NOTATION) {
471                                                         foreach (string notation in def.EnumeratedNotations)
472                                                                 if (dtd.NotationDecls [notation] == null)
473                                                                         this.HandleError ("Target notation was not found for NOTATION typed attribute default " + def.Name + ".",
474                                                                                 XmlSeverityType.Error);
475                                                 }
476
477                                 break;
478
479                         case XmlNodeType.Element:
480                                 if (constructingTextValue != null) {
481                                         currentTextValue = constructingTextValue;
482                                         constructingTextValue = null;
483                                         if (isWhitespace)
484                                                 ValidateWhitespaceNode ();
485                                         return true;
486                                 }
487                                 elementStack.Push (reader.Name);
488                                 // startElementDeriv
489                                 // If no schema specification, then skip validation.
490                                 if (currentAutomata == null) {
491                                         ValidateAttributes (null, false);
492                                         if (reader.IsEmptyElement)
493                                                 goto case XmlNodeType.EndElement;
494                                         break;
495                                 }
496
497                                 previousAutomata = currentAutomata;
498                                 currentAutomata = currentAutomata.TryStartElement (reader.Name);
499                                 if (currentAutomata == DTD.Invalid) {
500                                         HandleError (String.Format ("Invalid start element found: {0}", reader.Name),
501                                                 XmlSeverityType.Error);
502                                         currentAutomata = previousAutomata;
503                                 }
504                                 elem = DTD.ElementDecls [reader.Name];
505                                 if (elem == null) {
506                                         HandleError (String.Format ("Element {0} is not declared.", reader.Name),
507                                                 XmlSeverityType.Error);
508                                         currentAutomata = previousAutomata;
509                                 }
510
511                                 currentElement = Name;
512                                 automataStack.Push (currentAutomata);
513                                 if (elem != null)       // i.e. not invalid
514                                         currentAutomata = elem.ContentModel.GetAutomata ();
515
516                                 DTDAttListDeclaration attList = dtd.AttListDecls [currentElement];
517                                 if (attList != null) {
518                                         // check attributes
519                                         ValidateAttributes (attList, true);
520                                         currentAttribute = null;
521                                 } else {
522                                         if (reader.HasAttributes) {
523                                                 HandleError (String.Format (
524                                                         "Attributes are found on element {0} while it has no attribute definitions.", currentElement),
525                                                         XmlSeverityType.Error);
526                                         }
527                                         // SetupValidityIgnorantAttributes ();
528                                         ValidateAttributes (null, false);
529                                 }
530                                 foreach (string attr in attributes)
531                                         if (attr == "xmlns" ||
532                                                 String.CompareOrdinal (attr, 0, "xmlns", 0, 5) == 0)
533                                                 nsdecls.Add (
534                                                         attr == "xmlns" ? String.Empty : attributeLocalNames [attr],
535                                                         attributeValues [attr]);
536                                 // If it is empty element then directly check end element.
537                                 if (reader.IsEmptyElement)
538                                         goto case XmlNodeType.EndElement;
539                                 break;
540
541                         case XmlNodeType.EndElement:
542                                 if (constructingTextValue != null) {
543                                         currentTextValue = constructingTextValue;
544                                         constructingTextValue = null;
545                                         return true;
546                                 }
547                                 elementStack.Pop ();
548                                 // endElementDeriv
549                                 // If no schema specification, then skip validation.
550                                 if (currentAutomata == null)
551                                         break;
552
553                                 elem = DTD.ElementDecls [reader.Name];
554                                 if (elem == null) {
555                                         HandleError (String.Format ("Element {0} is not declared.", reader.Name),
556                                                 XmlSeverityType.Error);
557                                 }
558
559                                 previousAutomata = currentAutomata;
560                                 // Don't let currentAutomata
561                                 DTDAutomata tmpAutomata = currentAutomata.TryEndElement ();
562                                 if (tmpAutomata == DTD.Invalid) {
563                                         HandleError (String.Format ("Invalid end element found: {0}", reader.Name),
564                                                 XmlSeverityType.Error);
565                                         currentAutomata = previousAutomata;
566                                 }
567
568                                 currentAutomata = automataStack.Pop () as DTDAutomata;
569                                 break;
570
571                         case XmlNodeType.CDATA:
572                                 isSignificantWhitespace = isWhitespace = false;
573                                 isText = true;
574
575                                 ValidateText ();
576
577                                 if (currentTextValue != null) {
578                                         currentTextValue = constructingTextValue;
579                                         constructingTextValue = null;
580                                         return true;
581                                 }
582                                 break;
583                         case XmlNodeType.SignificantWhitespace:
584                                 if (!isText)
585                                         isSignificantWhitespace = true;
586                                 isWhitespace = false;
587                                 dontResetTextType = true;
588                                 goto case XmlNodeType.DocumentFragment;
589                         case XmlNodeType.Text:
590                                 isWhitespace = isSignificantWhitespace = false;
591                                 isText = true;
592                                 goto case XmlNodeType.DocumentFragment;
593                         case XmlNodeType.DocumentFragment:
594                                 // it should not happen, but in case if
595                                 // XmlReader really returns it, just ignore.
596                                 if (reader.NodeType == XmlNodeType.DocumentFragment)
597                                         break;
598
599                                 ValidateText ();
600
601                                 if (entityReaderStack.Count > 0 && validatingReader.EntityHandling == EntityHandling.ExpandEntities) {
602                                         constructingTextValue += reader.Value;
603                                         return ReadContent ();
604                                 }
605                                 break;
606                         case XmlNodeType.Whitespace:
607                                 if (!isText && !isSignificantWhitespace)
608                                         isWhitespace = true;
609                                 goto case XmlNodeType.DocumentFragment;
610                         case XmlNodeType.EntityReference:
611                                 if (validatingReader.EntityHandling == EntityHandling.ExpandEntities) {
612                                         ResolveEntity ();
613                                         return ReadContent ();
614                                 }
615                                 break;
616                         }
617                         if (isWhitespace)
618                                 ValidateWhitespaceNode ();
619                         currentTextValue = constructingTextValue;
620                         constructingTextValue = null;
621                         MoveToElement ();
622                         return true;
623                 }
624
625                 private void ValidateText ()
626                 {
627                         if (currentAutomata == null)
628                                 return;
629
630                         DTDElementDeclaration elem = null;
631                         if (elementStack.Count > 0)
632                                 elem = dtd.ElementDecls [elementStack.Peek () as string];
633                         // Here element should have been already validated, so
634                         // if no matching declaration is found, simply ignore.
635                         if (elem != null && !elem.IsMixedContent && !elem.IsAny && !isWhitespace) {
636                                 HandleError (String.Format ("Current element {0} does not allow character data content.", elementStack.Peek () as string),
637                                         XmlSeverityType.Error);
638                                 currentAutomata = previousAutomata;
639                         }
640                 }
641
642                 private void ValidateWhitespaceNode ()
643                 {
644                         // VC Standalone Document Declaration (2.9)
645                         if (this.isStandalone && DTD != null && elementStack.Count > 0) {
646                                 DTDElementDeclaration elem = DTD.ElementDecls [elementStack.Peek () as string];
647                                 if (elem != null && !elem.IsInternalSubset && !elem.IsMixedContent && !elem.IsAny && !elem.IsEmpty)
648                                         HandleError ("In standalone document, whitespace cannot appear in an element whose declaration explicitly contains child content model, not Mixed content.", XmlSeverityType.Error);
649                         }
650                 }
651
652                 private XmlException NotWFError (string message)
653                 {
654                         return new XmlException (this as IXmlLineInfo, BaseURI, message);
655                 }
656
657                 private void HandleError (string message, XmlSeverityType severity)
658                 {
659                         if (validatingReader != null &&
660                                 validatingReader.ValidationType == ValidationType.None)
661                                 return;
662
663                         IXmlLineInfo info = this as IXmlLineInfo;
664                         bool hasLine = info.HasLineInfo ();
665                         XmlSchemaException ex = new XmlSchemaException (
666                                 message,
667                                 hasLine ? info.LineNumber : 0,
668                                 hasLine ? info.LinePosition : 0, 
669                                 null,
670                                 BaseURI, 
671                                 null);
672                         HandleError (ex, severity);
673                 }
674
675                 private void HandleError (XmlSchemaException ex, XmlSeverityType severity)
676                 {
677                         if (validatingReader != null &&
678                                 validatingReader.ValidationType == ValidationType.None)
679                                 return;
680
681                         if (validatingReader != null)
682                                 this.validatingReader.OnValidationEvent (this,
683                                         new ValidationEventArgs (ex, ex.Message, severity));
684                         else if (severity == XmlSeverityType.Error)
685                                 throw ex;
686                 }
687
688                 Stack attributeValueEntityStack = new Stack ();
689
690                 private void ValidateAttributes (DTDAttListDeclaration decl, bool validate)
691                 {
692                         while (reader.MoveToNextAttribute ()) {
693                                 string attrName = reader.Name;
694                                 this.currentAttribute = attrName;
695                                 attributes.Add (attrName);
696                                 attributeLocalNames.Add (attrName, reader.LocalName);
697                                 attributeNamespaces.Add (attrName, reader.NamespaceURI);
698                                 attributePrefixes.Add (attrName, reader.Prefix);
699                                 XmlReader targetReader = reader;
700                                 string attrValue = null;
701                                 // It always resolves entity references on attributes (documented as such).
702 //                              if (currentEntityHandling == EntityHandling.ExpandCharEntities)
703 //                                      attrValue = reader.Value;
704 //                              else
705                                 {
706                                         while (attributeValueEntityStack.Count >= 0) {
707                                                 if (!targetReader.ReadAttributeValue ()) {
708                                                         if (attributeValueEntityStack.Count > 0) {
709                                                                 targetReader = attributeValueEntityStack.Pop () as XmlReader;
710                                                                 continue;
711                                                         } else
712                                                                 break;
713                                                 }
714                                                 switch (targetReader.NodeType) {
715                                                 case XmlNodeType.EntityReference:
716                                                         DTDEntityDeclaration edecl = DTD.EntityDecls [targetReader.Name];
717                                                         if (edecl == null) {
718                                                                 HandleError (String.Format ("Referenced entity {0} is not declared.", targetReader.Name),
719                                                                         XmlSeverityType.Error);
720                                                         } else {
721                                                                 XmlTextReader etr = new XmlTextReader (edecl.EntityValue, XmlNodeType.Attribute, ParserContext);
722                                                                 attributeValueEntityStack.Push (targetReader);
723                                                                 targetReader = etr;
724                                                                 continue;
725                                                         }
726                                                         break;
727                                                 case XmlNodeType.EndEntity:
728                                                         break;
729                                                 default:
730                                                         if (attrValue != null) {
731                                                                 valueBuilder.Append (attrValue);
732                                                                 attrValue = null;
733                                                         }
734                                                         
735                                                         if (valueBuilder.Length != 0)
736                                                                 valueBuilder.Append (targetReader.Value);
737                                                         else
738                                                                 attrValue = targetReader.Value;
739                                                         
740                                                         break;
741                                                 }
742                                         }
743                                         
744                                         if (attrValue == null) {
745                                                 attrValue = valueBuilder.ToString ();
746                                                 valueBuilder.Length = 0;
747                                         }
748                                 }
749                                 reader.MoveToElement ();
750                                 reader.MoveToAttribute (attrName);
751                                 attributeValues.Add (attrName, attrValue);
752
753                                 if (!validate)
754                                         continue;
755
756                                 // Validation
757
758                                 DTDAttributeDefinition def = decl [reader.Name];
759                                 if (def == null) {
760                                         HandleError (String.Format ("Attribute {0} is not declared.", reader.Name),
761                                                 XmlSeverityType.Error);
762                                         continue;
763                                 }
764
765                                 // check enumeration constraint
766                                 if (def.EnumeratedAttributeDeclaration.Count > 0)
767                                         if (!def.EnumeratedAttributeDeclaration.Contains (
768                                                 FilterNormalization (reader.Name, attrValue)))
769                                                 HandleError (String.Format ("Attribute enumeration constraint error in attribute {0}, value {1}.",
770                                                         reader.Name, attrValue), XmlSeverityType.Error);
771                                 if (def.EnumeratedNotations.Count > 0)
772                                         if (!def.EnumeratedNotations.Contains (
773                                                 FilterNormalization (reader.Name, attrValue)))
774                                                 HandleError (String.Format ("Attribute notation enumeration constraint error in attribute {0}, value {1}.",
775                                                         reader.Name, attrValue), XmlSeverityType.Error);
776
777                                 // check type constraint
778                                 string normalized = null;
779                                 if (def.Datatype != null)
780                                         normalized = FilterNormalization (def.Name, attrValue);
781                                 else
782                                         normalized = attrValue;
783                                 DTDEntityDeclaration ent;
784
785                                 // Common process to get list value
786                                 string [] list = null;
787                                 switch (def.Datatype.TokenizedType) {
788                                 case XmlTokenizedType.IDREFS:
789                                 case XmlTokenizedType.ENTITIES:
790                                 case XmlTokenizedType.NMTOKENS:
791                                         try {
792                                                 list = def.Datatype.ParseValue (normalized, NameTable, null) as string [];
793                                         } catch (Exception) {
794                                                 HandleError ("Attribute value is invalid against its data type.", XmlSeverityType.Error);
795                                                 list = new string [0];
796                                         }
797                                         break;
798                                 default:
799                                         try {
800                                                 def.Datatype.ParseValue (normalized, NameTable, null);
801                                         } catch (Exception ex) {
802                                                 HandleError (String.Format ("Attribute value is invalid against its data type '{0}'. {1}", def.Datatype, ex.Message), XmlSeverityType.Error);
803                                         }
804                                         break;
805                                 }
806
807                                 switch (def.Datatype.TokenizedType) {
808                                 case XmlTokenizedType.ID:
809                                         if (this.idList.Contains (normalized)) {
810                                                 HandleError (String.Format ("Node with ID {0} was already appeared.", attrValue),
811                                                         XmlSeverityType.Error);
812                                         } else {
813                                                 if (missingIDReferences.Contains (normalized))
814                                                         missingIDReferences.Remove (normalized);
815                                                 idList.Add (normalized);
816                                         }
817                                         break;
818                                 case XmlTokenizedType.IDREF:
819                                         if (!idList.Contains (normalized))
820                                                 missingIDReferences.Add (normalized);
821                                         break;
822                                 case XmlTokenizedType.IDREFS:
823                                         for (int i = 0; i < list.Length; i++) {
824                                                 string idref = list [i];
825                                                 if (!idList.Contains (idref))
826                                                         missingIDReferences.Add (idref);
827                                         }
828                                         break;
829                                 case XmlTokenizedType.ENTITY:
830                                         ent = dtd.EntityDecls [normalized];
831                                         if (ent == null)
832                                                 HandleError ("Reference to undeclared entity was found in attribute: " + reader.Name + ".", XmlSeverityType.Error);
833                                         else if (ent.NotationName == null)
834                                                 HandleError ("The entity specified by entity type value must be an unparsed entity. The entity definition has no NDATA in attribute: " + reader.Name + ".", XmlSeverityType.Error);
835                                         break;
836                                 case XmlTokenizedType.ENTITIES:
837                                         for (int i = 0; i < list.Length; i++) {
838                                                 string entref = list [i];
839                                                 ent = dtd.EntityDecls [FilterNormalization (reader.Name, entref)];
840                                                 if (ent == null)
841                                                         HandleError ("Reference to undeclared entity was found in attribute: " + reader.Name + ".", XmlSeverityType.Error);
842                                                 else if (ent.NotationName == null)
843                                                         HandleError ("The entity specified by ENTITIES type value must be an unparsed entity. The entity definition has no NDATA in attribute: " + reader.Name + ".", XmlSeverityType.Error);
844                                         }
845                                         break;
846 //                              case XmlTokenizedType.NMTOKEN: nothing to do
847 //                              case XmlTokenizedType.NMTOKENS: nothing to do
848                                 }
849
850                                 if (isStandalone && !def.IsInternalSubset && 
851                                         attrValue != normalized)
852                                         HandleError ("In standalone document, attribute value characters must not be checked against external definition.", XmlSeverityType.Error);
853
854                                 if (def.OccurenceType == 
855                                         DTDAttributeOccurenceType.Fixed &&
856                                         attrValue != def.DefaultValue)
857                                         HandleError (String.Format ("Fixed attribute {0} in element {1} has invalid value {2}.",
858                                                 def.Name, decl.Name, attrValue),
859                                                 XmlSeverityType.Error);
860                         }
861
862                         if (validate)
863                                 VerifyDeclaredAttributes (decl);
864
865                         MoveToElement ();
866                 }
867
868                 private void VerifyDeclaredAttributes (DTDAttListDeclaration decl)
869                 {
870                         // Check if all required attributes exist, and/or
871                         // if there is default values, then add them.
872                         for (int i = 0; i < decl.Definitions.Count; i++) {
873                                 DTDAttributeDefinition def = (DTDAttributeDefinition) decl.Definitions [i];
874                                 if (attributes.Contains (def.Name))
875                                         continue;
876
877                                 if (def.OccurenceType == DTDAttributeOccurenceType.Required) {
878                                         HandleError (String.Format ("Required attribute {0} in element {1} not found .",
879                                                 def.Name, decl.Name),
880                                                 XmlSeverityType.Error);
881                                         continue;
882                                 }
883
884                                 else if (def.DefaultValue == null)
885                                         continue;
886
887                                 if (this.isStandalone && !def.IsInternalSubset)
888                                         HandleError ("In standalone document, external default value definition must not be applied.", XmlSeverityType.Error);
889
890                                 switch (validatingReader.ValidationType) {
891                                 case ValidationType.Auto:
892                                         if (validatingReader.Schemas.Count == 0)
893                                                 goto case ValidationType.DTD;
894                                         break;
895                                 case ValidationType.DTD:
896                                 case ValidationType.None:
897                                         // Other than them, ignore DTD defaults.
898                                         attributes.Add (def.Name);
899                                         int colonAt = def.Name.IndexOf (':');
900                                         attributeLocalNames.Add (def.Name,
901                                                 colonAt < 0 ? def.Name :
902                                                 def.Name.Substring (colonAt + 1));
903                                         string prefix = colonAt < 0 ?
904                                                 String.Empty :
905                                                 def.Name.Substring (0, colonAt);
906                                         attributePrefixes.Add (def.Name, prefix);
907                                         attributeNamespaces.Add (def.Name, reader.LookupNamespace (prefix));
908                                         attributeValues.Add (def.Name, def.DefaultValue);
909                                         break;
910                                 }
911                         }
912                 }
913
914                 public override bool ReadAttributeValue ()
915                 {
916                         if (consumedAttribute)
917                                 return false;
918                         if (NodeType == XmlNodeType.Attribute &&
919                                         currentEntityHandling == EntityHandling.ExpandEntities) {
920                                 consumedAttribute = true;
921                                 return true;
922                         }
923                         else if (IsDefault) {
924                                 consumedAttribute = true;
925                                 return true;
926                         }
927                         else
928                                 return reader.ReadAttributeValue ();
929                 }
930
931 #if NET_1_0
932                 public override string ReadInnerXml ()
933                 {
934                         // MS.NET 1.0 has a serious bug here. It skips validation.
935                         return reader.ReadInnerXml ();
936                 }
937
938                 public override string ReadOuterXml ()
939                 {
940                         // MS.NET 1.0 has a serious bug here. It skips validation.
941                         return reader.ReadOuterXml ();
942                 }
943 #endif
944
945                 public override string ReadString ()
946                 {
947                         // It seems to be the same as ReadInnerXml(). 
948                         return base.ReadStringInternal ();
949                 }
950
951                 public override void ResolveEntity ()
952                 {
953                         if (resolver == null)
954                                 return;
955
956                         // "reader." is required since NodeType must not be entityref by nature.
957                         if (reader.NodeType != XmlNodeType.EntityReference)
958                                 throw new InvalidOperationException ("The current node is not an Entity Reference");
959                         DTDEntityDeclaration entity = DTD != null ? DTD.EntityDecls [reader.Name] as DTDEntityDeclaration : null;
960
961                         XmlNodeType xmlReaderNodeType =
962                                 (currentAttribute != null) ? XmlNodeType.Attribute : XmlNodeType.Element;
963
964 #if NET_2_0
965                         if (entity == null)
966                                         throw NotWFError (String.Format ("Reference to undeclared entity '{0}'.", reader.Name));
967 #endif
968
969                         // MS.NET 1.x ignores undeclared entity reference here..
970                         if (entity != null && entity.SystemId != null) {
971                                 Uri baseUri = entity.BaseURI == String.Empty ? null : new Uri (entity.BaseURI);
972                                 Stream stream = resolver.GetEntity (resolver.ResolveUri (baseUri, entity.SystemId), null, typeof (Stream)) as Stream;
973                                 nextEntityReader = new XmlTextReader (stream, xmlReaderNodeType, ParserContext);
974                         } else {
975                                 string replacementText =
976                                         (entity != null) ? entity.EntityValue : String.Empty;
977                                 nextEntityReader = new XmlTextReader (replacementText, xmlReaderNodeType, ParserContext);
978                         }
979                         nextEntityReader.XmlResolver = resolver;
980                 }
981
982                 public override int AttributeCount {
983                         get {
984                                 if (currentTextValue != null)
985                                         return 0;
986
987                                 if (dtd == null || !insideContent)
988                                         return reader.AttributeCount;
989
990                                 return attributes.Count;
991                         }
992                 }
993
994                 public override string BaseURI {
995                         get {
996                                 return reader.BaseURI;
997                         }
998                 }
999
1000                 public override bool CanResolveEntity {
1001                         get { return true; }
1002                 }
1003
1004                 public override int Depth {
1005                         get {
1006                                 int baseNum = reader.Depth;
1007                                 if (entityReaderDepthStack.Count > 0) {
1008                                         baseNum += (int) entityReaderDepthStack.Peek ();
1009                                         if (NodeType != XmlNodeType.EndEntity)
1010                                                 baseNum++;
1011                                 }
1012                                 if (currentTextValue != null && reader.NodeType == XmlNodeType.EndElement)
1013                                         baseNum++;
1014
1015                                 return IsDefault ? baseNum + 1 : baseNum;
1016                         }
1017                 }
1018
1019                 public override bool EOF {
1020                         get { return reader.EOF && entityReaderStack.Count == 0; }
1021                 }
1022
1023                 public override bool HasValue {
1024                         get {
1025                                 return IsDefault ? true :
1026                                         currentTextValue != null ? true :
1027                                         reader.HasValue; }
1028                 }
1029
1030                 public override bool IsDefault {
1031                         get {
1032                                 if (currentTextValue != null)
1033                                         return false;
1034                                 if (currentAttribute == null)
1035                                         return false;
1036                                 return reader.GetAttribute (currentAttribute) == null;
1037                         }
1038                 }
1039
1040                 public override bool IsEmptyElement {
1041                         get {
1042                                 if (currentTextValue != null)
1043                                         return false;
1044                                 return reader.IsEmptyElement;
1045                         }
1046                 }
1047
1048                 public override string this [int i] {
1049                         get { return GetAttribute (i); }
1050                 }
1051
1052                 public override string this [string name] {
1053                         get { return GetAttribute (name); }
1054                 }
1055
1056                 public override string this [string name, string ns] {
1057                         get { return GetAttribute (name, ns); }
1058                 }
1059
1060                 public int LineNumber {
1061                         get {
1062                                 IXmlLineInfo info = reader as IXmlLineInfo;
1063                                 return (info != null) ? info.LineNumber : 0;
1064                         }
1065                 }
1066
1067                 public int LinePosition {
1068                         get {
1069                                 IXmlLineInfo info = reader as IXmlLineInfo;
1070                                 return (info != null) ? info.LinePosition : 0;
1071                         }
1072                 }
1073
1074                 public override string LocalName {
1075                         get {
1076                                 if (currentTextValue != null || consumedAttribute)
1077                                         return String.Empty;
1078                                 if (entityReaderStack.Count > 0 && reader.EOF)
1079                                         return ((XmlReader) entityReaderStack.Peek ()).LocalName; // name of EndEntity
1080                                 else if (NodeType == XmlNodeType.Attribute)
1081                                         return (string) attributeLocalNames [currentAttribute];
1082                                 else if (IsDefault)
1083                                         return String.Empty;
1084                                 else
1085                                         return reader.LocalName;
1086                         }
1087                 }
1088
1089                 public override string Name {
1090                         get {
1091                                 if (currentTextValue != null || consumedAttribute)
1092                                         return String.Empty;
1093                                 if (entityReaderStack.Count > 0 && reader.EOF)
1094                                         return ((XmlReader) entityReaderStack.Peek ()).Name; // name of EndEntity
1095                                 else if (NodeType == XmlNodeType.Attribute)
1096                                         return currentAttribute;
1097                                 else if (IsDefault)
1098                                         return String.Empty;
1099                                 else
1100                                         return reader.Name;
1101                         }
1102                 }
1103
1104                 public override string NamespaceURI {
1105                         get {
1106                                 if (currentTextValue != null || consumedAttribute)
1107                                         return String.Empty;
1108                                 else if (NodeType == XmlNodeType.Attribute)
1109                                         return (string) attributeNamespaces [currentAttribute];
1110                                 else if (IsDefault)
1111                                         return String.Empty;
1112                                 else if (nsdecls.Count > 0 && nsdecls [Prefix] != null)
1113                                         return (string) nsdecls [Prefix];
1114                                 else
1115                                         return reader.NamespaceURI;
1116                         }
1117                 }
1118
1119                 public override XmlNameTable NameTable {
1120                         get { return reader.NameTable; }
1121                 }
1122
1123                 public override XmlNodeType NodeType {
1124                         get {
1125                                 if (currentTextValue != null)
1126                                         return isSignificantWhitespace ? XmlNodeType.SignificantWhitespace :
1127                                                 isWhitespace ? XmlNodeType.Whitespace :
1128                                                 XmlNodeType.Text;
1129
1130                                 if (entityReaderStack.Count > 0 && reader.EOF)
1131                                         return XmlNodeType.EndEntity;
1132
1133                                 // If consumedAttribute is true, then entities must be resolved.
1134                                 return consumedAttribute ? XmlNodeType.Text :
1135                                         IsDefault ? XmlNodeType.Attribute :
1136                                         reader.NodeType;
1137                         }
1138                 }
1139
1140                 public XmlParserContext ParserContext {
1141                         get { return XmlSchemaUtil.GetParserContext (reader); }
1142                 }
1143
1144                 public override string Prefix {
1145                         get {
1146                                 if (currentTextValue != null || consumedAttribute)
1147                                         return String.Empty;
1148                                 else if (NodeType == XmlNodeType.Attribute)
1149                                         return (string) attributePrefixes [currentAttribute];
1150                                 else if (IsDefault)
1151                                         return String.Empty;
1152                                 else
1153                                         return reader.Prefix;
1154                         }
1155                 }
1156                 
1157                 public override char QuoteChar {
1158                         get {
1159                                 // If it is not actually on an attribute, then it returns
1160                                 // undefined value or '"'.
1161                                 return reader.QuoteChar;
1162                         }
1163                 }
1164
1165                 public override ReadState ReadState {
1166                         get {
1167                                 if (reader.ReadState == ReadState.EndOfFile && currentTextValue != null)
1168                                         return ReadState.Interactive;
1169                                 return reader.ReadState;
1170                         }
1171                 }
1172
1173                 public object SchemaType {
1174                         get {
1175                                 if (currentElement == null)
1176                                         return null;
1177                                 DTDAttListDeclaration decl =
1178                                         DTD.AttListDecls [currentElement];
1179                                 DTDAttributeDefinition def =
1180                                         decl != null ? decl [currentAttribute] : null;
1181                                 return def != null ? def.Datatype : null;
1182                         }
1183                 }
1184
1185                 char [] whitespaceChars = new char [] {' '};
1186                 private string FilterNormalization (string attrName, string rawValue)
1187                 {
1188                         if (DTD == null || NodeType != XmlNodeType.Attribute ||
1189                                 sourceTextReader == null ||
1190                                 !sourceTextReader.Normalization)
1191                                 return rawValue;
1192
1193                         DTDAttributeDefinition def = 
1194                                 dtd.AttListDecls [currentElement].Get (attrName);
1195                         valueBuilder.Append (rawValue);
1196                         valueBuilder.Replace ('\r', ' ');
1197                         valueBuilder.Replace ('\n', ' ');
1198                         valueBuilder.Replace ('\t', ' ');
1199                         try {
1200                                 if (def.Datatype.TokenizedType == XmlTokenizedType.CDATA)
1201                                         return valueBuilder.ToString ();
1202                                 
1203                                 for (int i=0; i < valueBuilder.Length; i++) {
1204                                         if (valueBuilder [i] != ' ')
1205                                                 continue;
1206                                         while (++i < valueBuilder.Length && valueBuilder [i] == ' ')
1207                                                 valueBuilder.Remove (i, 1);
1208                                 }
1209                                 return valueBuilder.ToString ().Trim (whitespaceChars);
1210                         } finally {
1211                                 valueBuilder.Length = 0;
1212                         }
1213                 }
1214
1215                 public override string Value {
1216                         get {
1217                                 if (currentTextValue != null)
1218                                         return currentTextValue;
1219                                 // This check also covers value node of default attributes.
1220                                 if (IsDefault) {
1221                                         DTDAttributeDefinition def = 
1222                                                 dtd.AttListDecls [currentElement] [currentAttribute] as DTDAttributeDefinition;
1223                                         return sourceTextReader != null && sourceTextReader.Normalization ?
1224                                                 def.NormalizedDefaultValue : def.DefaultValue;
1225                                 }
1226                                 // As to this property, MS.NET seems ignorant of EntityHandling...
1227                                 else if (NodeType == XmlNodeType.Attribute)// &&
1228                                         return FilterNormalization (Name, (string) attributeValues [currentAttribute]);
1229                                 else if (consumedAttribute)
1230                                         return FilterNormalization (Name, (string) attributeValues [this.currentAttribute]);
1231                                 else
1232                                         return FilterNormalization (Name, reader.Value);
1233                         }
1234                 }
1235
1236                 public override string XmlLang {
1237                         get {
1238                                 string val = this ["xml:lang"];
1239                                 return val != null ? val : reader.XmlLang;
1240                         }
1241                 }
1242
1243                 public XmlResolver XmlResolver {
1244                         set {
1245                                 resolver = value;
1246                         }
1247                 }
1248
1249                 public override XmlSpace XmlSpace {
1250                         get {
1251                                 string val = this ["xml:space"];
1252                                 switch (val) {
1253                                 case "preserve":
1254                                         return XmlSpace.Preserve;
1255                                 case "default":
1256                                         return XmlSpace.Default;
1257                                 default:
1258                                         return reader.XmlSpace;
1259                                 }
1260                         }
1261                 }
1262
1263         }
1264 }