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