Simple content restriction was not validated when it is in further reference.
[mono-project.git] / mcs / class / System.XML / Mono.Xml.Schema / XsdValidatingReader.cs
blob2e2637fa7fc0baf724ae8a891cabded66cd27f2b
1 //
2 // Mono.Xml.Schema.XsdValidatingReader.cs
3 //
4 // Author:
5 // Atsushi Enomoto (ginga@kit.hi-ho.ne.jp)
6 //
7 // (C)2003 Atsushi Enomoto
8 //
11 // Permission is hereby granted, free of charge, to any person obtaining
12 // a copy of this software and associated documentation files (the
13 // "Software"), to deal in the Software without restriction, including
14 // without limitation the rights to use, copy, modify, merge, publish,
15 // distribute, sublicense, and/or sell copies of the Software, and to
16 // permit persons to whom the Software is furnished to do so, subject to
17 // the following conditions:
18 //
19 // The above copyright notice and this permission notice shall be
20 // included in all copies or substantial portions of the Software.
21 //
22 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
23 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
24 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
25 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
26 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
27 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
28 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
30 using System;
31 using System.Collections;
32 #if NET_2_0
33 using System.Collections.Generic;
34 #endif
35 using System.Collections.Specialized;
36 using System.IO;
37 using System.Text;
38 using System.Xml;
39 using System.Xml.Schema;
40 using Mono.Xml;
42 #if NET_2_0
43 using ValException = System.Xml.Schema.XmlSchemaValidationException;
44 #else
45 using ValException = System.Xml.Schema.XmlSchemaException;
46 #endif
48 using QName = System.Xml.XmlQualifiedName;
49 using ContentProc = System.Xml.Schema.XmlSchemaContentProcessing;
50 using XsElement = System.Xml.Schema.XmlSchemaElement;
51 using XsAttribute = System.Xml.Schema.XmlSchemaAttribute;
52 using ComplexType = System.Xml.Schema.XmlSchemaComplexType;
53 using SimpleType = System.Xml.Schema.XmlSchemaSimpleType;
54 using SimpleTypeRest = System.Xml.Schema.XmlSchemaSimpleTypeRestriction;
55 using SimpleTypeList = System.Xml.Schema.XmlSchemaSimpleTypeList;
56 using SimpleTypeUnion = System.Xml.Schema.XmlSchemaSimpleTypeUnion;
57 using XsDatatype = System.Xml.Schema.XmlSchemaDatatype;
59 namespace Mono.Xml.Schema
61 internal class XsdValidatingReader : XmlReader, IXmlLineInfo, IHasXmlSchemaInfo, IHasXmlParserContext
63 static readonly XsAttribute [] emptyAttributeArray =
64 new XsAttribute [0];
66 XmlReader reader;
67 XmlResolver resolver;
68 IHasXmlSchemaInfo sourceReaderSchemaInfo;
69 IXmlLineInfo readerLineInfo;
70 ValidationType validationType;
71 XmlSchemaSet schemas = new XmlSchemaSet ();
72 bool namespaces = true;
73 bool validationStarted;
75 #region ID Constraints
76 bool checkIdentity = true;
77 XsdIDManager idManager = new XsdIDManager ();
78 #endregion
80 #region Key Constraints
81 bool checkKeyConstraints = true;
82 ArrayList keyTables = new ArrayList ();
83 ArrayList currentKeyFieldConsumers;
84 ArrayList tmpKeyrefPool;
85 #endregion
86 ArrayList elementQNameStack = new ArrayList ();
88 XsdParticleStateManager state = new XsdParticleStateManager ();
90 int skipValidationDepth = -1;
91 int xsiNilDepth = -1;
92 StringBuilder storedCharacters = new StringBuilder ();
93 bool shouldValidateCharacters;
95 XsAttribute [] defaultAttributes = emptyAttributeArray;
96 int currentDefaultAttribute = -1;
97 ArrayList defaultAttributesCache = new ArrayList ();
98 bool defaultAttributeConsumed;
99 object currentAttrType;
101 #region .ctor
102 public XsdValidatingReader (XmlReader reader)
104 this.reader = reader;
105 readerLineInfo = reader as IXmlLineInfo;
106 sourceReaderSchemaInfo = reader as IHasXmlSchemaInfo;
107 schemas.ValidationEventHandler += ValidationEventHandler;
109 #endregion
111 public ValidationEventHandler ValidationEventHandler;
113 // Private Properties
115 private XsdValidationContext Context {
116 get { return state.Context; }
119 #region Key Constraints
120 internal ArrayList CurrentKeyFieldConsumers {
121 get {
122 if (currentKeyFieldConsumers == null)
123 currentKeyFieldConsumers = new ArrayList ();
124 return currentKeyFieldConsumers;
127 #endregion
129 // Public Non-overrides
131 public int XsiNilDepth {
132 get { return xsiNilDepth; }
135 public bool Namespaces {
136 get { return namespaces; }
137 set { namespaces = value; }
140 // This is required to resolve xsi:schemaLocation
141 public XmlResolver XmlResolver {
142 set {
143 resolver = value;
147 // This should be changed before the first Read() call.
148 public XmlSchemaSet Schemas {
149 get { return schemas; }
150 set {
151 if (validationStarted)
152 throw new InvalidOperationException ("Schemas must be set before the first call to Read().");
153 schemas = value;
157 public object SchemaType {
158 get {
159 if (ReadState != ReadState.Interactive)
160 return null;
162 switch (NodeType) {
163 case XmlNodeType.Element:
164 if (Context.ActualType != null)
165 return Context.ActualType;
166 else
167 return SourceReaderSchemaType;
168 case XmlNodeType.Attribute:
169 if (currentAttrType == null) {
170 ComplexType ct = Context.ActualType as ComplexType;
171 if (ct != null) {
172 XsAttribute attdef = ct.AttributeUses [new QName (LocalName, NamespaceURI)] as XsAttribute;
173 if (attdef != null)
174 currentAttrType = attdef.AttributeType;
175 return currentAttrType;
177 currentAttrType = SourceReaderSchemaType;
179 return currentAttrType;
180 default:
181 return SourceReaderSchemaType;
186 private object SourceReaderSchemaType {
187 get { return this.sourceReaderSchemaInfo != null ? sourceReaderSchemaInfo.SchemaType : null; }
190 public ValidationType ValidationType {
191 get { return validationType; }
192 set {
193 if (validationStarted)
194 throw new InvalidOperationException ("ValidationType must be set before reading.");
195 validationType = value;
199 // It is used only for independent XmlReader use, not for XmlValidatingReader.
200 public object ReadTypedValue ()
202 object o = XmlSchemaUtil.ReadTypedValue (this,
203 SchemaType, NamespaceManager,
204 storedCharacters);
205 storedCharacters.Length = 0;
206 return o;
209 // Public Overriden Properties
211 public override int AttributeCount {
212 get {
213 return reader.AttributeCount + defaultAttributes.Length;
217 public override string BaseURI {
218 get { return reader.BaseURI; }
221 // If this class is used to implement XmlValidatingReader,
222 // it should be left to DTDValidatingReader. In other cases,
223 // it depends on the reader's ability.
224 public override bool CanResolveEntity {
225 get { return reader.CanResolveEntity; }
228 public override int Depth {
229 get {
230 if (currentDefaultAttribute < 0)
231 return reader.Depth;
232 if (this.defaultAttributeConsumed)
233 return reader.Depth + 2;
234 return reader.Depth + 1;
238 public override bool EOF {
239 get { return reader.EOF; }
242 public override bool HasValue {
243 get {
244 if (currentDefaultAttribute < 0)
245 return reader.HasValue;
246 return true;
250 public override bool IsDefault {
251 get {
252 if (currentDefaultAttribute < 0)
253 return reader.IsDefault;
254 return true;
258 public override bool IsEmptyElement {
259 get {
260 if (currentDefaultAttribute < 0)
261 return reader.IsEmptyElement;
262 return false;
266 public override string this [int i] {
267 get { return GetAttribute (i); }
270 public override string this [string name] {
271 get { return GetAttribute (name); }
274 public override string this [string localName, string ns] {
275 get { return GetAttribute (localName, ns); }
278 public int LineNumber {
279 get { return readerLineInfo != null ? readerLineInfo.LineNumber : 0; }
282 public int LinePosition {
283 get { return readerLineInfo != null ? readerLineInfo.LinePosition : 0; }
286 public override string LocalName {
287 get {
288 if (currentDefaultAttribute < 0)
289 return reader.LocalName;
290 if (defaultAttributeConsumed)
291 return String.Empty;
292 return defaultAttributes [currentDefaultAttribute].QualifiedName.Name;
296 public override string Name {
297 get {
298 if (currentDefaultAttribute < 0)
299 return reader.Name;
300 if (defaultAttributeConsumed)
301 return String.Empty;
303 QName qname = defaultAttributes [currentDefaultAttribute].QualifiedName;
304 string prefix = Prefix;
305 if (prefix == String.Empty)
306 return qname.Name;
307 else
308 return String.Concat (prefix, ":", qname.Name);
312 public override string NamespaceURI {
313 get {
314 if (currentDefaultAttribute < 0)
315 return reader.NamespaceURI;
316 if (defaultAttributeConsumed)
317 return String.Empty;
318 return defaultAttributes [currentDefaultAttribute].QualifiedName.Namespace;
322 public override XmlNameTable NameTable {
323 get { return reader.NameTable; }
326 public override XmlNodeType NodeType {
327 get {
328 if (currentDefaultAttribute < 0)
329 return reader.NodeType;
330 if (defaultAttributeConsumed)
331 return XmlNodeType.Text;
332 return XmlNodeType.Attribute;
336 public XmlParserContext ParserContext {
337 get { return XmlSchemaUtil.GetParserContext (reader); }
340 internal XmlNamespaceManager NamespaceManager {
341 get { return ParserContext != null ? ParserContext.NamespaceManager : null; }
344 public override string Prefix {
345 get {
346 if (currentDefaultAttribute < 0)
347 return reader.Prefix;
348 if (defaultAttributeConsumed)
349 return String.Empty;
350 QName qname = defaultAttributes [currentDefaultAttribute].QualifiedName;
351 string prefix = NamespaceManager != null ? NamespaceManager.LookupPrefix (qname.Namespace, false) : null;
352 if (prefix == null)
353 return String.Empty;
354 else
355 return prefix;
359 public override char QuoteChar {
360 get { return reader.QuoteChar; }
363 public override ReadState ReadState {
364 get { return reader.ReadState; }
367 public override string Value {
368 get {
369 if (currentDefaultAttribute < 0)
370 return reader.Value;
371 string value = defaultAttributes [currentDefaultAttribute].ValidatedDefaultValue;
372 if (value == null)
373 value = defaultAttributes [currentDefaultAttribute].ValidatedFixedValue;
374 return value;
378 public override string XmlLang {
379 get {
380 string xmlLang = reader.XmlLang;
381 if (xmlLang != null)
382 return xmlLang;
383 int idx = this.FindDefaultAttribute ("lang", XmlNamespaceManager.XmlnsXml);
384 if (idx < 0)
385 return null;
386 xmlLang = defaultAttributes [idx].ValidatedDefaultValue;
387 if (xmlLang == null)
388 xmlLang = defaultAttributes [idx].ValidatedFixedValue;
389 return xmlLang;
393 public override XmlSpace XmlSpace {
394 get {
395 XmlSpace space = reader.XmlSpace;
396 if (space != XmlSpace.None)
397 return space;
398 int idx = this.FindDefaultAttribute ("space", XmlNamespaceManager.XmlnsXml);
399 if (idx < 0)
400 return XmlSpace.None;
401 string spaceSpec = defaultAttributes [idx].ValidatedDefaultValue;
402 if (spaceSpec == null)
403 spaceSpec = defaultAttributes [idx].ValidatedFixedValue;
404 return (XmlSpace) Enum.Parse (typeof (XmlSpace), spaceSpec, false);
408 // Private Methods
410 private void HandleError (string error)
412 HandleError (error, null);
415 private void HandleError (string error, Exception innerException)
417 HandleError (error, innerException, false);
420 private void HandleError (string error, Exception innerException, bool isWarning)
422 if (ValidationType == ValidationType.None) // extra quick check
423 return;
425 ValException schemaException = new ValException (error,
426 this, this.BaseURI, null, innerException);
427 HandleError (schemaException, isWarning);
430 private void HandleError (ValException schemaException)
432 HandleError (schemaException, false);
435 private void HandleError (ValException schemaException, bool isWarning)
437 if (ValidationType == ValidationType.None)
438 return;
440 ValidationEventArgs e = new ValidationEventArgs (schemaException,
441 schemaException.Message, isWarning ? XmlSeverityType.Warning : XmlSeverityType.Error);
443 if (ValidationEventHandler != null)
444 ValidationEventHandler (this, e);
446 else if (e.Severity == XmlSeverityType.Error)
447 throw e.Exception;
450 private XsElement FindElement (string name, string ns)
452 return (XsElement) schemas.GlobalElements [new QName (name, ns)];
455 private XmlSchemaType FindType (QName qname)
457 return (XmlSchemaType) schemas.GlobalTypes [qname];
460 private void ValidateStartElementParticle ()
462 if (Context.State == null)
463 return;
464 Context.XsiType = null;
465 state.CurrentElement = null;
466 Context.EvaluateStartElement (reader.LocalName,
467 reader.NamespaceURI);
468 if (Context.IsInvalid)
469 HandleError ("Invalid start element: " + reader.NamespaceURI + ":" + reader.LocalName);
471 Context.PushCurrentElement (state.CurrentElement);
474 private void ValidateEndElementParticle ()
476 if (Context.State != null) {
477 if (!Context.EvaluateEndElement ()) {
478 HandleError ("Invalid end element: " + reader.Name);
481 Context.PopCurrentElement ();
482 state.PopContext ();
485 // Utility for missing validation completion related to child items.
486 private void ValidateCharacters ()
488 if (xsiNilDepth >= 0 && xsiNilDepth < reader.Depth)
489 HandleError ("Element item appeared, while current element context is nil.");
491 if (shouldValidateCharacters)
492 storedCharacters.Append (reader.Value);
495 // Utility for missing validation completion related to child items.
496 private void ValidateEndSimpleContent ()
498 if (shouldValidateCharacters)
499 ValidateEndSimpleContentCore ();
500 shouldValidateCharacters = false;
501 storedCharacters.Length = 0;
504 private void ValidateEndSimpleContentCore ()
506 if (Context.ActualType == null)
507 return;
509 string value = storedCharacters.ToString ();
511 if (value.Length == 0) {
512 // 3.3.4 Element Locally Valid (Element) 5.1.2
513 if (Context.Element != null) {
514 if (Context.Element.ValidatedDefaultValue != null)
515 value = Context.Element.ValidatedDefaultValue;
519 XsDatatype dt = Context.ActualType as XsDatatype;
520 SimpleType st = Context.ActualType as SimpleType;
521 if (dt == null) {
522 if (st != null) {
523 dt = st.Datatype;
524 } else {
525 ComplexType ct = Context.ActualType as ComplexType;
526 var ctsm = ct.ContentModel as XmlSchemaSimpleContent;
527 if (ctsm != null) {
528 var scr = ctsm.Content as XmlSchemaSimpleContentRestriction;
529 if (scr != null)
530 st = FindSimpleBaseType (scr.BaseType ?? FindType (scr.BaseTypeName));
531 var sce = ctsm.Content as XmlSchemaSimpleContentExtension;
532 if (sce != null)
533 st = FindSimpleBaseType (FindType (sce.BaseTypeName));
536 dt = ct.Datatype;
537 switch (ct.ContentType) {
538 case XmlSchemaContentType.ElementOnly:
539 if (value.Length > 0 && !XmlChar.IsWhitespace (value))
540 HandleError ("Character content not allowed.");
541 break;
542 case XmlSchemaContentType.Empty:
543 if (value.Length > 0)
544 HandleError ("Character content not allowed.");
545 break;
549 if (dt != null) {
550 // 3.3.4 Element Locally Valid (Element) :: 5.2.2.2. Fixed value constraints
551 if (Context.Element != null && Context.Element.ValidatedFixedValue != null)
552 if (value != Context.Element.ValidatedFixedValue)
553 HandleError ("Fixed value constraint was not satisfied.");
554 AssessStringValid (st, dt, value);
557 #region Key Constraints
558 if (checkKeyConstraints)
559 ValidateSimpleContentIdentity (dt, value);
560 #endregion
562 shouldValidateCharacters = false;
565 SimpleType FindSimpleBaseType (XmlSchemaType xt)
567 var st = xt as SimpleType;
568 if (st != null)
569 return st;
570 if (xt == null)
571 return null;
572 return FindSimpleBaseType (xt.BaseXmlSchemaType);
575 // 3.14.4 String Valid
576 private void AssessStringValid (SimpleType st,
577 XsDatatype dt, string value)
579 XsDatatype validatedDatatype = dt;
580 if (st != null) {
581 string normalized = validatedDatatype.Normalize (value);
582 ValidateRestrictedSimpleTypeValue (st, ref validatedDatatype, normalized);
584 if (validatedDatatype != null) {
585 try {
586 validatedDatatype.ParseValue (value, NameTable, NamespaceManager);
587 } catch (Exception ex) { // FIXME: (wishlist) It is bad manner ;-(
588 HandleError ("Invalidly typed data was specified.", ex);
593 void ValidateRestrictedSimpleTypeValue (SimpleType st, ref XsDatatype dt, string normalized)
596 string [] values;
597 XsDatatype itemDatatype;
598 SimpleType itemSimpleType;
599 switch (st.DerivedBy) {
600 case XmlSchemaDerivationMethod.List:
601 SimpleTypeList listContent = st.Content as SimpleTypeList;
602 values = normalized.Split (XmlChar.WhitespaceChars);
603 itemDatatype = listContent.ValidatedListItemType as XsDatatype;
604 itemSimpleType = listContent.ValidatedListItemType as SimpleType;
605 for (int vi = 0; vi < values.Length; vi++) {
606 string each = values [vi];
607 if (each == String.Empty)
608 continue;
609 // validate against ValidatedItemType
610 if (itemDatatype != null) {
611 try {
612 itemDatatype.ParseValue (each, NameTable, NamespaceManager);
613 } catch (Exception ex) { // FIXME: (wishlist) better exception handling ;-(
614 HandleError ("List type value contains one or more invalid values.", ex);
615 break;
618 else
619 AssessStringValid (itemSimpleType, itemSimpleType.Datatype, each);
621 break;
622 case XmlSchemaDerivationMethod.Union:
623 SimpleTypeUnion union = st.Content as SimpleTypeUnion;
625 string each = normalized;
626 // validate against ValidatedItemType
627 bool passed = false;
628 foreach (object eachType in union.ValidatedTypes) {
629 itemDatatype = eachType as XsDatatype;
630 itemSimpleType = eachType as SimpleType;
631 if (itemDatatype != null) {
632 try {
633 itemDatatype.ParseValue (each, NameTable, NamespaceManager);
634 } catch (Exception) { // FIXME: (wishlist) better exception handling ;-(
635 continue;
638 else {
639 try {
640 AssessStringValid (itemSimpleType, itemSimpleType.Datatype, each);
641 } catch (ValException) {
642 continue;
645 passed = true;
646 break;
648 if (!passed) {
649 HandleError ("Union type value contains one or more invalid values.");
650 break;
653 break;
654 case XmlSchemaDerivationMethod.Restriction:
655 SimpleTypeRest str = st.Content as SimpleTypeRest;
656 // facet validation
657 if (str != null) {
658 /* Don't forget to validate against inherited type's facets
659 * Could we simplify this by assuming that the basetype will also
660 * be restriction?
661 * */
662 // mmm, will check later.
663 SimpleType baseType = st.BaseXmlSchemaType as SimpleType;
664 if (baseType != null) {
665 AssessStringValid(baseType, dt, normalized);
667 if (!str.ValidateValueWithFacets (normalized, NameTable, NamespaceManager)) {
668 HandleError ("Specified value was invalid against the facets.");
669 break;
672 dt = st.Datatype;
673 break;
678 private object GetXsiType (string name)
680 object xsiType = null;
681 QName typeQName = QName.Parse (name, this);
682 if (typeQName == ComplexType.AnyTypeName)
683 xsiType = ComplexType.AnyType;
684 else if (XmlSchemaUtil.IsBuiltInDatatypeName (typeQName))
685 xsiType = XsDatatype.FromName (typeQName);
686 else
687 xsiType = FindType (typeQName);
688 return xsiType;
691 // It is common to ElementLocallyValid::4 and SchemaValidityAssessment::1.2.1.2.4
692 private void AssessLocalTypeDerivationOK (object xsiType, object baseType, XmlSchemaDerivationMethod flag)
694 XmlSchemaType xsiSchemaType = xsiType as XmlSchemaType;
695 ComplexType baseComplexType = baseType as ComplexType;
696 ComplexType xsiComplexType = xsiSchemaType as ComplexType;
697 if (xsiType != baseType) {
698 // Extracted (not extraneous) check for 3.4.6 TypeDerivationOK.
699 if (baseComplexType != null)
700 flag |= baseComplexType.BlockResolved;
701 if (flag == XmlSchemaDerivationMethod.All) {
702 HandleError ("Prohibited element type substitution.");
703 return;
704 } else if (xsiSchemaType != null && (flag & xsiSchemaType.DerivedBy) != 0) {
705 HandleError ("Prohibited element type substitution.");
706 return;
710 if (xsiComplexType != null)
711 try {
712 xsiComplexType.ValidateTypeDerivationOK (baseType, null, null);
713 } catch (ValException ex) {
714 // HandleError ("Locally specified schema complex type derivation failed. " + ex.Message, ex);
715 HandleError (ex);
717 else {
718 SimpleType xsiSimpleType = xsiType as SimpleType;
719 if (xsiSimpleType != null) {
720 try {
721 xsiSimpleType.ValidateTypeDerivationOK (baseType, null, null, true);
722 } catch (ValException ex) {
723 // HandleError ("Locally specified schema simple type derivation failed. " + ex.Message, ex);
724 HandleError (ex);
727 else if (xsiType is XsDatatype) {
728 // do nothing
730 else
731 HandleError ("Primitive data type cannot be derived type using xsi:type specification.");
735 // Section 3.3.4 of the spec.
736 private void AssessStartElementSchemaValidity ()
738 // If the reader is inside xsi:nil (and failed
739 // on validation), then simply skip its content.
740 if (xsiNilDepth >= 0 && xsiNilDepth < reader.Depth)
741 HandleError ("Element item appeared, while current element context is nil.");
743 ValidateStartElementParticle ();
745 string xsiNilValue = reader.GetAttribute ("nil", XmlSchema.InstanceNamespace);
746 if (xsiNilValue != null)
747 xsiNilValue = xsiNilValue.Trim (XmlChar.WhitespaceChars);
748 bool isXsiNil = xsiNilValue == "true";
749 if (isXsiNil && this.xsiNilDepth < 0)
750 xsiNilDepth = reader.Depth;
752 // [Schema Validity Assessment (Element) 1.2]
753 // Evaluate "local type definition" from xsi:type.
754 // (See spec 3.3.4 Schema Validity Assessment (Element) 1.2.1.2.3.
755 // Note that Schema Validity Assessment(Element) 1.2 takes
756 // precedence than 1.1 of that.
758 string xsiTypeName = reader.GetAttribute ("type", XmlSchema.InstanceNamespace);
759 if (xsiTypeName != null) {
760 xsiTypeName = xsiTypeName.Trim (XmlChar.WhitespaceChars);
761 object xsiType = GetXsiType (xsiTypeName);
762 if (xsiType == null)
763 HandleError ("The instance type was not found: " + xsiTypeName + " .");
764 else {
765 XmlSchemaType xsiSchemaType = xsiType as XmlSchemaType;
766 if (xsiSchemaType != null && this.Context.Element != null) {
767 XmlSchemaType elemBaseType = Context.Element.ElementType as XmlSchemaType;
768 if (elemBaseType != null && (xsiSchemaType.DerivedBy & elemBaseType.FinalResolved) != 0)
769 HandleError ("The instance type is prohibited by the type of the context element.");
770 if (elemBaseType != xsiType && (xsiSchemaType.DerivedBy & this.Context.Element.BlockResolved) != 0)
771 HandleError ("The instance type is prohibited by the context element.");
773 ComplexType xsiComplexType = xsiType as ComplexType;
774 if (xsiComplexType != null && xsiComplexType.IsAbstract)
775 HandleError ("The instance type is abstract: " + xsiTypeName + " .");
776 else {
777 // If current schema type exists, then this xsi:type must be
778 // valid extension of that type. See 1.2.1.2.4.
779 if (Context.Element != null) {
780 AssessLocalTypeDerivationOK (xsiType, Context.Element.ElementType, Context.Element.BlockResolved);
782 AssessStartElementLocallyValidType (xsiType); // 1.2.2:
783 Context.XsiType = xsiType;
788 // Create Validation Root, if not exist.
789 // [Schema Validity Assessment (Element) 1.1]
790 if (Context.Element == null) {
791 state.CurrentElement = FindElement (reader.LocalName, reader.NamespaceURI);
792 Context.PushCurrentElement (state.CurrentElement);
794 if (Context.Element != null) {
795 if (Context.XsiType == null) {
796 AssessElementLocallyValidElement (xsiNilValue); // 1.1.2
798 } else {
799 switch (state.ProcessContents) {
800 case ContentProc.Skip:
801 break;
802 case ContentProc.Lax:
803 break;
804 default:
805 if (xsiTypeName == null &&
806 (schemas.Contains (reader.NamespaceURI) ||
807 !schemas.MissedSubComponents (reader.NamespaceURI)))
808 HandleError ("Element declaration for " + new QName (reader.LocalName, reader.NamespaceURI) + " is missing.");
809 break;
813 state.PushContext ();
815 XsdValidationState next = null;
816 if (state.ProcessContents == ContentProc.Skip)
817 skipValidationDepth = reader.Depth;
818 else {
819 // create child particle state.
820 ComplexType xsComplexType = SchemaType as ComplexType;
821 if (xsComplexType != null)
822 next = state.Create (xsComplexType.ValidatableParticle);
823 else if (state.ProcessContents == ContentProc.Lax)
824 next = state.Create (XmlSchemaAny.AnyTypeContent);
825 else
826 next = state.Create (XmlSchemaParticle.Empty);
828 Context.State = next;
830 #region Key Constraints
831 if (checkKeyConstraints) {
832 ValidateKeySelectors ();
833 ValidateKeyFields ();
835 #endregion
839 // 3.3.4 Element Locally Valid (Element)
840 private void AssessElementLocallyValidElement (string xsiNilValue)
842 XsElement element = Context.Element;
843 QName qname = new QName (reader.LocalName, reader.NamespaceURI);
844 // 1.
845 if (element == null)
846 HandleError ("Element declaration is required for " + qname);
847 // 2.
848 if (element.ActualIsAbstract)
849 HandleError ("Abstract element declaration was specified for " + qname);
850 // 3.1.
851 if (!element.ActualIsNillable && xsiNilValue != null)
852 HandleError ("This element declaration is not nillable: " + qname);
853 // 3.2.
854 // Note that 3.2.1 xsi:nil constraints are to be
855 // validated in AssessElementSchemaValidity() and
856 // ValidateCharacters().
857 else if (xsiNilValue == "true") {
858 if (element.ValidatedFixedValue != null)
859 HandleError ("Schema instance nil was specified, where the element declaration for " + qname + "has fixed value constraints.");
861 // 4. xsi:type (it takes precedence than element type)
862 string xsiType = reader.GetAttribute ("type", XmlSchema.InstanceNamespace);
863 if (xsiType != null) {
864 Context.XsiType = GetXsiType (xsiType);
865 AssessLocalTypeDerivationOK (Context.XsiType, element.ElementType, element.BlockResolved);
867 else
868 Context.XsiType = null;
870 // 5 Not all things cannot be assessed here.
871 // It is common to 5.1 and 5.2
872 if (element.ElementType != null)
873 AssessStartElementLocallyValidType (SchemaType);
875 // 6. should be out from here.
876 // See invokation of AssessStartIdentityConstraints().
878 // 7 is going to be validated in Read() (in case of xmlreader's EOF).
881 // 3.3.4 Element Locally Valid (Type)
882 private void AssessStartElementLocallyValidType (object schemaType)
884 if (schemaType == null) { // 1.
885 HandleError ("Schema type does not exist.");
886 return;
888 ComplexType cType = schemaType as ComplexType;
889 SimpleType sType = schemaType as SimpleType;
890 if (sType != null) {
891 // 3.1.1.
892 while (reader.MoveToNextAttribute ()) {
893 if (reader.NamespaceURI == XmlNamespaceManager.XmlnsXmlns)
894 continue;
895 if (reader.NamespaceURI != XmlSchema.InstanceNamespace)
896 HandleError ("Current simple type cannot accept attributes other than schema instance namespace.");
897 switch (reader.LocalName) {
898 case "type":
899 case "nil":
900 case "schemaLocation":
901 case "noNamespaceSchemaLocation":
902 break;
903 default:
904 HandleError ("Unknown schema instance namespace attribute: " + reader.LocalName);
905 break;
908 reader.MoveToElement ();
909 // 3.1.2 and 3.1.3 cannot be assessed here.
910 } else if (cType != null) {
911 if (cType.IsAbstract) { // 2.
912 HandleError ("Target complex type is abstract.");
913 return;
915 // 3.2
916 AssessElementLocallyValidComplexType (cType);
920 // 3.4.4 Element Locally Valid (Complex Type)
921 private void AssessElementLocallyValidComplexType (ComplexType cType)
923 // 1.
924 if (cType.IsAbstract)
925 HandleError ("Target complex type is abstract.");
927 // 2 (xsi:nil and content prohibition)
928 // See AssessStartElementSchemaValidity() and ValidateCharacters()
930 // 3. attribute uses and
931 // 5. wild IDs
932 if (reader.MoveToFirstAttribute ()) {
933 do {
934 switch (reader.NamespaceURI) {
935 case"http://www.w3.org/2000/xmlns/":
936 case XmlSchema.InstanceNamespace:
937 continue;
939 QName qname = new QName (reader.LocalName, reader.NamespaceURI);
940 // including 3.10.4 Item Valid (Wildcard)
941 XmlSchemaObject attMatch = XmlSchemaUtil.FindAttributeDeclaration (reader.NamespaceURI, schemas, cType, qname);
942 if (attMatch == null)
943 HandleError ("Attribute declaration was not found for " + qname);
944 XsAttribute attdecl = attMatch as XsAttribute;
945 if (attdecl != null) {
946 AssessAttributeLocallyValidUse (attdecl);
947 AssessAttributeLocallyValid (attdecl);
948 } // otherwise anyAttribute or null.
949 } while (reader.MoveToNextAttribute ());
950 reader.MoveToElement ();
953 // Collect default attributes.
954 // 4.
955 foreach (DictionaryEntry entry in cType.AttributeUses) {
956 XsAttribute attr = (XsAttribute) entry.Value;
957 if (reader [attr.QualifiedName.Name, attr.QualifiedName.Namespace] == null) {
958 if (attr.ValidatedUse == XmlSchemaUse.Required &&
959 attr.ValidatedFixedValue == null)
960 HandleError ("Required attribute " + attr.QualifiedName + " was not found.");
961 else if (attr.ValidatedDefaultValue != null || attr.ValidatedFixedValue != null)
962 defaultAttributesCache.Add (attr);
965 if (defaultAttributesCache.Count == 0)
966 defaultAttributes = emptyAttributeArray;
967 else
968 defaultAttributes = (XsAttribute [])
969 defaultAttributesCache.ToArray (
970 typeof (XsAttribute));
971 defaultAttributesCache.Clear ();
972 // 5. wild IDs was already checked above.
975 // 3.2.4 Attribute Locally Valid and 3.4.4
976 private void AssessAttributeLocallyValid (XsAttribute attr)
978 // 2. - 4.
979 if (attr.AttributeType == null)
980 HandleError ("Attribute type is missing for " + attr.QualifiedName);
981 XsDatatype dt = attr.AttributeType as XsDatatype;
982 if (dt == null)
983 dt = ((SimpleType) attr.AttributeType).Datatype;
984 // It is a bit heavy process, so let's omit as long as possible ;-)
985 if (dt != SimpleType.AnySimpleType || attr.ValidatedFixedValue != null) {
986 string normalized = dt.Normalize (reader.Value);
987 object parsedValue = null;
989 // check part of 3.14.4 StringValid
990 SimpleType st = attr.AttributeType as SimpleType;
991 if (st != null)
992 ValidateRestrictedSimpleTypeValue (st, ref dt, normalized);
994 try {
995 parsedValue = dt.ParseValue (normalized, reader.NameTable, NamespaceManager);
996 } catch (Exception ex) { // FIXME: (wishlist) It is bad manner ;-(
997 HandleError ("Attribute value is invalid against its data type " + dt.TokenizedType, ex);
1000 if (attr.ValidatedFixedValue != null &&
1001 attr.ValidatedFixedValue != normalized) {
1002 HandleError ("The value of the attribute " + attr.QualifiedName + " does not match with its fixed value.");
1003 parsedValue = dt.ParseValue (attr.ValidatedFixedValue, reader.NameTable, NamespaceManager);
1005 #region ID Constraints
1006 if (this.checkIdentity) {
1007 string error = idManager.AssessEachAttributeIdentityConstraint (dt, parsedValue, ((QName) elementQNameStack [elementQNameStack.Count - 1]).Name);
1008 if (error != null)
1009 HandleError (error);
1011 #endregion
1015 private void AssessAttributeLocallyValidUse (XsAttribute attr)
1017 // This is extra check than spec 3.5.4
1018 if (attr.ValidatedUse == XmlSchemaUse.Prohibited)
1019 HandleError ("Attribute " + attr.QualifiedName + " is prohibited in this context.");
1022 private void AssessEndElementSchemaValidity ()
1024 ValidateEndSimpleContent ();
1026 ValidateEndElementParticle (); // validate against childrens' state.
1028 // 3.3.4 Assess ElementLocallyValidElement 5: value constraints.
1029 // 3.3.4 Assess ElementLocallyValidType 3.1.3. = StringValid(3.14.4)
1030 // => ValidateEndSimpleContent().
1032 #region Key Constraints
1033 if (checkKeyConstraints)
1034 ValidateEndElementKeyConstraints ();
1035 #endregion
1037 // Reset xsi:nil, if required.
1038 if (xsiNilDepth == reader.Depth)
1039 xsiNilDepth = -1;
1042 #region Key Constraints
1043 private void ValidateEndElementKeyConstraints ()
1045 // Reset Identity constraints.
1046 for (int i = 0; i < keyTables.Count; i++) {
1047 XsdKeyTable seq = this.keyTables [i] as XsdKeyTable;
1048 if (seq.StartDepth == reader.Depth) {
1049 EndIdentityValidation (seq);
1050 } else {
1051 for (int k = 0; k < seq.Entries.Count; k++) {
1052 XsdKeyEntry entry = seq.Entries [k] as XsdKeyEntry;
1053 // Remove finished (maybe key not found) entries.
1054 if (entry.StartDepth == reader.Depth) {
1055 if (entry.KeyFound)
1056 seq.FinishedEntries.Add (entry);
1057 else if (seq.SourceSchemaIdentity is XmlSchemaKey)
1058 HandleError ("Key sequence is missing.");
1059 seq.Entries.RemoveAt (k);
1060 k--;
1062 // Pop validated key depth to find two or more fields.
1063 else {
1064 for (int j = 0; j < entry.KeyFields.Count; j++) {
1065 XsdKeyEntryField kf = entry.KeyFields [j];
1066 if (!kf.FieldFound && kf.FieldFoundDepth == reader.Depth) {
1067 kf.FieldFoundDepth = 0;
1068 kf.FieldFoundPath = null;
1075 for (int i = 0; i < keyTables.Count; i++) {
1076 XsdKeyTable seq = this.keyTables [i] as XsdKeyTable;
1077 if (seq.StartDepth == reader.Depth) {
1078 keyTables.RemoveAt (i);
1079 i--;
1084 // 3.11.4 Identity Constraint Satisfied
1085 private void ValidateKeySelectors ()
1087 if (tmpKeyrefPool != null)
1088 tmpKeyrefPool.Clear ();
1089 if (Context.Element != null && Context.Element.Constraints.Count > 0) {
1090 // (a) Create new key sequences, if required.
1091 for (int i = 0; i < Context.Element.Constraints.Count; i++) {
1092 XmlSchemaIdentityConstraint ident = (XmlSchemaIdentityConstraint) Context.Element.Constraints [i];
1093 XsdKeyTable seq = CreateNewKeyTable (ident);
1094 if (ident is XmlSchemaKeyref) {
1095 if (tmpKeyrefPool == null)
1096 tmpKeyrefPool = new ArrayList ();
1097 tmpKeyrefPool.Add (seq);
1102 // (b) Evaluate current key sequences.
1103 for (int i = 0; i < keyTables.Count; i++) {
1104 XsdKeyTable seq = (XsdKeyTable) keyTables [i];
1105 if (seq.SelectorMatches (this.elementQNameStack, reader.Depth) != null) {
1106 // creates and registers new entry.
1107 XsdKeyEntry entry = new XsdKeyEntry (seq, reader.Depth, readerLineInfo);
1108 seq.Entries.Add (entry);
1113 private void ValidateKeyFields ()
1115 // (c) Evaluate field paths.
1116 for (int i = 0; i < keyTables.Count; i++) {
1117 XsdKeyTable seq = (XsdKeyTable) keyTables [i];
1118 // If possible, create new field entry candidates.
1119 for (int j = 0; j < seq.Entries.Count; j++) {
1120 try {
1121 ProcessKeyEntry (seq.Entries [j]);
1122 } catch (ValException ex) {
1123 HandleError (ex);
1129 private void ProcessKeyEntry (XsdKeyEntry entry)
1131 bool isNil = XsiNilDepth == Depth;
1132 entry.ProcessMatch (false, elementQNameStack, this, NameTable, BaseURI, SchemaType, NamespaceManager, readerLineInfo, Depth, null, null, null, isNil, CurrentKeyFieldConsumers);
1133 if (MoveToFirstAttribute ()) {
1134 try {
1135 do {
1136 switch (NamespaceURI) {
1137 case XmlNamespaceManager.XmlnsXmlns:
1138 case XmlSchema.InstanceNamespace:
1139 continue;
1141 XmlSchemaDatatype dt = SchemaType as XmlSchemaDatatype;
1142 XmlSchemaSimpleType st = SchemaType as XmlSchemaSimpleType;
1143 if (dt == null && st != null)
1144 dt = st.Datatype;
1145 object identity = null;
1146 if (dt != null)
1147 identity = dt.ParseValue (Value, NameTable, NamespaceManager);
1148 if (identity == null)
1149 identity = Value;
1150 entry.ProcessMatch (true, elementQNameStack, this, NameTable, BaseURI, SchemaType, NamespaceManager, readerLineInfo, Depth, LocalName, NamespaceURI, identity, false, CurrentKeyFieldConsumers);
1151 } while (MoveToNextAttribute ());
1152 } finally {
1153 MoveToElement ();
1158 private XsdKeyTable CreateNewKeyTable (XmlSchemaIdentityConstraint ident)
1160 XsdKeyTable seq = new XsdKeyTable (ident);
1161 seq.StartDepth = reader.Depth;
1162 this.keyTables.Add (seq);
1163 return seq;
1166 private void ValidateSimpleContentIdentity (
1167 XmlSchemaDatatype dt, string value)
1169 // Identity field value
1170 if (currentKeyFieldConsumers != null) {
1171 while (this.currentKeyFieldConsumers.Count > 0) {
1172 XsdKeyEntryField field = this.currentKeyFieldConsumers [0] as XsdKeyEntryField;
1173 if (field.Identity != null)
1174 HandleError ("Two or more identical field was found. Former value is '" + field.Identity + "' .");
1175 object identity = null; // This means empty value
1176 if (dt != null) {
1177 try {
1178 identity = dt.ParseValue (value, NameTable, NamespaceManager);
1179 } catch (Exception ex) { // FIXME: (wishlist) This is bad manner ;-(
1180 HandleError ("Identity value is invalid against its data type " + dt.TokenizedType, ex);
1183 if (identity == null)
1184 identity = value;
1186 if (!field.SetIdentityField (identity, reader.Depth == xsiNilDepth, dt as XsdAnySimpleType, this.Depth, readerLineInfo))
1187 HandleError ("Two or more identical key value was found: '" + value + "' .");
1188 this.currentKeyFieldConsumers.RemoveAt (0);
1193 private void EndIdentityValidation (XsdKeyTable seq)
1195 ArrayList errors = null;
1196 for (int i = 0; i < seq.Entries.Count; i++) {
1197 XsdKeyEntry entry = (XsdKeyEntry) seq.Entries [i];
1198 if (entry.KeyFound)
1199 continue;
1200 if (seq.SourceSchemaIdentity is XmlSchemaKey) {
1201 if (errors == null)
1202 errors = new ArrayList ();
1203 errors.Add ("line " + entry.SelectorLineNumber + "position " + entry.SelectorLinePosition);
1206 if (errors != null)
1207 HandleError ("Invalid identity constraints were found. Key was not found. "
1208 + String.Join (", ", errors.ToArray (typeof (string)) as string []));
1210 // If it is keyref, then find reference target
1211 XmlSchemaKeyref xsdKeyref = seq.SourceSchemaIdentity as XmlSchemaKeyref;
1212 if (xsdKeyref != null)
1213 EndKeyrefValidation (seq, xsdKeyref.Target);
1216 private void EndKeyrefValidation (XsdKeyTable seq, XmlSchemaIdentityConstraint targetIdent)
1218 for (int i = this.keyTables.Count - 1; i >= 0; i--) {
1219 XsdKeyTable target = this.keyTables [i] as XsdKeyTable;
1220 if (target.SourceSchemaIdentity != targetIdent)
1221 continue;
1222 seq.ReferencedKey = target;
1223 for (int j = 0; j < seq.FinishedEntries.Count; j++) {
1224 XsdKeyEntry entry = (XsdKeyEntry) seq.FinishedEntries [j];
1225 for (int k = 0; k < target.FinishedEntries.Count; k++) {
1226 XsdKeyEntry targetEntry = (XsdKeyEntry) target.FinishedEntries [k];
1227 if (entry.CompareIdentity (targetEntry)) {
1228 entry.KeyRefFound = true;
1229 break;
1234 if (seq.ReferencedKey == null)
1235 HandleError ("Target key was not found.");
1236 ArrayList errors = null;
1237 for (int i = 0; i < seq.FinishedEntries.Count; i++) {
1238 XsdKeyEntry entry = (XsdKeyEntry) seq.FinishedEntries [i];
1239 if (!entry.KeyRefFound) {
1240 if (errors == null)
1241 errors = new ArrayList ();
1242 errors.Add (" line " + entry.SelectorLineNumber + ", position " + entry.SelectorLinePosition);
1245 if (errors != null)
1246 HandleError ("Invalid identity constraints were found. Referenced key was not found: "
1247 + String.Join (" / ", errors.ToArray (typeof (string)) as string []));
1249 #endregion
1251 // Overrided Methods
1253 public override void Close ()
1255 reader.Close ();
1258 public override string GetAttribute (int i)
1260 switch (reader.NodeType) {
1261 case XmlNodeType.XmlDeclaration:
1262 case XmlNodeType.DocumentType:
1263 return reader.GetAttribute (i);
1266 if (reader.AttributeCount > i)
1267 reader.GetAttribute (i);
1268 int defIdx = i - reader.AttributeCount;
1269 if (i < AttributeCount)
1270 return defaultAttributes [defIdx].DefaultValue;
1272 throw new ArgumentOutOfRangeException ("i", i, "Specified attribute index is out of range.");
1275 public override string GetAttribute (string name)
1277 switch (reader.NodeType) {
1278 case XmlNodeType.XmlDeclaration:
1279 case XmlNodeType.DocumentType:
1280 return reader.GetAttribute (name);
1283 string value = reader.GetAttribute (name);
1284 if (value != null)
1285 return value;
1287 QName qname = SplitQName (name);
1288 return GetDefaultAttribute (qname.Name, qname.Namespace);
1291 private QName SplitQName (string name)
1293 if (!XmlChar.IsName (name))
1294 throw new ArgumentException ("Invalid name was specified.", "name");
1296 Exception ex = null;
1297 QName qname = XmlSchemaUtil.ToQName (reader, name, out ex);
1298 if (ex != null)
1299 return QName.Empty;
1300 else
1301 return qname;
1304 public override string GetAttribute (string localName, string ns)
1306 switch (reader.NodeType) {
1307 case XmlNodeType.XmlDeclaration:
1308 case XmlNodeType.DocumentType:
1309 return reader.GetAttribute (localName, ns);
1312 string value = reader.GetAttribute (localName, ns);
1313 if (value != null)
1314 return value;
1316 return GetDefaultAttribute (localName, ns);
1319 private string GetDefaultAttribute (string localName, string ns)
1321 int idx = this.FindDefaultAttribute (localName, ns);
1322 if (idx < 0)
1323 return null;
1324 string value = defaultAttributes [idx].ValidatedDefaultValue;
1325 if (value == null)
1326 value = defaultAttributes [idx].ValidatedFixedValue;
1327 return value;
1330 private int FindDefaultAttribute (string localName, string ns)
1332 for (int i = 0; i < this.defaultAttributes.Length; i++) {
1333 XsAttribute attr = defaultAttributes [i];
1334 if (attr.QualifiedName.Name == localName &&
1335 (ns == null || attr.QualifiedName.Namespace == ns))
1336 return i;
1338 return -1;
1341 public bool HasLineInfo ()
1343 return readerLineInfo != null && readerLineInfo.HasLineInfo ();
1346 public override string LookupNamespace (string prefix)
1348 return reader.LookupNamespace (prefix);
1351 public override void MoveToAttribute (int i)
1353 switch (reader.NodeType) {
1354 case XmlNodeType.XmlDeclaration:
1355 case XmlNodeType.DocumentType:
1356 reader.MoveToAttribute (i);
1357 return;
1360 currentAttrType = null;
1361 if (i < reader.AttributeCount) {
1362 reader.MoveToAttribute (i);
1363 this.currentDefaultAttribute = -1;
1364 this.defaultAttributeConsumed = false;
1367 if (i < AttributeCount) {
1368 this.currentDefaultAttribute = i - reader.AttributeCount;
1369 this.defaultAttributeConsumed = false;
1371 else
1372 throw new ArgumentOutOfRangeException ("i", i, "Attribute index is out of range.");
1375 public override bool MoveToAttribute (string name)
1377 switch (reader.NodeType) {
1378 case XmlNodeType.XmlDeclaration:
1379 case XmlNodeType.DocumentType:
1380 return reader.MoveToAttribute (name);
1383 currentAttrType = null;
1384 bool b = reader.MoveToAttribute (name);
1385 if (b) {
1386 this.currentDefaultAttribute = -1;
1387 this.defaultAttributeConsumed = false;
1388 return true;
1391 return MoveToDefaultAttribute (name, null);
1394 public override bool MoveToAttribute (string localName, string ns)
1396 switch (reader.NodeType) {
1397 case XmlNodeType.XmlDeclaration:
1398 case XmlNodeType.DocumentType:
1399 return reader.MoveToAttribute (localName, ns);
1402 currentAttrType = null;
1403 bool b = reader.MoveToAttribute (localName, ns);
1404 if (b) {
1405 this.currentDefaultAttribute = -1;
1406 this.defaultAttributeConsumed = false;
1407 return true;
1410 return MoveToDefaultAttribute (localName, ns);
1413 private bool MoveToDefaultAttribute (string localName, string ns)
1415 int idx = this.FindDefaultAttribute (localName, ns);
1416 if (idx < 0)
1417 return false;
1418 currentDefaultAttribute = idx;
1419 defaultAttributeConsumed = false;
1420 return true;
1423 public override bool MoveToElement ()
1425 currentDefaultAttribute = -1;
1426 defaultAttributeConsumed = false;
1427 currentAttrType = null;
1428 return reader.MoveToElement ();
1431 public override bool MoveToFirstAttribute ()
1433 switch (reader.NodeType) {
1434 case XmlNodeType.XmlDeclaration:
1435 case XmlNodeType.DocumentType:
1436 return reader.MoveToFirstAttribute ();
1439 currentAttrType = null;
1440 if (reader.AttributeCount > 0) {
1441 bool b = reader.MoveToFirstAttribute ();
1442 if (b) {
1443 currentDefaultAttribute = -1;
1444 defaultAttributeConsumed = false;
1446 return b;
1449 if (this.defaultAttributes.Length > 0) {
1450 currentDefaultAttribute = 0;
1451 defaultAttributeConsumed = false;
1452 return true;
1454 else
1455 return false;
1458 public override bool MoveToNextAttribute ()
1460 switch (reader.NodeType) {
1461 case XmlNodeType.XmlDeclaration:
1462 case XmlNodeType.DocumentType:
1463 return reader.MoveToNextAttribute ();
1466 currentAttrType = null;
1467 if (currentDefaultAttribute >= 0) {
1468 if (defaultAttributes.Length == currentDefaultAttribute + 1)
1469 return false;
1470 currentDefaultAttribute++;
1471 defaultAttributeConsumed = false;
1472 return true;
1475 bool b = reader.MoveToNextAttribute ();
1476 if (b) {
1477 currentDefaultAttribute = -1;
1478 defaultAttributeConsumed = false;
1479 return true;
1482 if (defaultAttributes.Length > 0) {
1483 currentDefaultAttribute = 0;
1484 defaultAttributeConsumed = false;
1485 return true;
1487 else
1488 return false;
1491 private XmlSchema ReadExternalSchema (string uri)
1493 Uri absUri = resolver.ResolveUri ((BaseURI != "" ? new Uri (BaseURI) : null), uri);
1494 string absUriString = absUri != null ? absUri.ToString () : String.Empty;
1495 XmlTextReader xtr = null;
1496 try {
1497 xtr = new XmlTextReader (absUriString,
1498 (Stream) resolver.GetEntity (
1499 absUri, null, typeof (Stream)),
1500 NameTable);
1501 return XmlSchema.Read (
1502 xtr, ValidationEventHandler);
1503 } finally {
1504 if (xtr != null)
1505 xtr.Close ();
1509 private void ExamineAdditionalSchema ()
1511 if (resolver == null || ValidationType == ValidationType.None)
1512 return;
1513 XmlSchema schema = null;
1514 string schemaLocation = reader.GetAttribute ("schemaLocation", XmlSchema.InstanceNamespace);
1515 bool schemaAdded = false;
1516 if (schemaLocation != null) {
1517 string [] tmp = null;
1518 try {
1519 schemaLocation = XsDatatype.FromName ("token", XmlSchema.Namespace).Normalize (schemaLocation);
1520 tmp = schemaLocation.Split (XmlChar.WhitespaceChars);
1521 } catch (Exception ex) {
1522 if (schemas.Count == 0)
1523 HandleError ("Invalid schemaLocation attribute format.", ex, true);
1524 tmp = new string [0];
1526 if (tmp.Length % 2 != 0)
1527 if (schemas.Count == 0)
1528 HandleError ("Invalid schemaLocation attribute format.");
1529 int i=0;
1530 do {
1531 try {
1532 for (; i < tmp.Length; i += 2) {
1533 schema = ReadExternalSchema (tmp [i + 1]);
1534 if (schema.TargetNamespace == null)
1535 schema.TargetNamespace = tmp [i];
1536 else if (schema.TargetNamespace != tmp [i])
1537 HandleError ("Specified schema has different target namespace.");
1538 if (schema != null) {
1539 if (!schemas.Contains (schema.TargetNamespace)) {
1540 schemaAdded = true;
1541 schemas.Add (schema);
1543 schema = null;
1546 } catch (Exception) {
1547 if (!schemas.Contains (tmp [i]))
1548 HandleError (String.Format ("Could not resolve schema location URI: {0}",
1549 i + 1 < tmp.Length ? tmp [i + 1] : String.Empty), null, true);
1550 i += 2;
1551 continue;
1553 } while (i < tmp.Length);
1555 string noNsSchemaLocation = reader.GetAttribute ("noNamespaceSchemaLocation", XmlSchema.InstanceNamespace);
1556 if (noNsSchemaLocation != null) {
1557 try {
1558 schema = ReadExternalSchema (noNsSchemaLocation);
1559 } catch (Exception) { // FIXME: (wishlist) It is bad manner ;-(
1560 if (schemas.Count != 0)
1561 HandleError ("Could not resolve schema location URI: " + noNsSchemaLocation, null, true);
1563 if (schema != null && schema.TargetNamespace != null)
1564 HandleError ("Specified schema has different target namespace.");
1566 if (schema != null) {
1567 if (!schemas.Contains (schema.TargetNamespace)) {
1568 schemaAdded = true;
1569 schemas.Add (schema);
1572 // FIXME: should call Reprocess()?
1573 if (schemaAdded)
1574 schemas.Compile ();
1577 public override bool Read ()
1579 validationStarted = true;
1580 currentDefaultAttribute = -1;
1581 defaultAttributeConsumed = false;
1582 currentAttrType = null;
1583 defaultAttributes = emptyAttributeArray;
1585 bool result = reader.Read ();
1587 // FIXME: schemaLocation could be specified
1588 // at any Depth.
1589 if (reader.Depth == 0 &&
1590 reader.NodeType == XmlNodeType.Element) {
1591 // If the reader is DTDValidatingReader (it
1592 // is the default behavior of
1593 // XmlValidatingReader) and DTD didn't appear,
1594 // we could just use its source XmlReader.
1595 DTDValidatingReader dtdr = reader as DTDValidatingReader;
1596 if (dtdr != null && dtdr.DTD == null)
1597 reader = dtdr.Source;
1599 ExamineAdditionalSchema ();
1601 if (schemas.Count == 0)
1602 return result;
1603 if (!schemas.IsCompiled)
1604 schemas.Compile ();
1606 #region ID Constraints
1607 if (this.checkIdentity)
1608 idManager.OnStartElement ();
1610 // 3.3.4 ElementLocallyValidElement 7 = Root Valid.
1611 if (!result && this.checkIdentity &&
1612 idManager.HasMissingIDReferences ())
1613 HandleError ("There are missing ID references: " + idManager.GetMissingIDString ());
1614 #endregion
1616 switch (reader.NodeType) {
1617 case XmlNodeType.Element:
1618 #region Key Constraints
1619 if (checkKeyConstraints)
1620 this.elementQNameStack.Add (new QName (reader.LocalName, reader.NamespaceURI));
1621 #endregion
1623 // If there is no schema information, then no validation is performed.
1624 if (skipValidationDepth < 0 || reader.Depth <= skipValidationDepth) {
1625 ValidateEndSimpleContent ();
1626 AssessStartElementSchemaValidity ();
1629 if (reader.IsEmptyElement)
1630 goto case XmlNodeType.EndElement;
1631 else if (xsiNilDepth < reader.Depth)
1632 shouldValidateCharacters = true;
1633 break;
1634 case XmlNodeType.EndElement:
1635 if (reader.Depth == skipValidationDepth)
1636 skipValidationDepth = -1;
1637 else if (skipValidationDepth < 0 || reader.Depth <= skipValidationDepth)
1638 AssessEndElementSchemaValidity ();
1640 if (checkKeyConstraints)
1641 elementQNameStack.RemoveAt (elementQNameStack.Count - 1);
1642 break;
1644 case XmlNodeType.CDATA:
1645 case XmlNodeType.SignificantWhitespace:
1646 case XmlNodeType.Whitespace:
1647 case XmlNodeType.Text:
1648 if (skipValidationDepth >= 0 && reader.Depth > skipValidationDepth)
1649 break;
1651 ComplexType ct = Context.ActualType as ComplexType;
1652 if (ct != null) {
1653 switch (ct.ContentType) {
1654 case XmlSchemaContentType.ElementOnly:
1655 if (reader.NodeType != XmlNodeType.Whitespace)
1656 HandleError (String.Format ("Not allowed character content is found (current content model '{0}' is element-only).", ct.QualifiedName));
1657 break;
1658 case XmlSchemaContentType.Empty:
1659 HandleError (String.Format ("Not allowed character content is found (current element content model '{0}' is empty).", ct.QualifiedName));
1660 break;
1664 ValidateCharacters ();
1665 break;
1668 return result;
1671 public override bool ReadAttributeValue ()
1673 if (currentDefaultAttribute < 0)
1674 return reader.ReadAttributeValue ();
1676 if (this.defaultAttributeConsumed)
1677 return false;
1679 defaultAttributeConsumed = true;
1680 return true;
1683 #if NET_1_0
1684 public override string ReadInnerXml ()
1686 // MS.NET 1.0 has a serious bug here. It skips validation.
1687 return reader.ReadInnerXml ();
1690 public override string ReadOuterXml ()
1692 // MS.NET 1.0 has a serious bug here. It skips validation.
1693 return reader.ReadOuterXml ();
1695 #endif
1697 // XmlReader.ReadString() should call derived this.Read().
1698 public override string ReadString ()
1700 #if NET_1_0
1701 return reader.ReadString ();
1702 #else
1703 return base.ReadString ();
1704 #endif
1707 // This class itself does not have this feature.
1708 public override void ResolveEntity ()
1710 reader.ResolveEntity ();
1714 internal class XsdValidationContext
1716 public XsdValidationContext ()
1720 object xsi_type;
1721 public object XsiType { get { return xsi_type; } set { xsi_type = value; } } // xsi:type
1722 internal XsdValidationState State;
1723 Stack element_stack = new Stack ();
1725 // Some of them might be missing (See the spec section 5.3, and also 3.3.4).
1726 public XsElement Element {
1727 get { return element_stack.Count > 0 ? element_stack.Peek () as XsElement : null; }
1730 public void PushCurrentElement (XsElement element)
1732 element_stack.Push (element);
1735 public void PopCurrentElement ()
1737 element_stack.Pop ();
1740 // Note that it represents current element's type.
1741 public object ActualType {
1742 get {
1743 // FIXME: actually this should also be stacked
1744 if (element_stack.Count == 0)
1745 return null;
1746 if (XsiType != null)
1747 return XsiType;
1748 else
1749 return Element != null ? Element.ElementType : null;
1753 #if NET_2_0
1754 public XmlSchemaType ActualSchemaType {
1755 get {
1756 object at = ActualType;
1757 if (at == null)
1758 return null;
1759 XmlSchemaType st = at as XmlSchemaType;
1760 if (st == null)
1761 st = XmlSchemaType.GetBuiltInSimpleType (
1762 ((XmlSchemaDatatype) at).TypeCode);
1763 return st;
1766 #endif
1768 public bool IsInvalid {
1769 get { return State == XsdValidationState.Invalid; }
1772 public object Clone ()
1774 return MemberwiseClone ();
1777 public void EvaluateStartElement (
1778 string localName, string ns)
1780 State = State.EvaluateStartElement (localName, ns);
1783 public bool EvaluateEndElement ()
1785 return State.EvaluateEndElement ();
1789 internal class XsdIDManager
1791 public XsdIDManager ()
1795 Hashtable idList = new Hashtable ();
1796 ArrayList missingIDReferences;
1797 string thisElementId;
1799 private ArrayList MissingIDReferences {
1800 get {
1801 if (missingIDReferences == null)
1802 missingIDReferences = new ArrayList ();
1803 return missingIDReferences;
1807 public void OnStartElement ()
1809 thisElementId = null;
1812 // 3.4.4-5 wild IDs
1813 public string AssessEachAttributeIdentityConstraint (
1814 XsDatatype dt, object parsedValue, string elementName)
1816 // Validate identity constraints.
1817 string str = parsedValue as string;
1818 switch (dt.TokenizedType) {
1819 case XmlTokenizedType.ID:
1820 if (thisElementId != null)
1821 return "ID type attribute was already assigned in the containing element.";
1822 else
1823 thisElementId = str;
1824 if (idList.ContainsKey (str))
1825 return "Duplicate ID value was found.";
1826 else
1827 idList.Add (str, elementName);
1828 if (MissingIDReferences.Contains (str))
1829 MissingIDReferences.Remove (str);
1830 break;
1831 case XmlTokenizedType.IDREF:
1832 if (!idList.Contains (str) && !MissingIDReferences.Contains (str))
1833 MissingIDReferences.Add (str);
1834 break;
1835 case XmlTokenizedType.IDREFS:
1836 string [] idrefs = (string []) parsedValue;
1837 for (int i = 0; i < idrefs.Length; i++) {
1838 string id = idrefs [i];
1839 if (!idList.Contains (id) && !MissingIDReferences.Contains (str))
1840 MissingIDReferences.Add (id);
1842 break;
1844 return null;
1847 public bool HasMissingIDReferences ()
1849 return missingIDReferences != null
1850 && missingIDReferences.Count > 0;
1853 public string GetMissingIDString ()
1855 return String.Join (" ",
1856 MissingIDReferences.ToArray (typeof (string))
1857 as string []);