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