2 // Mono.Xml.Schema.XsdValidatingReader.cs
5 // Atsushi Enomoto (ginga@kit.hi-ho.ne.jp)
7 // (C)2003 Atsushi Enomoto
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:
19 // The above copyright notice and this permission notice shall be
20 // included in all copies or substantial portions of the Software.
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.
31 using System
.Collections
;
33 using System
.Collections
.Generic
;
35 using System
.Collections
.Specialized
;
39 using System
.Xml
.Schema
;
43 using ValException
= System
.Xml
.Schema
.XmlSchemaValidationException
;
45 using ValException
= System
.Xml
.Schema
.XmlSchemaException
;
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
=
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 ();
80 #region Key Constraints
81 bool checkKeyConstraints
= true;
82 ArrayList keyTables
= new ArrayList ();
83 ArrayList currentKeyFieldConsumers
;
84 ArrayList tmpKeyrefPool
;
86 ArrayList elementQNameStack
= new ArrayList ();
88 XsdParticleStateManager state
= new XsdParticleStateManager ();
90 int skipValidationDepth
= -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
;
102 public XsdValidatingReader (XmlReader reader
)
104 this.reader
= reader
;
105 readerLineInfo
= reader
as IXmlLineInfo
;
106 sourceReaderSchemaInfo
= reader
as IHasXmlSchemaInfo
;
107 schemas
.ValidationEventHandler
+= ValidationEventHandler
;
111 public ValidationEventHandler ValidationEventHandler
;
113 // Private Properties
115 private XsdValidationContext Context
{
116 get { return state.Context; }
119 #region Key Constraints
120 internal ArrayList CurrentKeyFieldConsumers
{
122 if (currentKeyFieldConsumers
== null)
123 currentKeyFieldConsumers
= new ArrayList ();
124 return currentKeyFieldConsumers
;
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
{
147 // This should be changed before the first Read() call.
148 public XmlSchemaSet Schemas
{
149 get { return schemas; }
151 if (validationStarted
)
152 throw new InvalidOperationException ("Schemas must be set before the first call to Read().");
157 public object SchemaType
{
159 if (ReadState
!= ReadState
.Interactive
)
163 case XmlNodeType
.Element
:
164 if (Context
.ActualType
!= null)
165 return Context
.ActualType
;
167 return SourceReaderSchemaType
;
168 case XmlNodeType
.Attribute
:
169 if (currentAttrType
== null) {
170 ComplexType ct
= Context
.ActualType
as ComplexType
;
172 XsAttribute attdef
= ct
.AttributeUses
[new QName (LocalName
, NamespaceURI
)] as XsAttribute
;
174 currentAttrType
= attdef
.AttributeType
;
175 return currentAttrType
;
177 currentAttrType
= SourceReaderSchemaType
;
179 return currentAttrType
;
181 return SourceReaderSchemaType
;
186 private object SourceReaderSchemaType
{
187 get { return this.sourceReaderSchemaInfo != null ? sourceReaderSchemaInfo.SchemaType : null; }
190 public ValidationType ValidationType
{
191 get { return validationType; }
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
,
205 storedCharacters
.Length
= 0;
209 // Public Overriden Properties
211 public override int AttributeCount
{
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
{
230 if (currentDefaultAttribute
< 0)
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
{
244 if (currentDefaultAttribute
< 0)
245 return reader
.HasValue
;
250 public override bool IsDefault
{
252 if (currentDefaultAttribute
< 0)
253 return reader
.IsDefault
;
258 public override bool IsEmptyElement
{
260 if (currentDefaultAttribute
< 0)
261 return reader
.IsEmptyElement
;
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
{
288 if (currentDefaultAttribute
< 0)
289 return reader
.LocalName
;
290 if (defaultAttributeConsumed
)
292 return defaultAttributes
[currentDefaultAttribute
].QualifiedName
.Name
;
296 public override string Name
{
298 if (currentDefaultAttribute
< 0)
300 if (defaultAttributeConsumed
)
303 QName qname
= defaultAttributes
[currentDefaultAttribute
].QualifiedName
;
304 string prefix
= Prefix
;
305 if (prefix
== String
.Empty
)
308 return String
.Concat (prefix
, ":", qname
.Name
);
312 public override string NamespaceURI
{
314 if (currentDefaultAttribute
< 0)
315 return reader
.NamespaceURI
;
316 if (defaultAttributeConsumed
)
318 return defaultAttributes
[currentDefaultAttribute
].QualifiedName
.Namespace
;
322 public override XmlNameTable NameTable
{
323 get { return reader.NameTable; }
326 public override XmlNodeType NodeType
{
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
{
346 if (currentDefaultAttribute
< 0)
347 return reader
.Prefix
;
348 if (defaultAttributeConsumed
)
350 QName qname
= defaultAttributes
[currentDefaultAttribute
].QualifiedName
;
351 string prefix
= NamespaceManager
!= null ? NamespaceManager
.LookupPrefix (qname
.Namespace
, false) : null;
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
{
369 if (currentDefaultAttribute
< 0)
371 string value = defaultAttributes
[currentDefaultAttribute
].ValidatedDefaultValue
;
373 value = defaultAttributes
[currentDefaultAttribute
].ValidatedFixedValue
;
378 public override string XmlLang
{
380 string xmlLang
= reader
.XmlLang
;
383 int idx
= this.FindDefaultAttribute ("lang", XmlNamespaceManager
.XmlnsXml
);
386 xmlLang
= defaultAttributes
[idx
].ValidatedDefaultValue
;
388 xmlLang
= defaultAttributes
[idx
].ValidatedFixedValue
;
393 public override XmlSpace XmlSpace
{
395 XmlSpace space
= reader
.XmlSpace
;
396 if (space
!= XmlSpace
.None
)
398 int idx
= this.FindDefaultAttribute ("space", XmlNamespaceManager
.XmlnsXml
);
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);
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
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
)
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
)
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)
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 ();
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)
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
;
525 ComplexType ct
= Context
.ActualType
as ComplexType
;
526 var ctsm
= ct
.ContentModel
as XmlSchemaSimpleContent
;
528 var scr
= ctsm
.Content
as XmlSchemaSimpleContentRestriction
;
530 st
= FindSimpleBaseType (scr
.BaseType
?? FindType (scr
.BaseTypeName
));
531 var sce
= ctsm
.Content
as XmlSchemaSimpleContentExtension
;
533 st
= FindSimpleBaseType (FindType (sce
.BaseTypeName
));
537 switch (ct
.ContentType
) {
538 case XmlSchemaContentType
.ElementOnly
:
539 if (value.Length
> 0 && !XmlChar
.IsWhitespace (value))
540 HandleError ("Character content not allowed.");
542 case XmlSchemaContentType
.Empty
:
543 if (value.Length
> 0)
544 HandleError ("Character content not allowed.");
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);
562 shouldValidateCharacters
= false;
565 SimpleType
FindSimpleBaseType (XmlSchemaType xt
)
567 var st
= xt
as SimpleType
;
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
;
581 string normalized
= validatedDatatype
.Normalize (value);
582 ValidateRestrictedSimpleTypeValue (st
, ref validatedDatatype
, normalized
);
584 if (validatedDatatype
!= null) {
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
)
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
)
609 // validate against ValidatedItemType
610 if (itemDatatype
!= null) {
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
);
619 AssessStringValid (itemSimpleType
, itemSimpleType
.Datatype
, each
);
622 case XmlSchemaDerivationMethod
.Union
:
623 SimpleTypeUnion union
= st
.Content
as SimpleTypeUnion
;
625 string each
= normalized
;
626 // validate against ValidatedItemType
628 foreach (object eachType
in union
.ValidatedTypes
) {
629 itemDatatype
= eachType
as XsDatatype
;
630 itemSimpleType
= eachType
as SimpleType
;
631 if (itemDatatype
!= null) {
633 itemDatatype
.ParseValue (each
, NameTable
, NamespaceManager
);
634 } catch (Exception
) { // FIXME: (wishlist) better exception handling ;-(
640 AssessStringValid (itemSimpleType
, itemSimpleType
.Datatype
, each
);
641 } catch (ValException
) {
649 HandleError ("Union type value contains one or more invalid values.");
654 case XmlSchemaDerivationMethod
.Restriction
:
655 SimpleTypeRest str
= st
.Content
as SimpleTypeRest
;
658 /* Don't forget to validate against inherited type's facets
659 * Could we simplify this by assuming that the basetype will also
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.");
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
);
687 xsiType
= FindType (typeQName
);
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.");
704 } else if (xsiSchemaType
!= null && (flag
& xsiSchemaType
.DerivedBy
) != 0) {
705 HandleError ("Prohibited element type substitution.");
710 if (xsiComplexType
!= null)
712 xsiComplexType
.ValidateTypeDerivationOK (baseType
, null, null);
713 } catch (ValException ex
) {
714 // HandleError ("Locally specified schema complex type derivation failed. " + ex.Message, ex);
718 SimpleType xsiSimpleType
= xsiType
as SimpleType
;
719 if (xsiSimpleType
!= null) {
721 xsiSimpleType
.ValidateTypeDerivationOK (baseType
, null, null, true);
722 } catch (ValException ex
) {
723 // HandleError ("Locally specified schema simple type derivation failed. " + ex.Message, ex);
727 else if (xsiType
is XsDatatype
) {
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
);
763 HandleError ("The instance type was not found: " + xsiTypeName
+ " .");
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
+ " .");
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
799 switch (state
.ProcessContents
) {
800 case ContentProc
.Skip
:
802 case ContentProc
.Lax
:
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.");
813 state
.PushContext ();
815 XsdValidationState next
= null;
816 if (state
.ProcessContents
== ContentProc
.Skip
)
817 skipValidationDepth
= reader
.Depth
;
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
);
826 next
= state
.Create (XmlSchemaParticle
.Empty
);
828 Context
.State
= next
;
830 #region Key Constraints
831 if (checkKeyConstraints
) {
832 ValidateKeySelectors ();
833 ValidateKeyFields ();
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
);
846 HandleError ("Element declaration is required for " + qname
);
848 if (element
.ActualIsAbstract
)
849 HandleError ("Abstract element declaration was specified for " + qname
);
851 if (!element
.ActualIsNillable
&& xsiNilValue
!= null)
852 HandleError ("This element declaration is not nillable: " + qname
);
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
);
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.");
888 ComplexType cType
= schemaType
as ComplexType
;
889 SimpleType sType
= schemaType
as SimpleType
;
892 while (reader
.MoveToNextAttribute ()) {
893 if (reader
.NamespaceURI
== XmlNamespaceManager
.XmlnsXmlns
)
895 if (reader
.NamespaceURI
!= XmlSchema
.InstanceNamespace
)
896 HandleError ("Current simple type cannot accept attributes other than schema instance namespace.");
897 switch (reader
.LocalName
) {
900 case "schemaLocation":
901 case "noNamespaceSchemaLocation":
904 HandleError ("Unknown schema instance namespace attribute: " + reader
.LocalName
);
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.");
916 AssessElementLocallyValidComplexType (cType
);
920 // 3.4.4 Element Locally Valid (Complex Type)
921 private void AssessElementLocallyValidComplexType (ComplexType cType
)
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
932 if (reader
.MoveToFirstAttribute ()) {
934 switch (reader
.NamespaceURI
) {
935 case"http://www.w3.org/2000/xmlns/":
936 case XmlSchema
.InstanceNamespace
:
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.
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
;
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
)
979 if (attr
.AttributeType
== null)
980 HandleError ("Attribute type is missing for " + attr
.QualifiedName
);
981 XsDatatype dt
= attr
.AttributeType
as XsDatatype
;
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
;
992 ValidateRestrictedSimpleTypeValue (st
, ref dt
, normalized
);
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
);
1009 HandleError (error
);
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 ();
1037 // Reset xsi:nil, if required.
1038 if (xsiNilDepth
== reader
.Depth
)
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
);
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
) {
1056 seq
.FinishedEntries
.Add (entry
);
1057 else if (seq
.SourceSchemaIdentity
is XmlSchemaKey
)
1058 HandleError ("Key sequence is missing.");
1059 seq
.Entries
.RemoveAt (k
);
1062 // Pop validated key depth to find two or more fields.
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
);
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
++) {
1121 ProcessKeyEntry (seq
.Entries
[j
]);
1122 } catch (ValException 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 ()) {
1136 switch (NamespaceURI
) {
1137 case XmlNamespaceManager
.XmlnsXmlns
:
1138 case XmlSchema
.InstanceNamespace
:
1141 XmlSchemaDatatype dt
= SchemaType
as XmlSchemaDatatype
;
1142 XmlSchemaSimpleType st
= SchemaType
as XmlSchemaSimpleType
;
1143 if (dt
== null && st
!= null)
1145 object identity
= null;
1147 identity
= dt
.ParseValue (Value
, NameTable
, NamespaceManager
);
1148 if (identity
== null)
1150 entry
.ProcessMatch (true, elementQNameStack
, this, NameTable
, BaseURI
, SchemaType
, NamespaceManager
, readerLineInfo
, Depth
, LocalName
, NamespaceURI
, identity
, false, CurrentKeyFieldConsumers
);
1151 } while (MoveToNextAttribute ());
1158 private XsdKeyTable
CreateNewKeyTable (XmlSchemaIdentityConstraint ident
)
1160 XsdKeyTable seq
= new XsdKeyTable (ident
);
1161 seq
.StartDepth
= reader
.Depth
;
1162 this.keyTables
.Add (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
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)
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
];
1200 if (seq
.SourceSchemaIdentity
is XmlSchemaKey
) {
1202 errors
= new ArrayList ();
1203 errors
.Add ("line " + entry
.SelectorLineNumber
+ "position " + entry
.SelectorLinePosition
);
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
)
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;
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
) {
1241 errors
= new ArrayList ();
1242 errors
.Add (" line " + entry
.SelectorLineNumber
+ ", position " + entry
.SelectorLinePosition
);
1246 HandleError ("Invalid identity constraints were found. Referenced key was not found: "
1247 + String
.Join (" / ", errors
.ToArray (typeof (string)) as string []));
1251 // Overrided Methods
1253 public override void 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
);
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
);
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
);
1316 return GetDefaultAttribute (localName
, ns
);
1319 private string GetDefaultAttribute (string localName
, string ns
)
1321 int idx
= this.FindDefaultAttribute (localName
, ns
);
1324 string value = defaultAttributes
[idx
].ValidatedDefaultValue
;
1326 value = defaultAttributes
[idx
].ValidatedFixedValue
;
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
))
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
);
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;
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
);
1386 this.currentDefaultAttribute
= -1;
1387 this.defaultAttributeConsumed
= false;
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
);
1405 this.currentDefaultAttribute
= -1;
1406 this.defaultAttributeConsumed
= false;
1410 return MoveToDefaultAttribute (localName
, ns
);
1413 private bool MoveToDefaultAttribute (string localName
, string ns
)
1415 int idx
= this.FindDefaultAttribute (localName
, ns
);
1418 currentDefaultAttribute
= idx
;
1419 defaultAttributeConsumed
= false;
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 ();
1443 currentDefaultAttribute
= -1;
1444 defaultAttributeConsumed
= false;
1449 if (this.defaultAttributes
.Length
> 0) {
1450 currentDefaultAttribute
= 0;
1451 defaultAttributeConsumed
= 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)
1470 currentDefaultAttribute
++;
1471 defaultAttributeConsumed
= false;
1475 bool b
= reader
.MoveToNextAttribute ();
1477 currentDefaultAttribute
= -1;
1478 defaultAttributeConsumed
= false;
1482 if (defaultAttributes
.Length
> 0) {
1483 currentDefaultAttribute
= 0;
1484 defaultAttributeConsumed
= 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;
1497 xtr
= new XmlTextReader (absUriString
,
1498 (Stream
) resolver
.GetEntity (
1499 absUri
, null, typeof (Stream
)),
1501 return XmlSchema
.Read (
1502 xtr
, ValidationEventHandler
);
1509 private void ExamineAdditionalSchema ()
1511 if (resolver
== null || ValidationType
== ValidationType
.None
)
1513 XmlSchema schema
= null;
1514 string schemaLocation
= reader
.GetAttribute ("schemaLocation", XmlSchema
.InstanceNamespace
);
1515 bool schemaAdded
= false;
1516 if (schemaLocation
!= null) {
1517 string [] tmp
= null;
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.");
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
)) {
1541 schemas
.Add (schema
);
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);
1553 } while (i
< tmp
.Length
);
1555 string noNsSchemaLocation
= reader
.GetAttribute ("noNamespaceSchemaLocation", XmlSchema
.InstanceNamespace
);
1556 if (noNsSchemaLocation
!= null) {
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
)) {
1569 schemas
.Add (schema
);
1572 // FIXME: should call Reprocess()?
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
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)
1603 if (!schemas
.IsCompiled
)
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 ());
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
));
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;
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);
1644 case XmlNodeType
.CDATA
:
1645 case XmlNodeType
.SignificantWhitespace
:
1646 case XmlNodeType
.Whitespace
:
1647 case XmlNodeType
.Text
:
1648 if (skipValidationDepth
>= 0 && reader
.Depth
> skipValidationDepth
)
1651 ComplexType ct
= Context
.ActualType
as ComplexType
;
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
));
1658 case XmlSchemaContentType
.Empty
:
1659 HandleError (String
.Format ("Not allowed character content is found (current element content model '{0}' is empty).", ct
.QualifiedName
));
1664 ValidateCharacters ();
1671 public override bool ReadAttributeValue ()
1673 if (currentDefaultAttribute
< 0)
1674 return reader
.ReadAttributeValue ();
1676 if (this.defaultAttributeConsumed
)
1679 defaultAttributeConsumed
= true;
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 ();
1697 // XmlReader.ReadString() should call derived this.Read().
1698 public override string ReadString ()
1701 return reader
.ReadString ();
1703 return base.ReadString ();
1707 // This class itself does not have this feature.
1708 public override void ResolveEntity ()
1710 reader
.ResolveEntity ();
1714 internal class XsdValidationContext
1716 public XsdValidationContext ()
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
{
1743 // FIXME: actually this should also be stacked
1744 if (element_stack
.Count
== 0)
1746 if (XsiType
!= null)
1749 return Element
!= null ? Element
.ElementType
: null;
1754 public XmlSchemaType ActualSchemaType
{
1756 object at
= ActualType
;
1759 XmlSchemaType st
= at
as XmlSchemaType
;
1761 st
= XmlSchemaType
.GetBuiltInSimpleType (
1762 ((XmlSchemaDatatype
) at
).TypeCode
);
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
{
1801 if (missingIDReferences
== null)
1802 missingIDReferences
= new ArrayList ();
1803 return missingIDReferences
;
1807 public void OnStartElement ()
1809 thisElementId
= null;
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.";
1823 thisElementId
= str
;
1824 if (idList
.ContainsKey (str
))
1825 return "Duplicate ID value was found.";
1827 idList
.Add (str
, elementName
);
1828 if (MissingIDReferences
.Contains (str
))
1829 MissingIDReferences
.Remove (str
);
1831 case XmlTokenizedType
.IDREF
:
1832 if (!idList
.Contains (str
) && !MissingIDReferences
.Contains (str
))
1833 MissingIDReferences
.Add (str
);
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
);
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))