* BuildEngine.cs (BuildProjectFile): Use AddProperty method to specify
[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                         switch (reader.ReadState) {
418                         case ReadState.Closed:
419                         case ReadState.Error:
420                         case ReadState.EndOfFile:
421                                 return false;
422                         }
423                         if (popScope) {
424                                 nsmgr.PopScope ();
425                                 popScope = false;
426                                 if (elementStack.Count == 0)
427                                 // it reached to the end of document element,
428                                 // so reset to non-validating state.
429                                         currentAutomata = null;
430                         }
431
432                         bool b = !reader.EOF;
433                         if (shouldResetCurrentTextValue) {
434                                 currentTextValue = null;
435                                 shouldResetCurrentTextValue = false;
436                         }
437                         else
438                                 b = reader.Read ();
439
440                         if (!b) {
441                                 if (elementStack.Count != 0)
442                                         throw new InvalidOperationException ("Unexpected end of XmlReader.");
443                                 return false;
444                         }
445
446                         return ProcessContent ();
447                 }
448
449                 bool ProcessContent ()
450                 {
451                         switch (reader.NodeType) {
452                         case XmlNodeType.XmlDeclaration:
453                                 FillAttributes ();
454                                 if (GetAttribute ("standalone") == "yes")
455                                         isStandalone = true;
456                                 break;
457
458                         case XmlNodeType.DocumentType:
459                                 ReadDoctype ();
460                                 break;
461
462                         case XmlNodeType.Element:
463                                 if (constructingTextValue != null) {
464                                         currentTextValue = constructingTextValue;
465                                         constructingTextValue = null;
466                                         if (isWhitespace)
467                                                 ValidateWhitespaceNode ();
468                                         return true;
469                                 }
470                                 ProcessStartElement ();
471                                 break;
472
473                         case XmlNodeType.EndElement:
474                                 if (constructingTextValue != null) {
475                                         currentTextValue = constructingTextValue;
476                                         constructingTextValue = null;
477                                         return true;
478                                 }
479                                 ProcessEndElement ();
480                                 break;
481
482                         case XmlNodeType.CDATA:
483                                 isSignificantWhitespace = isWhitespace = false;
484                                 isText = true;
485
486                                 ValidateText ();
487
488                                 if (currentTextValue != null) {
489                                         currentTextValue = constructingTextValue;
490                                         constructingTextValue = null;
491                                         return true;
492                                 }
493                                 break;
494                         case XmlNodeType.SignificantWhitespace:
495                                 if (!isText)
496                                         isSignificantWhitespace = true;
497                                 isWhitespace = false;
498                                 goto case XmlNodeType.DocumentFragment;
499                         case XmlNodeType.Text:
500                                 isWhitespace = isSignificantWhitespace = false;
501                                 isText = true;
502                                 goto case XmlNodeType.DocumentFragment;
503                         case XmlNodeType.DocumentFragment:
504                                 // it should not happen, but in case if
505                                 // XmlReader really returns it, just ignore.
506                                 if (reader.NodeType == XmlNodeType.DocumentFragment)
507                                         break;
508
509                                 ValidateText ();
510
511                                 break;
512                         case XmlNodeType.Whitespace:
513                                 if (!isText && !isSignificantWhitespace)
514                                         isWhitespace = true;
515                                 goto case XmlNodeType.DocumentFragment;
516                         }
517                         if (isWhitespace)
518                                 ValidateWhitespaceNode ();
519                         currentTextValue = constructingTextValue;
520                         constructingTextValue = null;
521                         return true;
522                 }
523
524                 private void FillAttributes ()
525                 {
526                         if (reader.MoveToFirstAttribute ()) {
527                                 do {
528                                         AttributeSlot slot = GetAttributeSlot ();
529                                         slot.Name = reader.Name;
530                                         slot.LocalName = reader.LocalName;
531                                         slot.Prefix = reader.Prefix;
532                                         slot.NS = reader.NamespaceURI;
533                                         slot.Value = reader.Value;
534                                 } while (reader.MoveToNextAttribute ());
535                                 reader.MoveToElement ();
536                         }
537                 }
538
539                 private void ValidateText ()
540                 {
541                         if (currentAutomata == null)
542                                 return;
543
544                         DTDElementDeclaration elem = null;
545                         if (elementStack.Count > 0)
546                                 elem = dtd.ElementDecls [elementStack.Peek () as string];
547                         // Here element should have been already validated, so
548                         // if no matching declaration is found, simply ignore.
549                         if (elem != null && !elem.IsMixedContent && !elem.IsAny && !isWhitespace) {
550                                 HandleError (String.Format ("Current element {0} does not allow character data content.", elementStack.Peek () as string),
551                                         XmlSeverityType.Error);
552                                 currentAutomata = previousAutomata;
553                         }
554                 }
555
556                 private void ValidateWhitespaceNode ()
557                 {
558                         // VC Standalone Document Declaration (2.9)
559                         if (this.isStandalone && DTD != null && elementStack.Count > 0) {
560                                 DTDElementDeclaration elem = DTD.ElementDecls [elementStack.Peek () as string];
561                                 if (elem != null && !elem.IsInternalSubset && !elem.IsMixedContent && !elem.IsAny && !elem.IsEmpty)
562                                         HandleError ("In a standalone document, whitespace cannot appear in an element which is declared to contain only element children.", XmlSeverityType.Error);
563                         }
564                 }
565
566                 private void HandleError (string message, XmlSeverityType severity)
567                 {
568                         if (validatingReader != null &&
569                                 validatingReader.ValidationType == ValidationType.None)
570                                 return;
571
572                         IXmlLineInfo info = this as IXmlLineInfo;
573                         bool hasLine = info.HasLineInfo ();
574                         XmlSchemaException ex = new XmlSchemaException (
575                                 message,
576                                 hasLine ? info.LineNumber : 0,
577                                 hasLine ? info.LinePosition : 0, 
578                                 null,
579                                 BaseURI, 
580                                 null);
581                         HandleError (ex, severity);
582                 }
583
584                 private void HandleError (XmlSchemaException ex, XmlSeverityType severity)
585                 {
586                         if (validatingReader != null &&
587                                 validatingReader.ValidationType == ValidationType.None)
588                                 return;
589
590                         if (validatingReader != null)
591                                 this.validatingReader.OnValidationEvent (this,
592                                         new ValidationEventArgs (ex, ex.Message, severity));
593                         else if (severity == XmlSeverityType.Error)
594                                 throw ex;
595                 }
596
597                 private void ValidateAttributes (DTDAttListDeclaration decl, bool validate)
598                 {
599                         DtdValidateAttributes (decl, validate);
600
601                         for (int i = 0; i < attributeCount; i++) {
602                                 AttributeSlot slot = attributes [i];
603                                 if (slot.Name == "xmlns" || slot.Prefix == "xmlns")
604                                         nsmgr.AddNamespace (
605                                                 slot.Prefix == "xmlns" ? slot.LocalName : String.Empty,
606                                                 slot.Value);
607                         }
608
609                         for (int i = 0; i < attributeCount; i++) {
610                                 AttributeSlot slot = attributes [i];
611                                 if (slot.Name == "xmlns")
612                                         slot.NS = XmlNamespaceManager.XmlnsXmlns;
613                                 else if (slot.Prefix.Length > 0)
614                                         slot.NS = LookupNamespace (slot.Prefix);
615                                 else
616                                         slot.NS = String.Empty;
617                         }
618                 }
619
620                 AttributeSlot GetAttributeSlot ()
621                 {
622                         if (attributeCount == attributes.Length) {
623                                 AttributeSlot [] tmp = new AttributeSlot [attributeCount << 1];
624                                 Array.Copy (attributes, tmp, attributeCount);
625                                 attributes = tmp;
626                         }
627                         if (attributes [attributeCount] == null)
628                                 attributes [attributeCount] = new AttributeSlot ();
629                         AttributeSlot slot = attributes [attributeCount];
630                         slot.Clear ();
631                         attributeCount++;
632                         return slot;
633                 }
634
635                 private void DtdValidateAttributes (DTDAttListDeclaration decl, bool validate)
636                 {
637                         while (reader.MoveToNextAttribute ()) {
638                                 string attrName = reader.Name;
639                                 AttributeSlot slot = GetAttributeSlot ();
640                                 slot.Name = reader.Name;
641                                 slot.LocalName = reader.LocalName;
642                                 slot.Prefix = reader.Prefix;
643                                 XmlReader targetReader = reader;
644                                 string attrValue = String.Empty;
645                                 // For attribute node, it always resolves
646                                 // entity references on attributes.
647                                 while (attributeValueEntityStack.Count >= 0) {
648                                         if (!targetReader.ReadAttributeValue ()) {
649                                                 if (attributeValueEntityStack.Count > 0) {
650                                                         targetReader = attributeValueEntityStack.Pop () as XmlReader;
651                                                         continue;
652                                                 } else
653                                                         break;
654                                         }
655                                         switch (targetReader.NodeType) {
656                                         case XmlNodeType.EntityReference:
657                                                 DTDEntityDeclaration edecl = DTD.EntityDecls [targetReader.Name];
658                                                 if (edecl == null) {
659                                                         HandleError (String.Format ("Referenced entity {0} is not declared.", targetReader.Name),
660                                                                 XmlSeverityType.Error);
661                                                 } else {
662                                                         XmlTextReader etr = new XmlTextReader (edecl.EntityValue, XmlNodeType.Attribute, ParserContext);
663                                                         attributeValueEntityStack.Push (targetReader);
664                                                         targetReader = etr;
665                                                         continue;
666                                                 }
667                                                 break;
668                                         case XmlNodeType.EndEntity:
669                                                 break;
670                                         default:
671                                                 attrValue += targetReader.Value;
672                                                 break;
673                                         }
674                                 }
675                                 
676                                 reader.MoveToElement ();
677                                 reader.MoveToAttribute (attrName);
678                                 slot.Value = FilterNormalization (attrName, attrValue);
679
680                                 if (!validate)
681                                         continue;
682
683                                 // Validation
684
685                                 DTDAttributeDefinition def = decl [reader.Name];
686                                 if (def == null) {
687                                         HandleError (String.Format ("Attribute {0} is not declared.", reader.Name),
688                                                 XmlSeverityType.Error);
689                                         continue;
690                                 }
691
692                                 // check enumeration constraint
693                                 if (def.EnumeratedAttributeDeclaration.Count > 0)
694                                         if (!def.EnumeratedAttributeDeclaration.Contains (slot.Value))
695                                                 HandleError (String.Format ("Attribute enumeration constraint error in attribute {0}, value {1}.",
696                                                         reader.Name, attrValue), XmlSeverityType.Error);
697                                 if (def.EnumeratedNotations.Count > 0)
698                                         if (!def.EnumeratedNotations.Contains (
699                                                 slot.Value))
700                                                 HandleError (String.Format ("Attribute notation enumeration constraint error in attribute {0}, value {1}.",
701                                                         reader.Name, attrValue), XmlSeverityType.Error);
702
703                                 // check type constraint
704                                 string normalized = null;
705                                 if (def.Datatype != null)
706                                         normalized = FilterNormalization (def.Name, attrValue);
707                                 else
708                                         normalized = attrValue;
709                                 DTDEntityDeclaration ent;
710
711                                 // Common process to get list value
712                                 string [] list = null;
713                                 switch (def.Datatype.TokenizedType) {
714                                 case XmlTokenizedType.IDREFS:
715                                 case XmlTokenizedType.ENTITIES:
716                                 case XmlTokenizedType.NMTOKENS:
717                                         try {
718                                                 list = def.Datatype.ParseValue (normalized, NameTable, null) as string [];
719                                         } catch (Exception) {
720                                                 HandleError ("Attribute value is invalid against its data type.", XmlSeverityType.Error);
721                                                 list = new string [0];
722                                         }
723                                         break;
724                                 default:
725                                         try {
726                                                 def.Datatype.ParseValue (normalized, NameTable, null);
727                                         } catch (Exception ex) {
728                                                 HandleError (String.Format ("Attribute value is invalid against its data type '{0}'. {1}", def.Datatype, ex.Message), XmlSeverityType.Error);
729                                         }
730                                         break;
731                                 }
732
733                                 switch (def.Datatype.TokenizedType) {
734                                 case XmlTokenizedType.ID:
735                                         if (this.idList.Contains (normalized)) {
736                                                 HandleError (String.Format ("Node with ID {0} was already appeared.", attrValue),
737                                                         XmlSeverityType.Error);
738                                         } else {
739                                                 if (missingIDReferences.Contains (normalized))
740                                                         missingIDReferences.Remove (normalized);
741                                                 idList.Add (normalized);
742                                         }
743                                         break;
744                                 case XmlTokenizedType.IDREF:
745                                         if (!idList.Contains (normalized))
746                                                 missingIDReferences.Add (normalized);
747                                         break;
748                                 case XmlTokenizedType.IDREFS:
749                                         for (int i = 0; i < list.Length; i++) {
750                                                 string idref = list [i];
751                                                 if (!idList.Contains (idref))
752                                                         missingIDReferences.Add (idref);
753                                         }
754                                         break;
755                                 case XmlTokenizedType.ENTITY:
756                                         ent = dtd.EntityDecls [normalized];
757                                         if (ent == null)
758                                                 HandleError ("Reference to undeclared entity was found in attribute: " + reader.Name + ".", XmlSeverityType.Error);
759                                         else if (ent.NotationName == null)
760                                                 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);
761                                         break;
762                                 case XmlTokenizedType.ENTITIES:
763                                         for (int i = 0; i < list.Length; i++) {
764                                                 string entref = list [i];
765                                                 ent = dtd.EntityDecls [FilterNormalization (reader.Name, entref)];
766                                                 if (ent == null)
767                                                         HandleError ("Reference to undeclared entity was found in attribute: " + reader.Name + ".", XmlSeverityType.Error);
768                                                 else if (ent.NotationName == null)
769                                                         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);
770                                         }
771                                         break;
772 //                              case XmlTokenizedType.NMTOKEN: nothing to do
773 //                              case XmlTokenizedType.NMTOKENS: nothing to do
774                                 }
775
776                                 if (isStandalone && !def.IsInternalSubset && 
777                                         attrValue != normalized)
778                                         HandleError ("In standalone document, attribute value characters must not be checked against external definition.", XmlSeverityType.Error);
779
780                                 if (def.OccurenceType == 
781                                         DTDAttributeOccurenceType.Fixed &&
782                                         attrValue != def.DefaultValue)
783                                         HandleError (String.Format ("Fixed attribute {0} in element {1} has invalid value {2}.",
784                                                 def.Name, decl.Name, attrValue),
785                                                 XmlSeverityType.Error);
786                         }
787
788                         if (validate)
789                                 VerifyDeclaredAttributes (decl);
790
791                         MoveToElement ();
792                 }
793
794                 void ReadDoctype ()
795                 {
796                         FillAttributes ();
797
798                         IHasXmlParserContext ctx = reader as IHasXmlParserContext;
799                         if (ctx != null)
800                                 dtd = ctx.ParserContext.Dtd;
801                         if (dtd == null) {
802                                 XmlTextReaderImpl xmlTextReader = new XmlTextReaderImpl ("", XmlNodeType.Document, null);
803                                 xmlTextReader.XmlResolver = resolver;
804                                 xmlTextReader.GenerateDTDObjectModel (reader.Name,
805                                         reader ["PUBLIC"], reader ["SYSTEM"], reader.Value);
806                                 dtd = xmlTextReader.DTD;
807                         }
808                         currentAutomata = dtd.RootAutomata;
809
810                         // Validity Constraint Check.
811                         for (int i = 0; i < DTD.Errors.Length; i++)
812                                 HandleError (DTD.Errors [i].Message, XmlSeverityType.Error);
813
814                         // NData target exists.
815                         foreach (DTDEntityDeclaration ent in dtd.EntityDecls.Values)
816                                 if (ent.NotationName != null && dtd.NotationDecls [ent.NotationName] == null)
817                                         this.HandleError ("Target notation was not found for NData in entity declaration " + ent.Name + ".",
818                                                 XmlSeverityType.Error);
819                         // NOTATION exists for attribute default values
820                         foreach (DTDAttListDeclaration attListIter in dtd.AttListDecls.Values) {
821                                 foreach (DTDAttributeDefinition def in attListIter.Definitions) {
822                                         if (def.Datatype.TokenizedType != XmlTokenizedType.NOTATION)
823                                                 continue;
824                                         foreach (string notation in def.EnumeratedNotations)
825                                                 if (dtd.NotationDecls [notation] == null)
826                                                         this.HandleError ("Target notation was not found for NOTATION typed attribute default " + def.Name + ".",
827                                                                 XmlSeverityType.Error);
828                                 }
829                         }
830                 }
831
832                 void ProcessStartElement ()
833                 {
834                         nsmgr.PushScope ();
835                         popScope = reader.IsEmptyElement;
836                         elementStack.Push (reader.Name);
837
838                         currentElement = Name;
839
840                         // If no DTD, skip validation.
841                         if (currentAutomata == null) {
842                                 ValidateAttributes (null, false);
843                                 if (reader.IsEmptyElement)
844                                         ProcessEndElement ();
845                                 return;
846                         }
847
848                         // StartElementDeriv
849
850                         previousAutomata = currentAutomata;
851                         currentAutomata = currentAutomata.TryStartElement (reader.Name);
852                         if (currentAutomata == DTD.Invalid) {
853                                 HandleError (String.Format ("Invalid start element found: {0}", reader.Name),
854                                         XmlSeverityType.Error);
855                                 currentAutomata = previousAutomata;
856                         }
857
858                         DTDElementDeclaration elem =
859                                 DTD.ElementDecls [reader.Name];
860                         if (elem == null) {
861                                 HandleError (String.Format ("Element {0} is not declared.", reader.Name),
862                                         XmlSeverityType.Error);
863                                 currentAutomata = previousAutomata;
864                         }
865
866                         automataStack.Push (currentAutomata);
867                         if (elem != null)       // i.e. not invalid
868                                 currentAutomata = elem.ContentModel.GetAutomata ();
869
870                         DTDAttListDeclaration attList = dtd.AttListDecls [currentElement];
871                         if (attList != null) {
872                                 // check attributes
873                                 ValidateAttributes (attList, true);
874                                 currentAttribute = -1;
875                         } else {
876                                 if (reader.HasAttributes) {
877                                         HandleError (String.Format (
878                                                 "Attributes are found on element {0} while it has no attribute definitions.", currentElement),
879                                                 XmlSeverityType.Error);
880                                 }
881                                 // SetupValidityIgnorantAttributes ();
882                                 ValidateAttributes (null, false);
883                         }
884                         // If it is empty element then directly check end element.
885                         if (reader.IsEmptyElement)
886                                 ProcessEndElement ();
887                 }
888
889                 void ProcessEndElement ()
890                 {
891                         popScope = true;
892                         elementStack.Pop ();
893
894                         // If no schema specification, then skip validation.
895                         if (currentAutomata == null)
896                                 return;
897
898                         // EndElementDeriv
899                         DTDElementDeclaration elem =
900                                 DTD.ElementDecls [reader.Name];
901                         if (elem == null) {
902                                 HandleError (String.Format ("Element {0} is not declared.", reader.Name),
903                                         XmlSeverityType.Error);
904                         }
905
906                         previousAutomata = currentAutomata;
907                         // Don't let currentAutomata
908                         DTDAutomata tmpAutomata = currentAutomata.TryEndElement ();
909                         if (tmpAutomata == DTD.Invalid) {
910                                 HandleError (String.Format ("Invalid end element found: {0}", reader.Name),
911                                         XmlSeverityType.Error);
912                                 currentAutomata = previousAutomata;
913                         }
914
915                         currentAutomata = automataStack.Pop () as DTDAutomata;
916                 }
917
918                 void VerifyDeclaredAttributes (DTDAttListDeclaration decl)
919                 {
920                         // Check if all required attributes exist, and/or
921                         // if there is default values, then add them.
922                         for (int i = 0; i < decl.Definitions.Count; i++) {
923                                 DTDAttributeDefinition def = (DTDAttributeDefinition) decl.Definitions [i];
924                                 bool exists = false;
925                                 for (int a = 0; a < attributeCount; a++) {
926                                         if (attributes [a].Name == def.Name) {
927                                                 exists = true;
928                                                 break;
929                                         }
930                                 }
931                                 if (exists)
932                                         continue;
933
934                                 if (def.OccurenceType == DTDAttributeOccurenceType.Required) {
935                                         HandleError (String.Format ("Required attribute {0} in element {1} not found .",
936                                                 def.Name, decl.Name),
937                                                 XmlSeverityType.Error);
938                                         continue;
939                                 }
940
941                                 else if (def.DefaultValue == null)
942                                         continue;
943
944                                 if (this.isStandalone && !def.IsInternalSubset)
945                                         HandleError ("In standalone document, external default value definition must not be applied.", XmlSeverityType.Error);
946
947                                 switch (validatingReader.ValidationType) {
948                                 case ValidationType.Auto:
949                                         if (validatingReader.Schemas.Count == 0)
950                                                 goto case ValidationType.DTD;
951                                         break;
952                                 case ValidationType.DTD:
953                                 case ValidationType.None:
954                                         // Other than them, ignore DTD defaults.
955                                         AttributeSlot slot = GetAttributeSlot ();
956                                         slot.Name = def.Name;
957                                         int colonAt = def.Name.IndexOf (':');
958                                         slot.LocalName = colonAt < 0 ? def.Name :
959                                                 def.Name.Substring (colonAt + 1);
960                                         string prefix = colonAt < 0 ?
961                                                 String.Empty :
962                                                 def.Name.Substring (0, colonAt);
963                                         slot.Prefix = prefix;
964                                         slot.Value = def.DefaultValue;
965                                         slot.IsDefault = true;
966                                         break;
967                                 }
968                         }
969                 }
970
971 #if NET_2_1
972                 internal
973 #else
974                 public
975 #endif
976                 override bool ReadAttributeValue ()
977                 {
978                         if (consumedAttribute)
979                                 return false;
980                         if (NodeType == XmlNodeType.Attribute &&
981                             EntityHandling == EntityHandling.ExpandEntities) {
982                                 consumedAttribute = true;
983                                 return true;
984                         }
985                         else if (IsDefault) {
986                                 consumedAttribute = true;
987                                 return true;
988                         }
989                         else
990                                 return reader.ReadAttributeValue ();
991                 }
992
993 #if NET_2_1
994                 internal
995 #else
996                 public
997 #endif
998                 override void ResolveEntity ()
999                 {
1000                         reader.ResolveEntity ();
1001                 }
1002
1003                 public override int AttributeCount {
1004                         get {
1005                                 if (currentTextValue != null)
1006                                         return 0;
1007
1008                                 return attributeCount;
1009                         }
1010                 }
1011
1012                 public override string BaseURI {
1013                         get {
1014                                 return reader.BaseURI;
1015                         }
1016                 }
1017
1018                 public override bool CanResolveEntity {
1019                         get { return true; }
1020                 }
1021
1022                 public override int Depth {
1023                         get {
1024                                 int baseNum = reader.Depth;
1025                                 if (currentTextValue != null && reader.NodeType == XmlNodeType.EndElement)
1026                                         baseNum++;
1027
1028                                 return IsDefault ? baseNum + 1 : baseNum;
1029                         }
1030                 }
1031
1032                 public override bool EOF {
1033                         get { return reader.EOF; }
1034                 }
1035
1036 #if NET_2_1
1037                 internal
1038 #else
1039                 public
1040 #endif
1041                 override bool HasValue {
1042                         get {
1043                                 return currentAttribute >= 0 ? true :
1044                                         currentTextValue != null ? true :
1045                                         reader.HasValue;
1046                         }
1047                 }
1048
1049                 public override bool IsDefault {
1050                         get {
1051                                 if (currentTextValue != null)
1052                                         return false;
1053                                 if (currentAttribute == -1)
1054                                         return false;
1055                                 return attributes [currentAttribute].IsDefault;
1056                         }
1057                 }
1058
1059                 public override bool IsEmptyElement {
1060                         get {
1061                                 if (currentTextValue != null)
1062                                         return false;
1063                                 return reader.IsEmptyElement;
1064                         }
1065                 }
1066
1067                 public override string this [int i] {
1068                         get { return GetAttribute (i); }
1069                 }
1070
1071                 public override string this [string name] {
1072                         get { return GetAttribute (name); }
1073                 }
1074
1075                 public override string this [string name, string ns] {
1076                         get { return GetAttribute (name, ns); }
1077                 }
1078
1079                 public int LineNumber {
1080                         get {
1081                                 IXmlLineInfo info = reader as IXmlLineInfo;
1082                                 return (info != null) ? info.LineNumber : 0;
1083                         }
1084                 }
1085
1086                 public int LinePosition {
1087                         get {
1088                                 IXmlLineInfo info = reader as IXmlLineInfo;
1089                                 return (info != null) ? info.LinePosition : 0;
1090                         }
1091                 }
1092
1093                 public override string LocalName {
1094                         get {
1095                                 if (currentTextValue != null || consumedAttribute)
1096                                         return String.Empty;
1097                                 else if (NodeType == XmlNodeType.Attribute)
1098                                         return attributes [currentAttribute].LocalName;
1099                                 else
1100                                         return reader.LocalName;
1101                         }
1102                 }
1103
1104                 public override string Name {
1105                         get {
1106                                 if (currentTextValue != null || consumedAttribute)
1107                                         return String.Empty;
1108                                 else if (NodeType == XmlNodeType.Attribute)
1109                                         return attributes [currentAttribute].Name;
1110                                 else
1111                                         return reader.Name;
1112                         }
1113                 }
1114
1115                 public override string NamespaceURI {
1116                         get {
1117                                 if (currentTextValue != null || consumedAttribute)
1118                                         return String.Empty;
1119                                 switch (NodeType) {
1120                                 case XmlNodeType.Attribute:
1121                                         return (string) attributes [currentAttribute].NS;
1122                                 case XmlNodeType.Element:
1123                                 case XmlNodeType.EndElement:
1124                                         return nsmgr.LookupNamespace (Prefix);
1125                                 default:
1126                                         return String.Empty;
1127                                 }
1128                         }
1129                 }
1130
1131                 public override XmlNameTable NameTable {
1132                         get { return reader.NameTable; }
1133                 }
1134
1135                 public override XmlNodeType NodeType {
1136                         get {
1137                                 if (currentTextValue != null)
1138                                         return isSignificantWhitespace ? XmlNodeType.SignificantWhitespace :
1139                                                 isWhitespace ? XmlNodeType.Whitespace :
1140                                                 XmlNodeType.Text;
1141
1142                                 // If consumedAttribute is true, then entities must be resolved.
1143                                 return consumedAttribute ? XmlNodeType.Text :
1144                                         IsDefault ? XmlNodeType.Attribute :
1145                                         reader.NodeType;
1146                         }
1147                 }
1148
1149                 public XmlParserContext ParserContext {
1150                         get { return XmlSchemaUtil.GetParserContext (reader); }
1151                 }
1152
1153                 public override string Prefix {
1154                         get {
1155                                 if (currentTextValue != null || consumedAttribute)
1156                                         return String.Empty;
1157                                 else if (NodeType == XmlNodeType.Attribute)
1158                                         return attributes [currentAttribute].Prefix;
1159                                 else
1160                                         return reader.Prefix;
1161                         }
1162                 }
1163                 
1164                 public override char QuoteChar {
1165                         get {
1166                                 // If it is not actually on an attribute, then it returns
1167                                 // undefined value or '"'.
1168                                 return reader.QuoteChar;
1169                         }
1170                 }
1171
1172                 public override ReadState ReadState {
1173                         get {
1174                                 if (reader.ReadState == ReadState.EndOfFile && currentTextValue != null)
1175                                         return ReadState.Interactive;
1176                                 return reader.ReadState;
1177                         }
1178                 }
1179
1180                 public object SchemaType {
1181                         get {
1182                                 if (DTD == null || currentAttribute == -1 ||
1183                                     currentElement == null)
1184                                         return null;
1185                                 DTDAttListDeclaration decl =
1186                                         DTD.AttListDecls [currentElement];
1187                                 DTDAttributeDefinition def =
1188                                         decl != null ? decl [attributes [currentAttribute].Name] : null;
1189                                 return def != null ? def.Datatype : null;
1190                         }
1191                 }
1192
1193                 char [] whitespaceChars = new char [] {' '};
1194                 private string FilterNormalization (string attrName, string rawValue)
1195                 {
1196                         if (DTD == null || sourceTextReader == null ||
1197                             !sourceTextReader.Normalization)
1198                                 return rawValue;
1199
1200                         DTDAttributeDefinition def = 
1201                                 dtd.AttListDecls [currentElement].Get (attrName);
1202                         valueBuilder.Append (rawValue);
1203                         valueBuilder.Replace ('\r', ' ');
1204                         valueBuilder.Replace ('\n', ' ');
1205                         valueBuilder.Replace ('\t', ' ');
1206                         try {
1207                                 if (def == null || def.Datatype.TokenizedType == XmlTokenizedType.CDATA)
1208                                         return valueBuilder.ToString ();
1209                                 for (int i=0; i < valueBuilder.Length; i++) {
1210                                         if (valueBuilder [i] != ' ')
1211                                                 continue;
1212                                         while (++i < valueBuilder.Length && valueBuilder [i] == ' ')
1213                                                 valueBuilder.Remove (i, 1);
1214                                 }
1215                                 return valueBuilder.ToString ().Trim (whitespaceChars);
1216                         } finally {
1217                                 valueBuilder.Length = 0;
1218                         }
1219                 }
1220
1221                 // LAMESPEC: When source XmlTextReader.Normalize is true, then
1222                 // every Attribute node is normalized. However, corresponding
1223                 // Values of attribute value Text nodes are not.
1224                 public override string Value {
1225                         get {
1226                                 if (currentTextValue != null)
1227                                         return currentTextValue;
1228                                 // As to this property, MS.NET seems ignorant of EntityHandling...
1229                                 else if (NodeType == XmlNodeType.Attribute
1230                                         // It also covers default attribute text.
1231                                         || consumedAttribute)
1232                                         return attributes [currentAttribute].Value;
1233                                 else
1234                                         return reader.Value;
1235                         }
1236                 }
1237
1238                 public override string XmlLang {
1239                         get {
1240                                 string val = this ["xml:lang"];
1241                                 return val != null ? val : reader.XmlLang;
1242                         }
1243                 }
1244
1245                 internal XmlResolver Resolver {
1246                         get { return resolver; }
1247                 }
1248
1249                 public XmlResolver XmlResolver {
1250                         set {
1251                                 if (dtd != null)
1252                                         dtd.XmlResolver = value;
1253                                 resolver = value;
1254                         }
1255                 }
1256
1257                 public override XmlSpace XmlSpace {
1258                         get {
1259                                 string val = this ["xml:space"];
1260                                 switch (val) {
1261                                 case "preserve":
1262                                         return XmlSpace.Preserve;
1263                                 case "default":
1264                                         return XmlSpace.Default;
1265                                 default:
1266                                         return reader.XmlSpace;
1267                                 }
1268                         }
1269                 }
1270
1271         }
1272 }