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