2010-04-07 Jb Evain <jbevain@novell.com>
[mcs.git] / class / System.XML / System.Xml / DTDValidatingReader2.cs
blobbfc183beb7d5fe7b10e0e678c7ce3ca213df04d7
1 //
2 // DTDValidatingReader2.cs
3 //
4 // Author:
5 // Atsushi Enomoto atsushi@ximian.com
6 //
7 // (C)2003 Atsushi Enomoto
8 // (C)2004-2006 Novell Inc.
9 //
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.
34 Some notes:
36 DTDValidatingReader requires somewhat different ResolveEntity()
37 implementation because unlike other readers (XmlTextReaderImpl and
38 XmlNodeReaderImpl), DTDValidatingReader manages validation state
39 and it must not be held in each entity reader.
41 Say, if there are such element and entity definitions:
43 <!ELEMENT root (child)>
44 <!ELEMENT child EMPTY>
45 <!ENTITY foo "<child />">
47 and an instance
49 <root>&foo;</root>
51 When the container XmlReader encounters "&foo;", it creates another
52 XmlReader for resolved entity "<child/>". However, the generated
53 reader must not be another standalone DTDValidatingReader since
54 <child/> must be a participant of the container's validation.
56 Thus, this reader handles validation, and it holds an
57 EntityResolvingXmlReader as its validation source XmlReader.
59 TODOs:
60 IsDefault messes all around the reader, so simplify it.
61 isWhitespace/isText/blah mess the code too, so clear it as well.
65 using System;
66 using System.Collections;
67 #if NET_2_0
68 using System.Collections.Generic;
69 #endif
70 using System.IO;
71 using System.Text;
72 using System.Xml;
73 using System.Xml.Schema;
75 #if NET_2_0
76 using XmlTextReaderImpl = Mono.Xml2.XmlTextReader;
77 #else
78 using XmlTextReaderImpl = System.Xml.XmlTextReader;
79 #endif
82 namespace Mono.Xml
84 internal class DTDValidatingReader : XmlReader, IXmlLineInfo,
85 #if NET_2_0
86 IXmlNamespaceResolver,
87 #endif
88 IHasXmlParserContext, IHasXmlSchemaInfo
90 public DTDValidatingReader (XmlReader reader)
91 : this (reader, null)
95 internal DTDValidatingReader (XmlReader reader,
96 XmlValidatingReader validatingReader)
98 this.reader = new EntityResolvingXmlReader (reader);
99 this.sourceTextReader = reader as XmlTextReader;
100 elementStack = new Stack ();
101 automataStack = new Stack ();
102 attributes = new AttributeSlot [10];
103 nsmgr = new XmlNamespaceManager (reader.NameTable);
104 this.validatingReader = validatingReader;
105 valueBuilder = new StringBuilder ();
106 idList = new ArrayList ();
107 missingIDReferences = new ArrayList ();
108 XmlTextReader xtReader = reader as XmlTextReader;
109 if (xtReader != null) {
110 resolver = xtReader.Resolver;
112 else
113 resolver = new XmlUrlResolver ();
116 // The primary xml source
117 EntityResolvingXmlReader reader;
119 // This is used to acquire "Normalization" property which
120 // could be dynamically changed.
121 XmlTextReader sourceTextReader;
123 // This field is used to get properties (such as
124 // EntityHandling) and to raise events.
125 XmlValidatingReader validatingReader;
127 // We hold DTDObjectModel for such case that the source
128 // XmlReader does not implement IHasXmlParerContext
129 // (especially for non-sys.xml.dll readers).
130 DTDObjectModel dtd;
132 // Used to resolve entities (as expected)
133 XmlResolver resolver;
135 // mainly used to retrieve DTDElementDeclaration
136 string currentElement;
137 AttributeSlot [] attributes;
138 int attributeCount;
140 // Holds MoveTo*Attribute()/ReadAttributeValue() status.
141 int currentAttribute = -1;
142 bool consumedAttribute;
144 // Ancestor and current node context for each depth.
145 Stack elementStack;
146 Stack automataStack;
147 bool popScope;
149 // Validation context.
150 bool isStandalone;
151 DTDAutomata currentAutomata;
152 DTDAutomata previousAutomata;
153 ArrayList idList;
154 ArrayList missingIDReferences;
156 // Holds namespace context. It must not be done in source
157 // XmlReader because default attributes could affect on it.
158 XmlNamespaceManager nsmgr;
160 // Those fields are used to store on-constructing text value.
161 // They are required to support entity-mixed text, so they
162 // are likely to be moved to EntityResolvingXmlReader.
163 string currentTextValue;
164 string constructingTextValue;
165 bool shouldResetCurrentTextValue;
166 bool isSignificantWhitespace;
167 bool isWhitespace;
168 bool isText;
170 // Utility caches.
171 Stack attributeValueEntityStack = new Stack ();
172 StringBuilder valueBuilder;
174 class AttributeSlot
176 public string Name;
177 public string LocalName;
178 public string NS;
179 public string Prefix;
180 public string Value; // normalized
181 public bool IsDefault;
183 public void Clear ()
185 Prefix = String.Empty;
186 LocalName = String.Empty;
187 NS = String.Empty;
188 Value = String.Empty;
189 IsDefault = false;
193 internal EntityResolvingXmlReader Source {
194 // we cannot return EntityResolvingXmlReader.source
195 // since it must check non-wellformedness error
196 // (undeclared entity in use).
197 get { return reader; }
200 public DTDObjectModel DTD {
201 get { return dtd; }
204 public EntityHandling EntityHandling {
205 get { return reader.EntityHandling; }
206 set { reader.EntityHandling = value; }
209 public override void Close ()
211 reader.Close ();
214 int GetAttributeIndex (string name)
216 for (int i = 0; i < attributeCount; i++)
217 if (attributes [i].Name == name)
218 return i;
219 return -1;
222 int GetAttributeIndex (string localName, string ns)
224 for (int i = 0; i < attributeCount; i++)
225 if (attributes [i].LocalName == localName &&
226 attributes [i].NS == ns)
227 return i;
228 return -1;
231 // We had already done attribute validation, so can ignore name.
232 public override string GetAttribute (int i)
234 if (currentTextValue != null)
235 throw new IndexOutOfRangeException ("Specified index is out of range: " + i);
237 if (attributeCount <= i)
238 throw new IndexOutOfRangeException ("Specified index is out of range: " + i);
239 return attributes [i].Value;
242 public override string GetAttribute (string name)
244 if (currentTextValue != null)
245 return null;
247 int i = GetAttributeIndex (name);
248 return i < 0 ? null : attributes [i].Value;
251 public override string GetAttribute (string name, string ns)
253 if (currentTextValue != null)
254 return null;
256 int i = GetAttributeIndex (name, ns);
257 return i < 0 ? null : attributes [i].Value;
260 #if NET_2_0
261 IDictionary<string, string> IXmlNamespaceResolver.GetNamespacesInScope (XmlNamespaceScope scope)
263 IXmlNamespaceResolver res = reader as IXmlNamespaceResolver;
264 return res != null ? res.GetNamespacesInScope (scope) : new Dictionary<string, string> ();
266 #endif
268 bool IXmlLineInfo.HasLineInfo ()
270 IXmlLineInfo ixli = reader as IXmlLineInfo;
271 if (ixli != null)
272 return ixli.HasLineInfo ();
273 else
274 return false;
277 public override string LookupNamespace (string prefix)
279 string s = nsmgr.LookupNamespace (NameTable.Get (prefix));
280 return s == String.Empty ? null : s;
283 #if NET_2_0
284 string IXmlNamespaceResolver.LookupPrefix (string ns)
286 IXmlNamespaceResolver res = reader as IXmlNamespaceResolver;
287 return res != null ? res.LookupPrefix (ns) : null;
289 #endif
291 public override void MoveToAttribute (int i)
293 if (currentTextValue != null)
294 throw new IndexOutOfRangeException ("The index is out of range.");
296 if (attributeCount <= i)
297 throw new IndexOutOfRangeException ("The index is out of range.");
299 if (i < reader.AttributeCount) // non-default attribute
300 reader.MoveToAttribute (i);
301 currentAttribute = i;
302 consumedAttribute = false;
303 return;
306 public override bool MoveToAttribute (string name)
308 if (currentTextValue != null)
309 return false;
311 int i = GetAttributeIndex (name);
312 if (i < 0)
313 return false;
314 if (i < reader.AttributeCount)
315 reader.MoveToAttribute (i);
316 currentAttribute = i;
317 consumedAttribute = false;
318 return true;
321 public override bool MoveToAttribute (string name, string ns)
323 if (currentTextValue != null)
324 return false;
326 int i = GetAttributeIndex (name, ns);
327 if (i < 0)
328 return false;
329 if (i < reader.AttributeCount)
330 reader.MoveToAttribute (i);
331 currentAttribute = i;
332 consumedAttribute = false;
333 return true;
336 public override bool MoveToElement ()
338 if (currentTextValue != null)
339 return false;
341 bool b = reader.MoveToElement ();
342 if (!b && !IsDefault)
343 return false;
344 currentAttribute = -1;
345 consumedAttribute = false;
346 return true;
349 public override bool MoveToFirstAttribute ()
351 if (currentTextValue != null)
352 return false;
354 if (attributeCount == 0)
355 return false;
356 currentAttribute = 0;
357 reader.MoveToFirstAttribute ();
358 consumedAttribute = false;
359 return true;
362 public override bool MoveToNextAttribute ()
364 if (currentTextValue != null)
365 return false;
367 if (currentAttribute == -1)
368 return MoveToFirstAttribute ();
369 if (++currentAttribute == attributeCount) {
370 currentAttribute--;
371 return false;
374 if (currentAttribute < reader.AttributeCount)
375 reader.MoveToAttribute (currentAttribute);
376 consumedAttribute = false;
377 return true;
380 public override bool Read ()
382 if (currentTextValue != null)
383 shouldResetCurrentTextValue = true;
385 if (currentAttribute >= 0)
386 MoveToElement ();
388 currentElement = null;
389 currentAttribute = -1;
390 consumedAttribute = false;
391 attributeCount = 0;
392 isWhitespace = false;
393 isSignificantWhitespace = false;
394 isText = false;
396 bool b = ReadContent () || currentTextValue != null;
397 if (!b &&
398 #if NET_2_0
399 (Settings == null || (Settings.ValidationFlags & XmlSchemaValidationFlags.ProcessIdentityConstraints) == 0) &&
400 #endif
401 this.missingIDReferences.Count > 0) {
402 this.HandleError ("Missing ID reference was found: " +
403 String.Join (",", missingIDReferences.ToArray (typeof (string)) as string []),
404 XmlSeverityType.Error);
405 // Don't output the same errors so many times.
406 this.missingIDReferences.Clear ();
408 if (validatingReader != null)
409 EntityHandling = validatingReader.EntityHandling;
410 return b;
413 private bool ReadContent ()
415 switch (reader.ReadState) {
416 case ReadState.Closed:
417 case ReadState.Error:
418 case ReadState.EndOfFile:
419 return false;
421 if (popScope) {
422 nsmgr.PopScope ();
423 popScope = false;
424 if (elementStack.Count == 0)
425 // it reached to the end of document element,
426 // so reset to non-validating state.
427 currentAutomata = null;
430 bool b = !reader.EOF;
431 if (shouldResetCurrentTextValue) {
432 currentTextValue = null;
433 shouldResetCurrentTextValue = false;
435 else
436 b = reader.Read ();
438 if (!b) {
439 if (elementStack.Count != 0)
440 throw new InvalidOperationException ("Unexpected end of XmlReader.");
441 return false;
444 return ProcessContent ();
447 bool ProcessContent ()
449 switch (reader.NodeType) {
450 case XmlNodeType.XmlDeclaration:
451 FillAttributes ();
452 if (GetAttribute ("standalone") == "yes")
453 isStandalone = true;
454 break;
456 case XmlNodeType.DocumentType:
457 ReadDoctype ();
458 break;
460 case XmlNodeType.Element:
461 if (constructingTextValue != null) {
462 currentTextValue = constructingTextValue;
463 constructingTextValue = null;
464 if (isWhitespace)
465 ValidateWhitespaceNode ();
466 return true;
468 ProcessStartElement ();
469 break;
471 case XmlNodeType.EndElement:
472 if (constructingTextValue != null) {
473 currentTextValue = constructingTextValue;
474 constructingTextValue = null;
475 return true;
477 ProcessEndElement ();
478 break;
480 case XmlNodeType.CDATA:
481 isSignificantWhitespace = isWhitespace = false;
482 isText = true;
484 ValidateText ();
486 if (currentTextValue != null) {
487 currentTextValue = constructingTextValue;
488 constructingTextValue = null;
489 return true;
491 break;
492 case XmlNodeType.SignificantWhitespace:
493 if (!isText)
494 isSignificantWhitespace = true;
495 isWhitespace = false;
496 goto case XmlNodeType.DocumentFragment;
497 case XmlNodeType.Text:
498 isWhitespace = isSignificantWhitespace = false;
499 isText = true;
500 goto case XmlNodeType.DocumentFragment;
501 case XmlNodeType.DocumentFragment:
502 // it should not happen, but in case if
503 // XmlReader really returns it, just ignore.
504 if (reader.NodeType == XmlNodeType.DocumentFragment)
505 break;
507 ValidateText ();
509 break;
510 case XmlNodeType.Whitespace:
511 if (!isText && !isSignificantWhitespace)
512 isWhitespace = true;
513 goto case XmlNodeType.DocumentFragment;
515 if (isWhitespace)
516 ValidateWhitespaceNode ();
517 currentTextValue = constructingTextValue;
518 constructingTextValue = null;
519 return true;
522 private void FillAttributes ()
524 if (reader.MoveToFirstAttribute ()) {
525 do {
526 AttributeSlot slot = GetAttributeSlot ();
527 slot.Name = reader.Name;
528 slot.LocalName = reader.LocalName;
529 slot.Prefix = reader.Prefix;
530 slot.NS = reader.NamespaceURI;
531 slot.Value = reader.Value;
532 } while (reader.MoveToNextAttribute ());
533 reader.MoveToElement ();
537 private void ValidateText ()
539 if (currentAutomata == null)
540 return;
542 DTDElementDeclaration elem = null;
543 if (elementStack.Count > 0)
544 elem = dtd.ElementDecls [elementStack.Peek () as string];
545 // Here element should have been already validated, so
546 // if no matching declaration is found, simply ignore.
547 if (elem != null && !elem.IsMixedContent && !elem.IsAny && !isWhitespace) {
548 HandleError (String.Format ("Current element {0} does not allow character data content.", elementStack.Peek () as string),
549 XmlSeverityType.Error);
550 currentAutomata = previousAutomata;
554 private void ValidateWhitespaceNode ()
556 // VC Standalone Document Declaration (2.9)
557 if (this.isStandalone && DTD != null && elementStack.Count > 0) {
558 DTDElementDeclaration elem = DTD.ElementDecls [elementStack.Peek () as string];
559 if (elem != null && !elem.IsInternalSubset && !elem.IsMixedContent && !elem.IsAny && !elem.IsEmpty)
560 HandleError ("In a standalone document, whitespace cannot appear in an element which is declared to contain only element children.", XmlSeverityType.Error);
564 private void HandleError (string message, XmlSeverityType severity)
566 if (validatingReader != null &&
567 validatingReader.ValidationType == ValidationType.None)
568 return;
570 IXmlLineInfo info = this as IXmlLineInfo;
571 bool hasLine = info.HasLineInfo ();
572 XmlSchemaException ex = new XmlSchemaException (
573 message,
574 hasLine ? info.LineNumber : 0,
575 hasLine ? info.LinePosition : 0,
576 null,
577 BaseURI,
578 null);
579 HandleError (ex, severity);
582 private void HandleError (XmlSchemaException ex, XmlSeverityType severity)
584 if (validatingReader != null &&
585 validatingReader.ValidationType == ValidationType.None)
586 return;
588 if (validatingReader != null)
589 this.validatingReader.OnValidationEvent (this,
590 new ValidationEventArgs (ex, ex.Message, severity));
591 else if (severity == XmlSeverityType.Error)
592 throw ex;
595 private void ValidateAttributes (DTDAttListDeclaration decl, bool validate)
597 DtdValidateAttributes (decl, validate);
599 for (int i = 0; i < attributeCount; i++) {
600 AttributeSlot slot = attributes [i];
601 if (slot.Name == "xmlns" || slot.Prefix == "xmlns")
602 nsmgr.AddNamespace (
603 slot.Prefix == "xmlns" ? slot.LocalName : String.Empty,
604 slot.Value);
607 for (int i = 0; i < attributeCount; i++) {
608 AttributeSlot slot = attributes [i];
609 if (slot.Name == "xmlns")
610 slot.NS = XmlNamespaceManager.XmlnsXmlns;
611 else if (slot.Prefix.Length > 0)
612 slot.NS = LookupNamespace (slot.Prefix);
613 else
614 slot.NS = String.Empty;
618 AttributeSlot GetAttributeSlot ()
620 if (attributeCount == attributes.Length) {
621 AttributeSlot [] tmp = new AttributeSlot [attributeCount << 1];
622 Array.Copy (attributes, tmp, attributeCount);
623 attributes = tmp;
625 if (attributes [attributeCount] == null)
626 attributes [attributeCount] = new AttributeSlot ();
627 AttributeSlot slot = attributes [attributeCount];
628 slot.Clear ();
629 attributeCount++;
630 return slot;
633 private void DtdValidateAttributes (DTDAttListDeclaration decl, bool validate)
635 while (reader.MoveToNextAttribute ()) {
636 string attrName = reader.Name;
637 AttributeSlot slot = GetAttributeSlot ();
638 slot.Name = reader.Name;
639 slot.LocalName = reader.LocalName;
640 slot.Prefix = reader.Prefix;
641 XmlReader targetReader = reader;
642 string attrValue = String.Empty;
643 // For attribute node, it always resolves
644 // entity references on attributes.
645 while (attributeValueEntityStack.Count >= 0) {
646 if (!targetReader.ReadAttributeValue ()) {
647 if (attributeValueEntityStack.Count > 0) {
648 targetReader = attributeValueEntityStack.Pop () as XmlReader;
649 continue;
650 } else
651 break;
653 switch (targetReader.NodeType) {
654 case XmlNodeType.EntityReference:
655 DTDEntityDeclaration edecl = DTD.EntityDecls [targetReader.Name];
656 if (edecl == null) {
657 HandleError (String.Format ("Referenced entity {0} is not declared.", targetReader.Name),
658 XmlSeverityType.Error);
659 } else {
660 XmlTextReader etr = new XmlTextReader (edecl.EntityValue, XmlNodeType.Attribute, ParserContext);
661 attributeValueEntityStack.Push (targetReader);
662 targetReader = etr;
663 continue;
665 break;
666 case XmlNodeType.EndEntity:
667 break;
668 default:
669 attrValue += targetReader.Value;
670 break;
674 reader.MoveToElement ();
675 reader.MoveToAttribute (attrName);
676 slot.Value = FilterNormalization (attrName, attrValue);
678 if (!validate)
679 continue;
681 // Validation
683 DTDAttributeDefinition def = decl [reader.Name];
684 if (def == null) {
685 HandleError (String.Format ("Attribute {0} is not declared.", reader.Name),
686 XmlSeverityType.Error);
687 continue;
690 // check enumeration constraint
691 if (def.EnumeratedAttributeDeclaration.Count > 0)
692 if (!def.EnumeratedAttributeDeclaration.Contains (slot.Value))
693 HandleError (String.Format ("Attribute enumeration constraint error in attribute {0}, value {1}.",
694 reader.Name, attrValue), XmlSeverityType.Error);
695 if (def.EnumeratedNotations.Count > 0)
696 if (!def.EnumeratedNotations.Contains (
697 slot.Value))
698 HandleError (String.Format ("Attribute notation enumeration constraint error in attribute {0}, value {1}.",
699 reader.Name, attrValue), XmlSeverityType.Error);
701 // check type constraint
702 string normalized = null;
703 if (def.Datatype != null)
704 normalized = FilterNormalization (def.Name, attrValue);
705 else
706 normalized = attrValue;
707 DTDEntityDeclaration ent;
709 // Common process to get list value
710 string [] list = null;
711 switch (def.Datatype.TokenizedType) {
712 case XmlTokenizedType.IDREFS:
713 case XmlTokenizedType.ENTITIES:
714 case XmlTokenizedType.NMTOKENS:
715 try {
716 list = def.Datatype.ParseValue (normalized, NameTable, null) as string [];
717 } catch (Exception) {
718 HandleError ("Attribute value is invalid against its data type.", XmlSeverityType.Error);
719 list = new string [0];
721 break;
722 default:
723 try {
724 def.Datatype.ParseValue (normalized, NameTable, null);
725 } catch (Exception ex) {
726 HandleError (String.Format ("Attribute value is invalid against its data type '{0}'. {1}", def.Datatype, ex.Message), XmlSeverityType.Error);
728 break;
731 switch (def.Datatype.TokenizedType) {
732 case XmlTokenizedType.ID:
733 if (this.idList.Contains (normalized)) {
734 HandleError (String.Format ("Node with ID {0} was already appeared.", attrValue),
735 XmlSeverityType.Error);
736 } else {
737 if (missingIDReferences.Contains (normalized))
738 missingIDReferences.Remove (normalized);
739 idList.Add (normalized);
741 break;
742 case XmlTokenizedType.IDREF:
743 if (!idList.Contains (normalized))
744 missingIDReferences.Add (normalized);
745 break;
746 case XmlTokenizedType.IDREFS:
747 for (int i = 0; i < list.Length; i++) {
748 string idref = list [i];
749 if (!idList.Contains (idref))
750 missingIDReferences.Add (idref);
752 break;
753 case XmlTokenizedType.ENTITY:
754 ent = dtd.EntityDecls [normalized];
755 if (ent == null)
756 HandleError ("Reference to undeclared entity was found in attribute: " + reader.Name + ".", XmlSeverityType.Error);
757 else if (ent.NotationName == null)
758 HandleError ("The entity specified by entity type value must be an unparsed entity. The entity definition has no NDATA in attribute: " + reader.Name + ".", XmlSeverityType.Error);
759 break;
760 case XmlTokenizedType.ENTITIES:
761 for (int i = 0; i < list.Length; i++) {
762 string entref = list [i];
763 ent = dtd.EntityDecls [FilterNormalization (reader.Name, entref)];
764 if (ent == null)
765 HandleError ("Reference to undeclared entity was found in attribute: " + reader.Name + ".", XmlSeverityType.Error);
766 else if (ent.NotationName == null)
767 HandleError ("The entity specified by ENTITIES type value must be an unparsed entity. The entity definition has no NDATA in attribute: " + reader.Name + ".", XmlSeverityType.Error);
769 break;
770 // case XmlTokenizedType.NMTOKEN: nothing to do
771 // case XmlTokenizedType.NMTOKENS: nothing to do
774 if (isStandalone && !def.IsInternalSubset &&
775 attrValue != normalized)
776 HandleError ("In standalone document, attribute value characters must not be checked against external definition.", XmlSeverityType.Error);
778 if (def.OccurenceType ==
779 DTDAttributeOccurenceType.Fixed &&
780 attrValue != def.DefaultValue)
781 HandleError (String.Format ("Fixed attribute {0} in element {1} has invalid value {2}.",
782 def.Name, decl.Name, attrValue),
783 XmlSeverityType.Error);
786 if (validate)
787 VerifyDeclaredAttributes (decl);
789 MoveToElement ();
792 void ReadDoctype ()
794 FillAttributes ();
796 IHasXmlParserContext ctx = reader as IHasXmlParserContext;
797 if (ctx != null)
798 dtd = ctx.ParserContext.Dtd;
799 if (dtd == null) {
800 XmlTextReaderImpl xmlTextReader = new XmlTextReaderImpl ("", XmlNodeType.Document, null);
801 xmlTextReader.XmlResolver = resolver;
802 xmlTextReader.GenerateDTDObjectModel (reader.Name,
803 reader ["PUBLIC"], reader ["SYSTEM"], reader.Value);
804 dtd = xmlTextReader.DTD;
806 currentAutomata = dtd.RootAutomata;
808 // Validity Constraint Check.
809 for (int i = 0; i < DTD.Errors.Length; i++)
810 HandleError (DTD.Errors [i].Message, XmlSeverityType.Error);
812 // NData target exists.
813 foreach (DTDEntityDeclaration ent in dtd.EntityDecls.Values)
814 if (ent.NotationName != null && dtd.NotationDecls [ent.NotationName] == null)
815 this.HandleError ("Target notation was not found for NData in entity declaration " + ent.Name + ".",
816 XmlSeverityType.Error);
817 // NOTATION exists for attribute default values
818 foreach (DTDAttListDeclaration attListIter in dtd.AttListDecls.Values) {
819 foreach (DTDAttributeDefinition def in attListIter.Definitions) {
820 if (def.Datatype.TokenizedType != XmlTokenizedType.NOTATION)
821 continue;
822 foreach (string notation in def.EnumeratedNotations)
823 if (dtd.NotationDecls [notation] == null)
824 this.HandleError ("Target notation was not found for NOTATION typed attribute default " + def.Name + ".",
825 XmlSeverityType.Error);
830 void ProcessStartElement ()
832 nsmgr.PushScope ();
833 popScope = reader.IsEmptyElement;
834 elementStack.Push (reader.Name);
836 currentElement = Name;
838 // If no DTD, skip validation.
839 if (currentAutomata == null) {
840 ValidateAttributes (null, false);
841 if (reader.IsEmptyElement)
842 ProcessEndElement ();
843 return;
846 // StartElementDeriv
848 previousAutomata = currentAutomata;
849 currentAutomata = currentAutomata.TryStartElement (reader.Name);
850 if (currentAutomata == DTD.Invalid) {
851 HandleError (String.Format ("Invalid start element found: {0}", reader.Name),
852 XmlSeverityType.Error);
853 currentAutomata = previousAutomata;
856 DTDElementDeclaration elem =
857 DTD.ElementDecls [reader.Name];
858 if (elem == null) {
859 HandleError (String.Format ("Element {0} is not declared.", reader.Name),
860 XmlSeverityType.Error);
861 currentAutomata = previousAutomata;
864 automataStack.Push (currentAutomata);
865 if (elem != null) // i.e. not invalid
866 currentAutomata = elem.ContentModel.GetAutomata ();
868 DTDAttListDeclaration attList = dtd.AttListDecls [currentElement];
869 if (attList != null) {
870 // check attributes
871 ValidateAttributes (attList, true);
872 currentAttribute = -1;
873 } else {
874 if (reader.HasAttributes) {
875 HandleError (String.Format (
876 "Attributes are found on element {0} while it has no attribute definitions.", currentElement),
877 XmlSeverityType.Error);
879 // SetupValidityIgnorantAttributes ();
880 ValidateAttributes (null, false);
882 // If it is empty element then directly check end element.
883 if (reader.IsEmptyElement)
884 ProcessEndElement ();
887 void ProcessEndElement ()
889 popScope = true;
890 elementStack.Pop ();
892 // If no schema specification, then skip validation.
893 if (currentAutomata == null)
894 return;
896 // EndElementDeriv
897 DTDElementDeclaration elem =
898 DTD.ElementDecls [reader.Name];
899 if (elem == null) {
900 HandleError (String.Format ("Element {0} is not declared.", reader.Name),
901 XmlSeverityType.Error);
904 previousAutomata = currentAutomata;
905 // Don't let currentAutomata
906 DTDAutomata tmpAutomata = currentAutomata.TryEndElement ();
907 if (tmpAutomata == DTD.Invalid) {
908 HandleError (String.Format ("Invalid end element found: {0}", reader.Name),
909 XmlSeverityType.Error);
910 currentAutomata = previousAutomata;
913 currentAutomata = automataStack.Pop () as DTDAutomata;
916 void VerifyDeclaredAttributes (DTDAttListDeclaration decl)
918 // Check if all required attributes exist, and/or
919 // if there is default values, then add them.
920 for (int i = 0; i < decl.Definitions.Count; i++) {
921 DTDAttributeDefinition def = (DTDAttributeDefinition) decl.Definitions [i];
922 bool exists = false;
923 for (int a = 0; a < attributeCount; a++) {
924 if (attributes [a].Name == def.Name) {
925 exists = true;
926 break;
929 if (exists)
930 continue;
932 if (def.OccurenceType == DTDAttributeOccurenceType.Required) {
933 HandleError (String.Format ("Required attribute {0} in element {1} not found .",
934 def.Name, decl.Name),
935 XmlSeverityType.Error);
936 continue;
939 else if (def.DefaultValue == null)
940 continue;
942 if (this.isStandalone && !def.IsInternalSubset)
943 HandleError ("In standalone document, external default value definition must not be applied.", XmlSeverityType.Error);
945 switch (validatingReader.ValidationType) {
946 case ValidationType.Auto:
947 if (validatingReader.Schemas.Count == 0)
948 goto case ValidationType.DTD;
949 break;
950 case ValidationType.DTD:
951 case ValidationType.None:
952 // Other than them, ignore DTD defaults.
953 AttributeSlot slot = GetAttributeSlot ();
954 slot.Name = def.Name;
955 int colonAt = def.Name.IndexOf (':');
956 slot.LocalName = colonAt < 0 ? def.Name :
957 def.Name.Substring (colonAt + 1);
958 string prefix = colonAt < 0 ?
959 String.Empty :
960 def.Name.Substring (0, colonAt);
961 slot.Prefix = prefix;
962 slot.Value = def.DefaultValue;
963 slot.IsDefault = true;
964 break;
969 #if MOONLIGHT
970 internal
971 #else
972 public
973 #endif
974 override bool ReadAttributeValue ()
976 if (consumedAttribute)
977 return false;
978 if (NodeType == XmlNodeType.Attribute &&
979 EntityHandling == EntityHandling.ExpandEntities) {
980 consumedAttribute = true;
981 return true;
983 else if (IsDefault) {
984 consumedAttribute = true;
985 return true;
987 else
988 return reader.ReadAttributeValue ();
991 #if MOONLIGHT
992 internal
993 #else
994 public
995 #endif
996 override void ResolveEntity ()
998 reader.ResolveEntity ();
1001 public override int AttributeCount {
1002 get {
1003 if (currentTextValue != null)
1004 return 0;
1006 return attributeCount;
1010 public override string BaseURI {
1011 get {
1012 return reader.BaseURI;
1016 public override bool CanResolveEntity {
1017 get { return true; }
1020 public override int Depth {
1021 get {
1022 int baseNum = reader.Depth;
1023 if (currentTextValue != null && reader.NodeType == XmlNodeType.EndElement)
1024 baseNum++;
1026 return IsDefault ? baseNum + 1 : baseNum;
1030 public override bool EOF {
1031 get { return reader.EOF; }
1034 #if MOONLIGHT
1035 internal
1036 #else
1037 public
1038 #endif
1039 override bool HasValue {
1040 get {
1041 return currentAttribute >= 0 ? true :
1042 currentTextValue != null ? true :
1043 reader.HasValue;
1047 public override bool IsDefault {
1048 get {
1049 if (currentTextValue != null)
1050 return false;
1051 if (currentAttribute == -1)
1052 return false;
1053 return attributes [currentAttribute].IsDefault;
1057 public override bool IsEmptyElement {
1058 get {
1059 if (currentTextValue != null)
1060 return false;
1061 return reader.IsEmptyElement;
1065 public override string this [int i] {
1066 get { return GetAttribute (i); }
1069 public override string this [string name] {
1070 get { return GetAttribute (name); }
1073 public override string this [string name, string ns] {
1074 get { return GetAttribute (name, ns); }
1077 public int LineNumber {
1078 get {
1079 IXmlLineInfo info = reader as IXmlLineInfo;
1080 return (info != null) ? info.LineNumber : 0;
1084 public int LinePosition {
1085 get {
1086 IXmlLineInfo info = reader as IXmlLineInfo;
1087 return (info != null) ? info.LinePosition : 0;
1091 public override string LocalName {
1092 get {
1093 if (currentTextValue != null || consumedAttribute)
1094 return String.Empty;
1095 else if (NodeType == XmlNodeType.Attribute)
1096 return attributes [currentAttribute].LocalName;
1097 else
1098 return reader.LocalName;
1102 public override string Name {
1103 get {
1104 if (currentTextValue != null || consumedAttribute)
1105 return String.Empty;
1106 else if (NodeType == XmlNodeType.Attribute)
1107 return attributes [currentAttribute].Name;
1108 else
1109 return reader.Name;
1113 public override string NamespaceURI {
1114 get {
1115 if (currentTextValue != null || consumedAttribute)
1116 return String.Empty;
1117 switch (NodeType) {
1118 case XmlNodeType.Attribute:
1119 return (string) attributes [currentAttribute].NS;
1120 case XmlNodeType.Element:
1121 case XmlNodeType.EndElement:
1122 return nsmgr.LookupNamespace (Prefix);
1123 default:
1124 return String.Empty;
1129 public override XmlNameTable NameTable {
1130 get { return reader.NameTable; }
1133 public override XmlNodeType NodeType {
1134 get {
1135 if (currentTextValue != null)
1136 return isSignificantWhitespace ? XmlNodeType.SignificantWhitespace :
1137 isWhitespace ? XmlNodeType.Whitespace :
1138 XmlNodeType.Text;
1140 // If consumedAttribute is true, then entities must be resolved.
1141 return consumedAttribute ? XmlNodeType.Text :
1142 IsDefault ? XmlNodeType.Attribute :
1143 reader.NodeType;
1147 public XmlParserContext ParserContext {
1148 get { return XmlSchemaUtil.GetParserContext (reader); }
1151 public override string Prefix {
1152 get {
1153 if (currentTextValue != null || consumedAttribute)
1154 return String.Empty;
1155 else if (NodeType == XmlNodeType.Attribute)
1156 return attributes [currentAttribute].Prefix;
1157 else
1158 return reader.Prefix;
1162 public override char QuoteChar {
1163 get {
1164 // If it is not actually on an attribute, then it returns
1165 // undefined value or '"'.
1166 return reader.QuoteChar;
1170 public override ReadState ReadState {
1171 get {
1172 if (reader.ReadState == ReadState.EndOfFile && currentTextValue != null)
1173 return ReadState.Interactive;
1174 return reader.ReadState;
1178 public object SchemaType {
1179 get {
1180 if (DTD == null || currentAttribute == -1 ||
1181 currentElement == null)
1182 return null;
1183 DTDAttListDeclaration decl =
1184 DTD.AttListDecls [currentElement];
1185 DTDAttributeDefinition def =
1186 decl != null ? decl [attributes [currentAttribute].Name] : null;
1187 return def != null ? def.Datatype : null;
1191 char [] whitespaceChars = new char [] {' '};
1192 private string FilterNormalization (string attrName, string rawValue)
1194 if (DTD == null || sourceTextReader == null ||
1195 !sourceTextReader.Normalization)
1196 return rawValue;
1198 DTDAttributeDefinition def =
1199 dtd.AttListDecls [currentElement].Get (attrName);
1200 valueBuilder.Append (rawValue);
1201 valueBuilder.Replace ('\r', ' ');
1202 valueBuilder.Replace ('\n', ' ');
1203 valueBuilder.Replace ('\t', ' ');
1204 try {
1205 if (def == null || def.Datatype.TokenizedType == XmlTokenizedType.CDATA)
1206 return valueBuilder.ToString ();
1207 for (int i=0; i < valueBuilder.Length; i++) {
1208 if (valueBuilder [i] != ' ')
1209 continue;
1210 while (++i < valueBuilder.Length && valueBuilder [i] == ' ')
1211 valueBuilder.Remove (i, 1);
1213 return valueBuilder.ToString ().Trim (whitespaceChars);
1214 } finally {
1215 valueBuilder.Length = 0;
1219 // LAMESPEC: When source XmlTextReader.Normalize is true, then
1220 // every Attribute node is normalized. However, corresponding
1221 // Values of attribute value Text nodes are not.
1222 public override string Value {
1223 get {
1224 if (currentTextValue != null)
1225 return currentTextValue;
1226 // As to this property, MS.NET seems ignorant of EntityHandling...
1227 else if (NodeType == XmlNodeType.Attribute
1228 // It also covers default attribute text.
1229 || consumedAttribute)
1230 return attributes [currentAttribute].Value;
1231 else
1232 return reader.Value;
1236 public override string XmlLang {
1237 get {
1238 string val = this ["xml:lang"];
1239 return val != null ? val : reader.XmlLang;
1243 internal XmlResolver Resolver {
1244 get { return resolver; }
1247 public XmlResolver XmlResolver {
1248 set {
1249 if (dtd != null)
1250 dtd.XmlResolver = value;
1251 resolver = value;
1255 public override XmlSpace XmlSpace {
1256 get {
1257 string val = this ["xml:space"];
1258 switch (val) {
1259 case "preserve":
1260 return XmlSpace.Preserve;
1261 case "default":
1262 return XmlSpace.Default;
1263 default:
1264 return reader.XmlSpace;