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