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