2 // DTDValidatingReader2.cs
5 // Atsushi Enomoto atsushi@ximian.com
7 // (C)2003 Atsushi Enomoto
8 // (C)2004-2006 Novell Inc.
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:
20 // The above copyright notice and this permission notice shall be
21 // included in all copies or substantial portions of the Software.
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.
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 />">
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.
60 IsDefault messes all around the reader, so simplify it.
61 isWhitespace/isText/blah mess the code too, so clear it as well.
66 using System.Collections;
68 using System.Collections.Generic;
73 using System.Xml.Schema;
76 using XmlTextReaderImpl = Mono.Xml2.XmlTextReader;
78 using XmlTextReaderImpl = System.Xml.XmlTextReader;
84 internal class DTDValidatingReader : XmlReader, IXmlLineInfo,
86 IXmlNamespaceResolver,
88 IHasXmlParserContext, IHasXmlSchemaInfo
90 public DTDValidatingReader (XmlReader reader)
95 internal DTDValidatingReader (XmlReader reader,
96 XmlValidatingReader validatingReader)
98 IHasXmlParserContext container = reader as IHasXmlParserContext;
99 this.reader = new EntityResolvingXmlReader (reader,
100 container != null ? container.ParserContext : null);
101 this.sourceTextReader = reader as XmlTextReader;
102 elementStack = new Stack ();
103 automataStack = new Stack ();
104 attributes = new AttributeSlot [10];
105 nsmgr = new XmlNamespaceManager (reader.NameTable);
106 this.validatingReader = validatingReader;
107 valueBuilder = new StringBuilder ();
108 idList = new ArrayList ();
109 missingIDReferences = new ArrayList ();
110 XmlTextReader xtReader = reader as XmlTextReader;
111 if (xtReader != null) {
112 resolver = xtReader.Resolver;
115 resolver = new XmlUrlResolver ();
118 // The primary xml source
119 EntityResolvingXmlReader reader;
121 // This is used to acquire "Normalization" property which
122 // could be dynamically changed.
123 XmlTextReader sourceTextReader;
125 // This field is used to get properties (such as
126 // EntityHandling) and to raise events.
127 XmlValidatingReader validatingReader;
129 // We hold DTDObjectModel for such case that the source
130 // XmlReader does not implement IHasXmlParerContext
131 // (especially for non-sys.xml.dll readers).
134 // Used to resolve entities (as expected)
135 XmlResolver resolver;
137 // mainly used to retrieve DTDElementDeclaration
138 string currentElement;
139 AttributeSlot [] attributes;
142 // Holds MoveTo*Attribute()/ReadAttributeValue() status.
143 int currentAttribute = -1;
144 bool consumedAttribute;
146 // Ancestor and current node context for each depth.
151 // Validation context.
153 DTDAutomata currentAutomata;
154 DTDAutomata previousAutomata;
156 ArrayList missingIDReferences;
158 // Holds namespace context. It must not be done in source
159 // XmlReader because default attributes could affect on it.
160 XmlNamespaceManager nsmgr;
162 // Those fields are used to store on-constructing text value.
163 // They are required to support entity-mixed text, so they
164 // are likely to be moved to EntityResolvingXmlReader.
165 string currentTextValue;
166 string constructingTextValue;
167 bool shouldResetCurrentTextValue;
168 bool isSignificantWhitespace;
173 Stack attributeValueEntityStack = new Stack ();
174 StringBuilder valueBuilder;
179 public string LocalName;
181 public string Prefix;
182 public string Value; // normalized
183 public bool IsDefault;
187 Prefix = String.Empty;
188 LocalName = String.Empty;
190 Value = String.Empty;
195 internal EntityResolvingXmlReader Source {
196 // we cannot return EntityResolvingXmlReader.source
197 // since it must check non-wellformedness error
198 // (undeclared entity in use).
199 get { return reader; }
202 public DTDObjectModel DTD {
206 public EntityHandling EntityHandling {
207 get { return reader.EntityHandling; }
208 set { reader.EntityHandling = value; }
211 public override void Close ()
216 int GetAttributeIndex (string name)
218 for (int i = 0; i < attributeCount; i++)
219 if (attributes [i].Name == name)
224 int GetAttributeIndex (string localName, string ns)
226 for (int i = 0; i < attributeCount; i++)
227 if (attributes [i].LocalName == localName &&
228 attributes [i].NS == ns)
233 // We had already done attribute validation, so can ignore name.
234 public override string GetAttribute (int i)
236 if (currentTextValue != null)
237 throw new IndexOutOfRangeException ("Specified index is out of range: " + i);
239 if (attributeCount <= i)
240 throw new IndexOutOfRangeException ("Specified index is out of range: " + i);
241 return attributes [i].Value;
244 public override string GetAttribute (string name)
246 if (currentTextValue != null)
249 int i = GetAttributeIndex (name);
250 return i < 0 ? null : attributes [i].Value;
253 public override string GetAttribute (string name, string ns)
255 if (currentTextValue != null)
258 int i = GetAttributeIndex (name, ns);
259 return i < 0 ? null : attributes [i].Value;
263 IDictionary<string, string> IXmlNamespaceResolver.GetNamespacesInScope (XmlNamespaceScope scope)
265 IXmlNamespaceResolver res = reader as IXmlNamespaceResolver;
266 return res != null ? res.GetNamespacesInScope (scope) : new Dictionary<string, string> ();
270 bool IXmlLineInfo.HasLineInfo ()
272 IXmlLineInfo ixli = reader as IXmlLineInfo;
274 return ixli.HasLineInfo ();
279 public override string LookupNamespace (string prefix)
281 string s = nsmgr.LookupNamespace (NameTable.Get (prefix));
282 return s == String.Empty ? null : s;
286 string IXmlNamespaceResolver.LookupPrefix (string ns)
288 IXmlNamespaceResolver res = reader as IXmlNamespaceResolver;
289 return res != null ? res.LookupPrefix (ns) : null;
293 public override void MoveToAttribute (int i)
295 if (currentTextValue != null)
296 throw new IndexOutOfRangeException ("The index is out of range.");
298 if (attributeCount <= i)
299 throw new IndexOutOfRangeException ("The index is out of range.");
301 if (i < reader.AttributeCount) // non-default attribute
302 reader.MoveToAttribute (i);
303 currentAttribute = i;
304 consumedAttribute = false;
308 public override bool MoveToAttribute (string name)
310 if (currentTextValue != null)
313 int i = GetAttributeIndex (name);
316 if (i < reader.AttributeCount)
317 reader.MoveToAttribute (i);
318 currentAttribute = i;
319 consumedAttribute = false;
323 public override bool MoveToAttribute (string name, string ns)
325 if (currentTextValue != null)
328 int i = GetAttributeIndex (name, ns);
331 if (i < reader.AttributeCount)
332 reader.MoveToAttribute (i);
333 currentAttribute = i;
334 consumedAttribute = false;
338 public override bool MoveToElement ()
340 if (currentTextValue != null)
343 bool b = reader.MoveToElement ();
344 if (!b && !IsDefault)
346 currentAttribute = -1;
347 consumedAttribute = false;
351 public override bool MoveToFirstAttribute ()
353 if (currentTextValue != null)
356 if (attributeCount == 0)
358 currentAttribute = 0;
359 reader.MoveToFirstAttribute ();
360 consumedAttribute = false;
364 public override bool MoveToNextAttribute ()
366 if (currentTextValue != null)
369 if (currentAttribute == -1)
370 return MoveToFirstAttribute ();
371 if (++currentAttribute == attributeCount) {
376 if (currentAttribute < reader.AttributeCount)
377 reader.MoveToAttribute (currentAttribute);
378 consumedAttribute = false;
382 public override bool Read ()
384 if (currentTextValue != null)
385 shouldResetCurrentTextValue = true;
387 if (currentAttribute >= 0)
390 currentElement = null;
391 currentAttribute = -1;
392 consumedAttribute = false;
394 isWhitespace = false;
395 isSignificantWhitespace = false;
398 bool b = ReadContent () || currentTextValue != null;
401 (Settings == null || (Settings.ValidationFlags & XmlSchemaValidationFlags.ProcessIdentityConstraints) == 0) &&
403 this.missingIDReferences.Count > 0) {
404 this.HandleError ("Missing ID reference was found: " +
405 String.Join (",", missingIDReferences.ToArray (typeof (string)) as string []),
406 XmlSeverityType.Error);
407 // Don't output the same errors so many times.
408 this.missingIDReferences.Clear ();
410 if (validatingReader != null)
411 EntityHandling = validatingReader.EntityHandling;
415 private bool ReadContent ()
417 switch (reader.ReadState) {
418 case ReadState.Closed:
419 case ReadState.Error:
420 case ReadState.EndOfFile:
426 if (elementStack.Count == 0)
427 // it reached to the end of document element,
428 // so reset to non-validating state.
429 currentAutomata = null;
432 bool b = !reader.EOF;
433 if (shouldResetCurrentTextValue) {
434 currentTextValue = null;
435 shouldResetCurrentTextValue = false;
441 if (elementStack.Count != 0)
442 throw new InvalidOperationException ("Unexpected end of XmlReader.");
446 return ProcessContent ();
449 bool ProcessContent ()
451 switch (reader.NodeType) {
452 case XmlNodeType.XmlDeclaration:
454 if (GetAttribute ("standalone") == "yes")
458 case XmlNodeType.DocumentType:
462 case XmlNodeType.Element:
463 if (constructingTextValue != null) {
464 currentTextValue = constructingTextValue;
465 constructingTextValue = null;
467 ValidateWhitespaceNode ();
470 ProcessStartElement ();
473 case XmlNodeType.EndElement:
474 if (constructingTextValue != null) {
475 currentTextValue = constructingTextValue;
476 constructingTextValue = null;
479 ProcessEndElement ();
482 case XmlNodeType.CDATA:
483 isSignificantWhitespace = isWhitespace = false;
488 if (currentTextValue != null) {
489 currentTextValue = constructingTextValue;
490 constructingTextValue = null;
494 case XmlNodeType.SignificantWhitespace:
496 isSignificantWhitespace = true;
497 isWhitespace = false;
498 goto case XmlNodeType.DocumentFragment;
499 case XmlNodeType.Text:
500 isWhitespace = isSignificantWhitespace = false;
502 goto case XmlNodeType.DocumentFragment;
503 case XmlNodeType.DocumentFragment:
504 // it should not happen, but in case if
505 // XmlReader really returns it, just ignore.
506 if (reader.NodeType == XmlNodeType.DocumentFragment)
512 case XmlNodeType.Whitespace:
513 if (!isText && !isSignificantWhitespace)
515 goto case XmlNodeType.DocumentFragment;
518 ValidateWhitespaceNode ();
519 currentTextValue = constructingTextValue;
520 constructingTextValue = null;
524 private void FillAttributes ()
526 if (reader.MoveToFirstAttribute ()) {
528 AttributeSlot slot = GetAttributeSlot ();
529 slot.Name = reader.Name;
530 slot.LocalName = reader.LocalName;
531 slot.Prefix = reader.Prefix;
532 slot.NS = reader.NamespaceURI;
533 slot.Value = reader.Value;
534 } while (reader.MoveToNextAttribute ());
535 reader.MoveToElement ();
539 private void ValidateText ()
541 if (currentAutomata == null)
544 DTDElementDeclaration elem = null;
545 if (elementStack.Count > 0)
546 elem = dtd.ElementDecls [elementStack.Peek () as string];
547 // Here element should have been already validated, so
548 // if no matching declaration is found, simply ignore.
549 if (elem != null && !elem.IsMixedContent && !elem.IsAny && !isWhitespace) {
550 HandleError (String.Format ("Current element {0} does not allow character data content.", elementStack.Peek () as string),
551 XmlSeverityType.Error);
552 currentAutomata = previousAutomata;
556 private void ValidateWhitespaceNode ()
558 // VC Standalone Document Declaration (2.9)
559 if (this.isStandalone && DTD != null && elementStack.Count > 0) {
560 DTDElementDeclaration elem = DTD.ElementDecls [elementStack.Peek () as string];
561 if (elem != null && !elem.IsInternalSubset && !elem.IsMixedContent && !elem.IsAny && !elem.IsEmpty)
562 HandleError ("In a standalone document, whitespace cannot appear in an element which is declared to contain only element children.", XmlSeverityType.Error);
566 private XmlException NotWFError (string message)
568 return new XmlException (this as IXmlLineInfo, BaseURI, message);
571 private void HandleError (string message, XmlSeverityType severity)
573 if (validatingReader != null &&
574 validatingReader.ValidationType == ValidationType.None)
577 IXmlLineInfo info = this as IXmlLineInfo;
578 bool hasLine = info.HasLineInfo ();
579 XmlSchemaException ex = new XmlSchemaException (
581 hasLine ? info.LineNumber : 0,
582 hasLine ? info.LinePosition : 0,
586 HandleError (ex, severity);
589 private void HandleError (XmlSchemaException ex, XmlSeverityType severity)
591 if (validatingReader != null &&
592 validatingReader.ValidationType == ValidationType.None)
595 if (validatingReader != null)
596 this.validatingReader.OnValidationEvent (this,
597 new ValidationEventArgs (ex, ex.Message, severity));
598 else if (severity == XmlSeverityType.Error)
602 private void ValidateAttributes (DTDAttListDeclaration decl, bool validate)
604 DtdValidateAttributes (decl, validate);
606 for (int i = 0; i < attributeCount; i++) {
607 AttributeSlot slot = attributes [i];
608 if (slot.Name == "xmlns" || slot.Prefix == "xmlns")
610 slot.Prefix == "xmlns" ? slot.LocalName : String.Empty,
614 for (int i = 0; i < attributeCount; i++) {
615 AttributeSlot slot = attributes [i];
616 if (slot.Name == "xmlns")
617 slot.NS = XmlNamespaceManager.XmlnsXmlns;
618 else if (slot.Prefix.Length > 0)
619 slot.NS = LookupNamespace (slot.Prefix);
621 slot.NS = String.Empty;
625 AttributeSlot GetAttributeSlot ()
627 if (attributeCount == attributes.Length) {
628 AttributeSlot [] tmp = new AttributeSlot [attributeCount << 1];
629 Array.Copy (attributes, tmp, attributeCount);
632 if (attributes [attributeCount] == null)
633 attributes [attributeCount] = new AttributeSlot ();
634 AttributeSlot slot = attributes [attributeCount];
640 private void DtdValidateAttributes (DTDAttListDeclaration decl, bool validate)
642 while (reader.MoveToNextAttribute ()) {
643 string attrName = reader.Name;
644 AttributeSlot slot = GetAttributeSlot ();
645 slot.Name = reader.Name;
646 slot.LocalName = reader.LocalName;
647 slot.Prefix = reader.Prefix;
648 XmlReader targetReader = reader;
649 string attrValue = String.Empty;
650 // For attribute node, it always resolves
651 // entity references on attributes.
652 while (attributeValueEntityStack.Count >= 0) {
653 if (!targetReader.ReadAttributeValue ()) {
654 if (attributeValueEntityStack.Count > 0) {
655 targetReader = attributeValueEntityStack.Pop () as XmlReader;
660 switch (targetReader.NodeType) {
661 case XmlNodeType.EntityReference:
662 DTDEntityDeclaration edecl = DTD.EntityDecls [targetReader.Name];
664 HandleError (String.Format ("Referenced entity {0} is not declared.", targetReader.Name),
665 XmlSeverityType.Error);
667 XmlTextReader etr = new XmlTextReader (edecl.EntityValue, XmlNodeType.Attribute, ParserContext);
668 attributeValueEntityStack.Push (targetReader);
673 case XmlNodeType.EndEntity:
676 attrValue += targetReader.Value;
681 reader.MoveToElement ();
682 reader.MoveToAttribute (attrName);
683 slot.Value = FilterNormalization (attrName, attrValue);
690 DTDAttributeDefinition def = decl [reader.Name];
692 HandleError (String.Format ("Attribute {0} is not declared.", reader.Name),
693 XmlSeverityType.Error);
697 // check enumeration constraint
698 if (def.EnumeratedAttributeDeclaration.Count > 0)
699 if (!def.EnumeratedAttributeDeclaration.Contains (slot.Value))
700 HandleError (String.Format ("Attribute enumeration constraint error in attribute {0}, value {1}.",
701 reader.Name, attrValue), XmlSeverityType.Error);
702 if (def.EnumeratedNotations.Count > 0)
703 if (!def.EnumeratedNotations.Contains (
705 HandleError (String.Format ("Attribute notation enumeration constraint error in attribute {0}, value {1}.",
706 reader.Name, attrValue), XmlSeverityType.Error);
708 // check type constraint
709 string normalized = null;
710 if (def.Datatype != null)
711 normalized = FilterNormalization (def.Name, attrValue);
713 normalized = attrValue;
714 DTDEntityDeclaration ent;
716 // Common process to get list value
717 string [] list = null;
718 switch (def.Datatype.TokenizedType) {
719 case XmlTokenizedType.IDREFS:
720 case XmlTokenizedType.ENTITIES:
721 case XmlTokenizedType.NMTOKENS:
723 list = def.Datatype.ParseValue (normalized, NameTable, null) as string [];
724 } catch (Exception) {
725 HandleError ("Attribute value is invalid against its data type.", XmlSeverityType.Error);
726 list = new string [0];
731 def.Datatype.ParseValue (normalized, NameTable, null);
732 } catch (Exception ex) {
733 HandleError (String.Format ("Attribute value is invalid against its data type '{0}'. {1}", def.Datatype, ex.Message), XmlSeverityType.Error);
738 switch (def.Datatype.TokenizedType) {
739 case XmlTokenizedType.ID:
740 if (this.idList.Contains (normalized)) {
741 HandleError (String.Format ("Node with ID {0} was already appeared.", attrValue),
742 XmlSeverityType.Error);
744 if (missingIDReferences.Contains (normalized))
745 missingIDReferences.Remove (normalized);
746 idList.Add (normalized);
749 case XmlTokenizedType.IDREF:
750 if (!idList.Contains (normalized))
751 missingIDReferences.Add (normalized);
753 case XmlTokenizedType.IDREFS:
754 for (int i = 0; i < list.Length; i++) {
755 string idref = list [i];
756 if (!idList.Contains (idref))
757 missingIDReferences.Add (idref);
760 case XmlTokenizedType.ENTITY:
761 ent = dtd.EntityDecls [normalized];
763 HandleError ("Reference to undeclared entity was found in attribute: " + reader.Name + ".", XmlSeverityType.Error);
764 else if (ent.NotationName == null)
765 HandleError ("The entity specified by entity type value must be an unparsed entity. The entity definition has no NDATA in attribute: " + reader.Name + ".", XmlSeverityType.Error);
767 case XmlTokenizedType.ENTITIES:
768 for (int i = 0; i < list.Length; i++) {
769 string entref = list [i];
770 ent = dtd.EntityDecls [FilterNormalization (reader.Name, entref)];
772 HandleError ("Reference to undeclared entity was found in attribute: " + reader.Name + ".", XmlSeverityType.Error);
773 else if (ent.NotationName == null)
774 HandleError ("The entity specified by ENTITIES type value must be an unparsed entity. The entity definition has no NDATA in attribute: " + reader.Name + ".", XmlSeverityType.Error);
777 // case XmlTokenizedType.NMTOKEN: nothing to do
778 // case XmlTokenizedType.NMTOKENS: nothing to do
781 if (isStandalone && !def.IsInternalSubset &&
782 attrValue != normalized)
783 HandleError ("In standalone document, attribute value characters must not be checked against external definition.", XmlSeverityType.Error);
785 if (def.OccurenceType ==
786 DTDAttributeOccurenceType.Fixed &&
787 attrValue != def.DefaultValue)
788 HandleError (String.Format ("Fixed attribute {0} in element {1} has invalid value {2}.",
789 def.Name, decl.Name, attrValue),
790 XmlSeverityType.Error);
794 VerifyDeclaredAttributes (decl);
803 IHasXmlParserContext ctx = reader as IHasXmlParserContext;
805 dtd = ctx.ParserContext.Dtd;
807 XmlTextReaderImpl xmlTextReader = new XmlTextReaderImpl ("", XmlNodeType.Document, null);
808 xmlTextReader.XmlResolver = resolver;
809 xmlTextReader.GenerateDTDObjectModel (reader.Name,
810 reader ["PUBLIC"], reader ["SYSTEM"], reader.Value);
811 dtd = xmlTextReader.DTD;
813 currentAutomata = dtd.RootAutomata;
815 // Validity Constraint Check.
816 for (int i = 0; i < DTD.Errors.Length; i++)
817 HandleError (DTD.Errors [i].Message, XmlSeverityType.Error);
819 // NData target exists.
820 foreach (DTDEntityDeclaration ent in dtd.EntityDecls.Values)
821 if (ent.NotationName != null && dtd.NotationDecls [ent.NotationName] == null)
822 this.HandleError ("Target notation was not found for NData in entity declaration " + ent.Name + ".",
823 XmlSeverityType.Error);
824 // NOTATION exists for attribute default values
825 foreach (DTDAttListDeclaration attListIter in dtd.AttListDecls.Values) {
826 foreach (DTDAttributeDefinition def in attListIter.Definitions) {
827 if (def.Datatype.TokenizedType != XmlTokenizedType.NOTATION)
829 foreach (string notation in def.EnumeratedNotations)
830 if (dtd.NotationDecls [notation] == null)
831 this.HandleError ("Target notation was not found for NOTATION typed attribute default " + def.Name + ".",
832 XmlSeverityType.Error);
837 void ProcessStartElement ()
840 popScope = reader.IsEmptyElement;
841 elementStack.Push (reader.Name);
843 currentElement = Name;
845 // If no DTD, skip validation.
846 if (currentAutomata == null) {
847 ValidateAttributes (null, false);
848 if (reader.IsEmptyElement)
849 ProcessEndElement ();
855 previousAutomata = currentAutomata;
856 currentAutomata = currentAutomata.TryStartElement (reader.Name);
857 if (currentAutomata == DTD.Invalid) {
858 HandleError (String.Format ("Invalid start element found: {0}", reader.Name),
859 XmlSeverityType.Error);
860 currentAutomata = previousAutomata;
863 DTDElementDeclaration elem =
864 DTD.ElementDecls [reader.Name];
866 HandleError (String.Format ("Element {0} is not declared.", reader.Name),
867 XmlSeverityType.Error);
868 currentAutomata = previousAutomata;
871 automataStack.Push (currentAutomata);
872 if (elem != null) // i.e. not invalid
873 currentAutomata = elem.ContentModel.GetAutomata ();
875 DTDAttListDeclaration attList = dtd.AttListDecls [currentElement];
876 if (attList != null) {
878 ValidateAttributes (attList, true);
879 currentAttribute = -1;
881 if (reader.HasAttributes) {
882 HandleError (String.Format (
883 "Attributes are found on element {0} while it has no attribute definitions.", currentElement),
884 XmlSeverityType.Error);
886 // SetupValidityIgnorantAttributes ();
887 ValidateAttributes (null, false);
889 // If it is empty element then directly check end element.
890 if (reader.IsEmptyElement)
891 ProcessEndElement ();
894 void ProcessEndElement ()
899 // If no schema specification, then skip validation.
900 if (currentAutomata == null)
904 DTDElementDeclaration elem =
905 DTD.ElementDecls [reader.Name];
907 HandleError (String.Format ("Element {0} is not declared.", reader.Name),
908 XmlSeverityType.Error);
911 previousAutomata = currentAutomata;
912 // Don't let currentAutomata
913 DTDAutomata tmpAutomata = currentAutomata.TryEndElement ();
914 if (tmpAutomata == DTD.Invalid) {
915 HandleError (String.Format ("Invalid end element found: {0}", reader.Name),
916 XmlSeverityType.Error);
917 currentAutomata = previousAutomata;
920 currentAutomata = automataStack.Pop () as DTDAutomata;
923 void VerifyDeclaredAttributes (DTDAttListDeclaration decl)
925 // Check if all required attributes exist, and/or
926 // if there is default values, then add them.
927 for (int i = 0; i < decl.Definitions.Count; i++) {
928 DTDAttributeDefinition def = (DTDAttributeDefinition) decl.Definitions [i];
930 for (int a = 0; a < attributeCount; a++) {
931 if (attributes [a].Name == def.Name) {
939 if (def.OccurenceType == DTDAttributeOccurenceType.Required) {
940 HandleError (String.Format ("Required attribute {0} in element {1} not found .",
941 def.Name, decl.Name),
942 XmlSeverityType.Error);
946 else if (def.DefaultValue == null)
949 if (this.isStandalone && !def.IsInternalSubset)
950 HandleError ("In standalone document, external default value definition must not be applied.", XmlSeverityType.Error);
952 switch (validatingReader.ValidationType) {
953 case ValidationType.Auto:
954 if (validatingReader.Schemas.Count == 0)
955 goto case ValidationType.DTD;
957 case ValidationType.DTD:
958 case ValidationType.None:
959 // Other than them, ignore DTD defaults.
960 AttributeSlot slot = GetAttributeSlot ();
961 slot.Name = def.Name;
962 int colonAt = def.Name.IndexOf (':');
963 slot.LocalName = colonAt < 0 ? def.Name :
964 def.Name.Substring (colonAt + 1);
965 string prefix = colonAt < 0 ?
967 def.Name.Substring (0, colonAt);
968 slot.Prefix = prefix;
969 slot.Value = def.DefaultValue;
970 slot.IsDefault = true;
981 override bool ReadAttributeValue ()
983 if (consumedAttribute)
985 if (NodeType == XmlNodeType.Attribute &&
986 EntityHandling == EntityHandling.ExpandEntities) {
987 consumedAttribute = true;
990 else if (IsDefault) {
991 consumedAttribute = true;
995 return reader.ReadAttributeValue ();
1003 override void ResolveEntity ()
1005 reader.ResolveEntity ();
1008 public override int AttributeCount {
1010 if (currentTextValue != null)
1013 return attributeCount;
1017 public override string BaseURI {
1019 return reader.BaseURI;
1023 public override bool CanResolveEntity {
1024 get { return true; }
1027 public override int Depth {
1029 int baseNum = reader.Depth;
1030 if (currentTextValue != null && reader.NodeType == XmlNodeType.EndElement)
1033 return IsDefault ? baseNum + 1 : baseNum;
1037 public override bool EOF {
1038 get { return reader.EOF; }
1046 override bool HasValue {
1048 return currentAttribute >= 0 ? true :
1049 currentTextValue != null ? true :
1054 public override bool IsDefault {
1056 if (currentTextValue != null)
1058 if (currentAttribute == -1)
1060 return attributes [currentAttribute].IsDefault;
1064 public override bool IsEmptyElement {
1066 if (currentTextValue != null)
1068 return reader.IsEmptyElement;
1072 public override string this [int i] {
1073 get { return GetAttribute (i); }
1076 public override string this [string name] {
1077 get { return GetAttribute (name); }
1080 public override string this [string name, string ns] {
1081 get { return GetAttribute (name, ns); }
1084 public int LineNumber {
1086 IXmlLineInfo info = reader as IXmlLineInfo;
1087 return (info != null) ? info.LineNumber : 0;
1091 public int LinePosition {
1093 IXmlLineInfo info = reader as IXmlLineInfo;
1094 return (info != null) ? info.LinePosition : 0;
1098 public override string LocalName {
1100 if (currentTextValue != null || consumedAttribute)
1101 return String.Empty;
1102 else if (NodeType == XmlNodeType.Attribute)
1103 return attributes [currentAttribute].LocalName;
1105 return reader.LocalName;
1109 public override string Name {
1111 if (currentTextValue != null || consumedAttribute)
1112 return String.Empty;
1113 else if (NodeType == XmlNodeType.Attribute)
1114 return attributes [currentAttribute].Name;
1120 public override string NamespaceURI {
1122 if (currentTextValue != null || consumedAttribute)
1123 return String.Empty;
1125 case XmlNodeType.Attribute:
1126 return (string) attributes [currentAttribute].NS;
1127 case XmlNodeType.Element:
1128 case XmlNodeType.EndElement:
1129 return nsmgr.LookupNamespace (Prefix);
1131 return String.Empty;
1136 public override XmlNameTable NameTable {
1137 get { return reader.NameTable; }
1140 public override XmlNodeType NodeType {
1142 if (currentTextValue != null)
1143 return isSignificantWhitespace ? XmlNodeType.SignificantWhitespace :
1144 isWhitespace ? XmlNodeType.Whitespace :
1147 // If consumedAttribute is true, then entities must be resolved.
1148 return consumedAttribute ? XmlNodeType.Text :
1149 IsDefault ? XmlNodeType.Attribute :
1154 public XmlParserContext ParserContext {
1155 get { return XmlSchemaUtil.GetParserContext (reader); }
1158 public override string Prefix {
1160 if (currentTextValue != null || consumedAttribute)
1161 return String.Empty;
1162 else if (NodeType == XmlNodeType.Attribute)
1163 return attributes [currentAttribute].Prefix;
1165 return reader.Prefix;
1169 public override char QuoteChar {
1171 // If it is not actually on an attribute, then it returns
1172 // undefined value or '"'.
1173 return reader.QuoteChar;
1177 public override ReadState ReadState {
1179 if (reader.ReadState == ReadState.EndOfFile && currentTextValue != null)
1180 return ReadState.Interactive;
1181 return reader.ReadState;
1185 public object SchemaType {
1187 if (DTD == null || currentAttribute == -1 ||
1188 currentElement == null)
1190 DTDAttListDeclaration decl =
1191 DTD.AttListDecls [currentElement];
1192 DTDAttributeDefinition def =
1193 decl != null ? decl [attributes [currentAttribute].Name] : null;
1194 return def != null ? def.Datatype : null;
1198 char [] whitespaceChars = new char [] {' '};
1199 private string FilterNormalization (string attrName, string rawValue)
1201 if (DTD == null || sourceTextReader == null ||
1202 !sourceTextReader.Normalization)
1205 DTDAttributeDefinition def =
1206 dtd.AttListDecls [currentElement].Get (attrName);
1207 valueBuilder.Append (rawValue);
1208 valueBuilder.Replace ('\r', ' ');
1209 valueBuilder.Replace ('\n', ' ');
1210 valueBuilder.Replace ('\t', ' ');
1212 if (def == null || def.Datatype.TokenizedType == XmlTokenizedType.CDATA)
1213 return valueBuilder.ToString ();
1214 for (int i=0; i < valueBuilder.Length; i++) {
1215 if (valueBuilder [i] != ' ')
1217 while (++i < valueBuilder.Length && valueBuilder [i] == ' ')
1218 valueBuilder.Remove (i, 1);
1220 return valueBuilder.ToString ().Trim (whitespaceChars);
1222 valueBuilder.Length = 0;
1226 // LAMESPEC: When source XmlTextReader.Normalize is true, then
1227 // every Attribute node is normalized. However, corresponding
1228 // Values of attribute value Text nodes are not.
1229 public override string Value {
1231 if (currentTextValue != null)
1232 return currentTextValue;
1233 // As to this property, MS.NET seems ignorant of EntityHandling...
1234 else if (NodeType == XmlNodeType.Attribute
1235 // It also covers default attribute text.
1236 || consumedAttribute)
1237 return attributes [currentAttribute].Value;
1239 return reader.Value;
1243 public override string XmlLang {
1245 string val = this ["xml:lang"];
1246 return val != null ? val : reader.XmlLang;
1250 internal XmlResolver Resolver {
1251 get { return resolver; }
1254 public XmlResolver XmlResolver {
1257 dtd.XmlResolver = value;
1262 public override XmlSpace XmlSpace {
1264 string val = this ["xml:space"];
1267 return XmlSpace.Preserve;
1269 return XmlSpace.Default;
1271 return reader.XmlSpace;