Jo Shields a575963da9 Imported Upstream version 3.6.0
Former-commit-id: da6be194a6b1221998fc28233f2503bd61dd9d14
2014-08-13 10:39:27 +01:00

1843 lines
54 KiB
C#

//
// Mono.Xml.Schema.XsdValidatingReader.cs
//
// Author:
// Atsushi Enomoto (ginga@kit.hi-ho.ne.jp)
//
// (C)2003 Atsushi Enomoto
//
//
// Permission is hereby granted, free of charge, to any person obtaining
// a copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to
// permit persons to whom the Software is furnished to do so, subject to
// the following conditions:
//
// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
//
using System;
using System.Collections;
#if NET_2_0
using System.Collections.Generic;
#endif
using System.Collections.Specialized;
using System.IO;
using System.Text;
using System.Xml;
using System.Xml.Schema;
using Mono.Xml;
#if NET_2_0
using ValException = System.Xml.Schema.XmlSchemaValidationException;
#else
using ValException = System.Xml.Schema.XmlSchemaException;
#endif
using QName = System.Xml.XmlQualifiedName;
using ContentProc = System.Xml.Schema.XmlSchemaContentProcessing;
using XsElement = System.Xml.Schema.XmlSchemaElement;
using XsAttribute = System.Xml.Schema.XmlSchemaAttribute;
using ComplexType = System.Xml.Schema.XmlSchemaComplexType;
using SimpleType = System.Xml.Schema.XmlSchemaSimpleType;
using SimpleTypeRest = System.Xml.Schema.XmlSchemaSimpleTypeRestriction;
using SimpleTypeList = System.Xml.Schema.XmlSchemaSimpleTypeList;
using SimpleTypeUnion = System.Xml.Schema.XmlSchemaSimpleTypeUnion;
using XsDatatype = System.Xml.Schema.XmlSchemaDatatype;
namespace Mono.Xml.Schema
{
internal class XsdValidatingReader : XmlReader, IXmlLineInfo, IHasXmlSchemaInfo, IHasXmlParserContext
{
static readonly XsAttribute [] emptyAttributeArray =
new XsAttribute [0];
XmlReader reader;
XmlResolver resolver;
IHasXmlSchemaInfo sourceReaderSchemaInfo;
IXmlLineInfo readerLineInfo;
ValidationType validationType;
XmlSchemaSet schemas = new XmlSchemaSet ();
bool namespaces = true;
bool validationStarted;
#region ID Constraints
bool checkIdentity = true;
XsdIDManager idManager = new XsdIDManager ();
#endregion
#region Key Constraints
bool checkKeyConstraints = true;
ArrayList keyTables = new ArrayList ();
ArrayList currentKeyFieldConsumers;
ArrayList tmpKeyrefPool;
#endregion
ArrayList elementQNameStack = new ArrayList ();
XsdParticleStateManager state = new XsdParticleStateManager ();
int skipValidationDepth = -1;
int xsiNilDepth = -1;
StringBuilder storedCharacters = new StringBuilder ();
bool shouldValidateCharacters;
XsAttribute [] defaultAttributes = emptyAttributeArray;
int currentDefaultAttribute = -1;
ArrayList defaultAttributesCache = new ArrayList ();
bool defaultAttributeConsumed;
object currentAttrType;
#region .ctor
public XsdValidatingReader (XmlReader reader)
{
this.reader = reader;
readerLineInfo = reader as IXmlLineInfo;
sourceReaderSchemaInfo = reader as IHasXmlSchemaInfo;
schemas.ValidationEventHandler += ValidationEventHandler;
}
#endregion
public ValidationEventHandler ValidationEventHandler;
// Private Properties
private XsdValidationContext Context {
get { return state.Context; }
}
#region Key Constraints
internal ArrayList CurrentKeyFieldConsumers {
get {
if (currentKeyFieldConsumers == null)
currentKeyFieldConsumers = new ArrayList ();
return currentKeyFieldConsumers;
}
}
#endregion
// Public Non-overrides
public int XsiNilDepth {
get { return xsiNilDepth; }
}
public bool Namespaces {
get { return namespaces; }
set { namespaces = value; }
}
// This is required to resolve xsi:schemaLocation
public XmlResolver XmlResolver {
set {
resolver = value;
}
}
// This should be changed before the first Read() call.
public XmlSchemaSet Schemas {
get { return schemas; }
set {
if (validationStarted)
throw new InvalidOperationException ("Schemas must be set before the first call to Read().");
schemas = value;
}
}
public object SchemaType {
get {
if (ReadState != ReadState.Interactive)
return null;
switch (NodeType) {
case XmlNodeType.Element:
if (Context.ActualType != null)
return Context.ActualType;
else
return SourceReaderSchemaType;
case XmlNodeType.Attribute:
if (currentAttrType == null) {
ComplexType ct = Context.ActualType as ComplexType;
if (ct != null) {
XsAttribute attdef = ct.AttributeUses [new QName (LocalName, NamespaceURI)] as XsAttribute;
if (attdef != null)
currentAttrType = attdef.AttributeType;
return currentAttrType;
}
currentAttrType = SourceReaderSchemaType;
}
return currentAttrType;
default:
return SourceReaderSchemaType;
}
}
}
private object SourceReaderSchemaType {
get { return this.sourceReaderSchemaInfo != null ? sourceReaderSchemaInfo.SchemaType : null; }
}
public ValidationType ValidationType {
get { return validationType; }
set {
if (validationStarted)
throw new InvalidOperationException ("ValidationType must be set before reading.");
validationType = value;
}
}
// It is used only for independent XmlReader use, not for XmlValidatingReader.
public object ReadTypedValue ()
{
object o = XmlSchemaUtil.ReadTypedValue (this,
SchemaType, NamespaceManager,
storedCharacters);
storedCharacters.Length = 0;
return o;
}
// Public Overriden Properties
public override int AttributeCount {
get {
return reader.AttributeCount + defaultAttributes.Length;
}
}
public override string BaseURI {
get { return reader.BaseURI; }
}
// If this class is used to implement XmlValidatingReader,
// it should be left to DTDValidatingReader. In other cases,
// it depends on the reader's ability.
public override bool CanResolveEntity {
get { return reader.CanResolveEntity; }
}
public override int Depth {
get {
if (currentDefaultAttribute < 0)
return reader.Depth;
if (this.defaultAttributeConsumed)
return reader.Depth + 2;
return reader.Depth + 1;
}
}
public override bool EOF {
get { return reader.EOF; }
}
public override bool HasValue {
get {
if (currentDefaultAttribute < 0)
return reader.HasValue;
return true;
}
}
public override bool IsDefault {
get {
if (currentDefaultAttribute < 0)
return reader.IsDefault;
return true;
}
}
public override bool IsEmptyElement {
get {
if (currentDefaultAttribute < 0)
return reader.IsEmptyElement;
return false;
}
}
public override string this [int i] {
get { return GetAttribute (i); }
}
public override string this [string name] {
get { return GetAttribute (name); }
}
public override string this [string localName, string ns] {
get { return GetAttribute (localName, ns); }
}
public int LineNumber {
get { return readerLineInfo != null ? readerLineInfo.LineNumber : 0; }
}
public int LinePosition {
get { return readerLineInfo != null ? readerLineInfo.LinePosition : 0; }
}
public override string LocalName {
get {
if (currentDefaultAttribute < 0)
return reader.LocalName;
if (defaultAttributeConsumed)
return String.Empty;
return defaultAttributes [currentDefaultAttribute].QualifiedName.Name;
}
}
public override string Name {
get {
if (currentDefaultAttribute < 0)
return reader.Name;
if (defaultAttributeConsumed)
return String.Empty;
QName qname = defaultAttributes [currentDefaultAttribute].QualifiedName;
string prefix = Prefix;
if (prefix == String.Empty)
return qname.Name;
else
return String.Concat (prefix, ":", qname.Name);
}
}
public override string NamespaceURI {
get {
if (currentDefaultAttribute < 0)
return reader.NamespaceURI;
if (defaultAttributeConsumed)
return String.Empty;
return defaultAttributes [currentDefaultAttribute].QualifiedName.Namespace;
}
}
public override XmlNameTable NameTable {
get { return reader.NameTable; }
}
public override XmlNodeType NodeType {
get {
if (currentDefaultAttribute < 0)
return reader.NodeType;
if (defaultAttributeConsumed)
return XmlNodeType.Text;
return XmlNodeType.Attribute;
}
}
public XmlParserContext ParserContext {
get { return XmlSchemaUtil.GetParserContext (reader); }
}
internal XmlNamespaceManager NamespaceManager {
get { return ParserContext != null ? ParserContext.NamespaceManager : null; }
}
public override string Prefix {
get {
if (currentDefaultAttribute < 0)
return reader.Prefix;
if (defaultAttributeConsumed)
return String.Empty;
QName qname = defaultAttributes [currentDefaultAttribute].QualifiedName;
string prefix = NamespaceManager != null ? NamespaceManager.LookupPrefix (qname.Namespace, false) : null;
if (prefix == null)
return String.Empty;
else
return prefix;
}
}
public override char QuoteChar {
get { return reader.QuoteChar; }
}
public override ReadState ReadState {
get { return reader.ReadState; }
}
public override string Value {
get {
if (currentDefaultAttribute < 0)
return reader.Value;
string value = defaultAttributes [currentDefaultAttribute].ValidatedDefaultValue;
if (value == null)
value = defaultAttributes [currentDefaultAttribute].ValidatedFixedValue;
return value;
}
}
public override string XmlLang {
get {
string xmlLang = reader.XmlLang;
if (xmlLang != null)
return xmlLang;
int idx = this.FindDefaultAttribute ("lang", XmlNamespaceManager.XmlnsXml);
if (idx < 0)
return null;
xmlLang = defaultAttributes [idx].ValidatedDefaultValue;
if (xmlLang == null)
xmlLang = defaultAttributes [idx].ValidatedFixedValue;
return xmlLang;
}
}
public override XmlSpace XmlSpace {
get {
XmlSpace space = reader.XmlSpace;
if (space != XmlSpace.None)
return space;
int idx = this.FindDefaultAttribute ("space", XmlNamespaceManager.XmlnsXml);
if (idx < 0)
return XmlSpace.None;
string spaceSpec = defaultAttributes [idx].ValidatedDefaultValue;
if (spaceSpec == null)
spaceSpec = defaultAttributes [idx].ValidatedFixedValue;
return (XmlSpace) Enum.Parse (typeof (XmlSpace), spaceSpec, false);
}
}
// Private Methods
private void HandleError (string error)
{
HandleError (error, null);
}
private void HandleError (string error, Exception innerException)
{
HandleError (error, innerException, false);
}
private void HandleError (string error, Exception innerException, bool isWarning)
{
if (ValidationType == ValidationType.None) // extra quick check
return;
ValException schemaException = new ValException (error,
this, this.BaseURI, null, innerException);
HandleError (schemaException, isWarning);
}
private void HandleError (ValException schemaException)
{
HandleError (schemaException, false);
}
private void HandleError (ValException schemaException, bool isWarning)
{
if (ValidationType == ValidationType.None)
return;
ValidationEventArgs e = new ValidationEventArgs (schemaException,
schemaException.Message, isWarning ? XmlSeverityType.Warning : XmlSeverityType.Error);
if (ValidationEventHandler != null)
ValidationEventHandler (this, e);
else if (e.Severity == XmlSeverityType.Error)
throw e.Exception;
}
private XsElement FindElement (string name, string ns)
{
return (XsElement) schemas.GlobalElements [new QName (name, ns)];
}
private XmlSchemaType FindType (QName qname)
{
return (XmlSchemaType) schemas.GlobalTypes [qname];
}
private void ValidateStartElementParticle ()
{
if (Context.State == null)
return;
Context.XsiType = null;
state.CurrentElement = null;
Context.EvaluateStartElement (reader.LocalName,
reader.NamespaceURI);
if (Context.IsInvalid)
HandleError ("Invalid start element: " + reader.NamespaceURI + ":" + reader.LocalName);
Context.PushCurrentElement (state.CurrentElement);
}
private void ValidateEndElementParticle ()
{
if (xsiNilDepth < 0 && Context.State != null) {
if (!Context.EvaluateEndElement ()) {
HandleError ("Invalid end element: " + reader.Name);
}
}
Context.PopCurrentElement ();
state.PopContext ();
}
// Utility for missing validation completion related to child items.
private void ValidateCharacters ()
{
if (xsiNilDepth >= 0 && xsiNilDepth < reader.Depth)
HandleError ("Element item appeared, while current element context is nil.");
if (shouldValidateCharacters)
storedCharacters.Append (reader.Value);
}
// Utility for missing validation completion related to child items.
private void ValidateEndSimpleContent ()
{
if (shouldValidateCharacters)
ValidateEndSimpleContentCore ();
shouldValidateCharacters = false;
storedCharacters.Length = 0;
}
private void ValidateEndSimpleContentCore ()
{
if (Context.ActualType == null)
return;
string value = storedCharacters.ToString ();
if (value.Length == 0) {
// 3.3.4 Element Locally Valid (Element) 5.1.2
if (Context.Element != null) {
if (Context.Element.ValidatedDefaultValue != null)
value = Context.Element.ValidatedDefaultValue;
}
}
XsDatatype dt = Context.ActualType as XsDatatype;
SimpleType st = Context.ActualType as SimpleType;
if (dt == null) {
if (st != null) {
dt = st.Datatype;
} else {
ComplexType ct = Context.ActualType as ComplexType;
var ctsm = ct.ContentModel as XmlSchemaSimpleContent;
if (ctsm != null) {
var scr = ctsm.Content as XmlSchemaSimpleContentRestriction;
if (scr != null)
st = FindSimpleBaseType (scr.BaseType ?? FindType (scr.BaseTypeName));
var sce = ctsm.Content as XmlSchemaSimpleContentExtension;
if (sce != null)
st = FindSimpleBaseType (FindType (sce.BaseTypeName));
}
dt = ct.Datatype;
switch (ct.ContentType) {
case XmlSchemaContentType.ElementOnly:
if (value.Length > 0 && !XmlChar.IsWhitespace (value))
HandleError ("Character content not allowed.");
break;
case XmlSchemaContentType.Empty:
if (value.Length > 0)
HandleError ("Character content not allowed.");
break;
}
}
}
if (dt != null) {
// 3.3.4 Element Locally Valid (Element) :: 5.2.2.2. Fixed value constraints
if (Context.Element != null && Context.Element.ValidatedFixedValue != null)
if (value != Context.Element.ValidatedFixedValue)
HandleError ("Fixed value constraint was not satisfied.");
AssessStringValid (st, dt, value);
}
#region Key Constraints
if (checkKeyConstraints)
ValidateSimpleContentIdentity (dt, value);
#endregion
shouldValidateCharacters = false;
}
SimpleType FindSimpleBaseType (XmlSchemaType xt)
{
var st = xt as SimpleType;
if (st != null)
return st;
if (xt == null)
return null;
return FindSimpleBaseType (xt.BaseXmlSchemaType);
}
// 3.14.4 String Valid
private void AssessStringValid (SimpleType st,
XsDatatype dt, string value)
{
XsDatatype validatedDatatype = dt;
if (st != null) {
string normalized = validatedDatatype.Normalize (value);
ValidateRestrictedSimpleTypeValue (st, ref validatedDatatype, normalized);
}
if (validatedDatatype != null) {
try {
validatedDatatype.ParseValue (value, NameTable, NamespaceManager);
} catch (Exception ex) { // FIXME: (wishlist) It is bad manner ;-(
HandleError ("Invalidly typed data was specified.", ex);
}
}
}
void ValidateRestrictedSimpleTypeValue (SimpleType st, ref XsDatatype dt, string normalized)
{
{
string [] values;
XsDatatype itemDatatype;
SimpleType itemSimpleType;
switch (st.DerivedBy) {
case XmlSchemaDerivationMethod.List:
SimpleTypeList listContent = st.Content as SimpleTypeList;
values = normalized.Split (XmlChar.WhitespaceChars);
itemDatatype = listContent.ValidatedListItemType as XsDatatype;
itemSimpleType = listContent.ValidatedListItemType as SimpleType;
for (int vi = 0; vi < values.Length; vi++) {
string each = values [vi];
if (each == String.Empty)
continue;
// validate against ValidatedItemType
if (itemDatatype != null) {
try {
itemDatatype.ParseValue (each, NameTable, NamespaceManager);
} catch (Exception ex) { // FIXME: (wishlist) better exception handling ;-(
HandleError ("List type value contains one or more invalid values.", ex);
break;
}
}
else
AssessStringValid (itemSimpleType, itemSimpleType.Datatype, each);
}
break;
case XmlSchemaDerivationMethod.Union:
SimpleTypeUnion union = st.Content as SimpleTypeUnion;
{
string each = normalized;
// validate against ValidatedItemType
bool passed = false;
foreach (object eachType in union.ValidatedTypes) {
itemDatatype = eachType as XsDatatype;
itemSimpleType = eachType as SimpleType;
if (itemDatatype != null) {
try {
itemDatatype.ParseValue (each, NameTable, NamespaceManager);
} catch (Exception) { // FIXME: (wishlist) better exception handling ;-(
continue;
}
}
else {
try {
AssessStringValid (itemSimpleType, itemSimpleType.Datatype, each);
} catch (ValException) {
continue;
}
}
passed = true;
break;
}
if (!passed) {
HandleError ("Union type value contains one or more invalid values.");
break;
}
}
break;
case XmlSchemaDerivationMethod.Restriction:
SimpleTypeRest str = st.Content as SimpleTypeRest;
// facet validation
if (str != null) {
/* Don't forget to validate against inherited type's facets
* Could we simplify this by assuming that the basetype will also
* be restriction?
* */
// mmm, will check later.
SimpleType baseType = st.BaseXmlSchemaType as SimpleType;
if (baseType != null) {
AssessStringValid(baseType, dt, normalized);
}
if (!str.ValidateValueWithFacets (normalized, NameTable, NamespaceManager)) {
HandleError ("Specified value was invalid against the facets.");
break;
}
}
dt = st.Datatype;
break;
}
}
}
private object GetXsiType (string name)
{
object xsiType = null;
QName typeQName = QName.Parse (name, this);
if (typeQName == ComplexType.AnyTypeName)
xsiType = ComplexType.AnyType;
else if (XmlSchemaUtil.IsBuiltInDatatypeName (typeQName))
xsiType = XsDatatype.FromName (typeQName);
else
xsiType = FindType (typeQName);
return xsiType;
}
// It is common to ElementLocallyValid::4 and SchemaValidityAssessment::1.2.1.2.4
private void AssessLocalTypeDerivationOK (object xsiType, object baseType, XmlSchemaDerivationMethod flag)
{
XmlSchemaType xsiSchemaType = xsiType as XmlSchemaType;
ComplexType baseComplexType = baseType as ComplexType;
ComplexType xsiComplexType = xsiSchemaType as ComplexType;
if (xsiType != baseType) {
// Extracted (not extraneous) check for 3.4.6 TypeDerivationOK.
if (baseComplexType != null)
flag |= baseComplexType.BlockResolved;
if (flag == XmlSchemaDerivationMethod.All) {
HandleError ("Prohibited element type substitution.");
return;
} else if (xsiSchemaType != null && (flag & xsiSchemaType.DerivedBy) != 0) {
HandleError ("Prohibited element type substitution.");
return;
}
}
if (xsiComplexType != null)
try {
xsiComplexType.ValidateTypeDerivationOK (baseType, null, null);
} catch (ValException ex) {
// HandleError ("Locally specified schema complex type derivation failed. " + ex.Message, ex);
HandleError (ex);
}
else {
SimpleType xsiSimpleType = xsiType as SimpleType;
if (xsiSimpleType != null) {
try {
xsiSimpleType.ValidateTypeDerivationOK (baseType, null, null, true);
} catch (ValException ex) {
// HandleError ("Locally specified schema simple type derivation failed. " + ex.Message, ex);
HandleError (ex);
}
}
else if (xsiType is XsDatatype) {
// do nothing
}
else
HandleError ("Primitive data type cannot be derived type using xsi:type specification.");
}
}
// Section 3.3.4 of the spec.
private void AssessStartElementSchemaValidity ()
{
// If the reader is inside xsi:nil (and failed
// on validation), then simply skip its content.
if (xsiNilDepth >= 0 && xsiNilDepth < reader.Depth)
HandleError ("Element item appeared, while current element context is nil.");
ValidateStartElementParticle ();
string xsiNilValue = reader.GetAttribute ("nil", XmlSchema.InstanceNamespace);
if (xsiNilValue != null)
xsiNilValue = xsiNilValue.Trim (XmlChar.WhitespaceChars);
bool isXsiNil = xsiNilValue == "true";
if (isXsiNil && this.xsiNilDepth < 0)
xsiNilDepth = reader.Depth;
// [Schema Validity Assessment (Element) 1.2]
// Evaluate "local type definition" from xsi:type.
// (See spec 3.3.4 Schema Validity Assessment (Element) 1.2.1.2.3.
// Note that Schema Validity Assessment(Element) 1.2 takes
// precedence than 1.1 of that.
string xsiTypeName = reader.GetAttribute ("type", XmlSchema.InstanceNamespace);
if (xsiTypeName != null) {
xsiTypeName = xsiTypeName.Trim (XmlChar.WhitespaceChars);
object xsiType = GetXsiType (xsiTypeName);
if (xsiType == null)
HandleError ("The instance type was not found: " + xsiTypeName + " .");
else {
XmlSchemaType xsiSchemaType = xsiType as XmlSchemaType;
if (xsiSchemaType != null && this.Context.Element != null) {
XmlSchemaType elemBaseType = Context.Element.ElementType as XmlSchemaType;
if (elemBaseType != null && (xsiSchemaType.DerivedBy & elemBaseType.FinalResolved) != 0)
HandleError ("The instance type is prohibited by the type of the context element.");
if (elemBaseType != xsiType && (xsiSchemaType.DerivedBy & this.Context.Element.BlockResolved) != 0)
HandleError ("The instance type is prohibited by the context element.");
}
ComplexType xsiComplexType = xsiType as ComplexType;
if (xsiComplexType != null && xsiComplexType.IsAbstract)
HandleError ("The instance type is abstract: " + xsiTypeName + " .");
else {
// If current schema type exists, then this xsi:type must be
// valid extension of that type. See 1.2.1.2.4.
if (Context.Element != null) {
AssessLocalTypeDerivationOK (xsiType, Context.Element.ElementType, Context.Element.BlockResolved);
}
AssessStartElementLocallyValidType (xsiType); // 1.2.2:
Context.XsiType = xsiType;
}
}
}
// Create Validation Root, if not exist.
// [Schema Validity Assessment (Element) 1.1]
if (Context.Element == null) {
state.CurrentElement = FindElement (reader.LocalName, reader.NamespaceURI);
Context.PushCurrentElement (state.CurrentElement);
}
if (Context.Element != null) {
if (Context.XsiType == null) {
AssessElementLocallyValidElement (xsiNilValue); // 1.1.2
}
} else {
switch (state.ProcessContents) {
case ContentProc.Skip:
break;
case ContentProc.Lax:
break;
default:
if (xsiTypeName == null &&
(schemas.Contains (reader.NamespaceURI) ||
!schemas.MissedSubComponents (reader.NamespaceURI)))
HandleError ("Element declaration for " + new QName (reader.LocalName, reader.NamespaceURI) + " is missing.");
break;
}
}
state.PushContext ();
XsdValidationState next = null;
if (state.ProcessContents == ContentProc.Skip)
skipValidationDepth = reader.Depth;
else {
// create child particle state.
ComplexType xsComplexType = SchemaType as ComplexType;
if (xsComplexType != null)
next = state.Create (xsComplexType.ValidatableParticle);
else if (state.ProcessContents == ContentProc.Lax)
next = state.Create (XmlSchemaAny.AnyTypeContent);
else
next = state.Create (XmlSchemaParticle.Empty);
}
Context.State = next;
#region Key Constraints
if (checkKeyConstraints) {
ValidateKeySelectors ();
ValidateKeyFields ();
}
#endregion
}
// 3.3.4 Element Locally Valid (Element)
private void AssessElementLocallyValidElement (string xsiNilValue)
{
XsElement element = Context.Element;
QName qname = new QName (reader.LocalName, reader.NamespaceURI);
// 1.
if (element == null)
HandleError ("Element declaration is required for " + qname);
// 2.
if (element.ActualIsAbstract)
HandleError ("Abstract element declaration was specified for " + qname);
// 3.1.
if (!element.ActualIsNillable && xsiNilValue != null)
HandleError ("This element declaration is not nillable: " + qname);
// 3.2.
// Note that 3.2.1 xsi:nil constraints are to be
// validated in AssessElementSchemaValidity() and
// ValidateCharacters().
else if (xsiNilValue == "true") {
if (element.ValidatedFixedValue != null)
HandleError ("Schema instance nil was specified, where the element declaration for " + qname + "has fixed value constraints.");
}
// 4. xsi:type (it takes precedence than element type)
string xsiType = reader.GetAttribute ("type", XmlSchema.InstanceNamespace);
if (xsiType != null) {
Context.XsiType = GetXsiType (xsiType);
AssessLocalTypeDerivationOK (Context.XsiType, element.ElementType, element.BlockResolved);
}
else
Context.XsiType = null;
// 5 Not all things cannot be assessed here.
// It is common to 5.1 and 5.2
if (element.ElementType != null)
AssessStartElementLocallyValidType (SchemaType);
// 6. should be out from here.
// See invokation of AssessStartIdentityConstraints().
// 7 is going to be validated in Read() (in case of xmlreader's EOF).
}
// 3.3.4 Element Locally Valid (Type)
private void AssessStartElementLocallyValidType (object schemaType)
{
if (schemaType == null) { // 1.
HandleError ("Schema type does not exist.");
return;
}
ComplexType cType = schemaType as ComplexType;
SimpleType sType = schemaType as SimpleType;
if (sType != null) {
// 3.1.1.
while (reader.MoveToNextAttribute ()) {
if (reader.NamespaceURI == XmlNamespaceManager.XmlnsXmlns)
continue;
if (reader.NamespaceURI != XmlSchema.InstanceNamespace)
HandleError ("Current simple type cannot accept attributes other than schema instance namespace.");
switch (reader.LocalName) {
case "type":
case "nil":
case "schemaLocation":
case "noNamespaceSchemaLocation":
break;
default:
HandleError ("Unknown schema instance namespace attribute: " + reader.LocalName);
break;
}
}
reader.MoveToElement ();
// 3.1.2 and 3.1.3 cannot be assessed here.
} else if (cType != null) {
if (cType.IsAbstract) { // 2.
HandleError ("Target complex type is abstract.");
return;
}
// 3.2
AssessElementLocallyValidComplexType (cType);
}
}
// 3.4.4 Element Locally Valid (Complex Type)
private void AssessElementLocallyValidComplexType (ComplexType cType)
{
// 1.
if (cType.IsAbstract)
HandleError ("Target complex type is abstract.");
// 2 (xsi:nil and content prohibition)
// See AssessStartElementSchemaValidity() and ValidateCharacters()
// 3. attribute uses and
// 5. wild IDs
if (reader.MoveToFirstAttribute ()) {
do {
switch (reader.NamespaceURI) {
case"http://www.w3.org/2000/xmlns/":
case XmlSchema.InstanceNamespace:
continue;
}
QName qname = new QName (reader.LocalName, reader.NamespaceURI);
// including 3.10.4 Item Valid (Wildcard)
XmlSchemaObject attMatch = XmlSchemaUtil.FindAttributeDeclaration (reader.NamespaceURI, schemas, cType, qname);
if (attMatch == null)
HandleError ("Attribute declaration was not found for " + qname);
XsAttribute attdecl = attMatch as XsAttribute;
if (attdecl != null) {
AssessAttributeLocallyValidUse (attdecl);
AssessAttributeLocallyValid (attdecl);
} // otherwise anyAttribute or null.
} while (reader.MoveToNextAttribute ());
reader.MoveToElement ();
}
// Collect default attributes.
// 4.
foreach (DictionaryEntry entry in cType.AttributeUses) {
XsAttribute attr = (XsAttribute) entry.Value;
if (reader [attr.QualifiedName.Name, attr.QualifiedName.Namespace] == null) {
if (attr.ValidatedUse == XmlSchemaUse.Required &&
attr.ValidatedFixedValue == null)
HandleError ("Required attribute " + attr.QualifiedName + " was not found.");
else if (attr.ValidatedDefaultValue != null || attr.ValidatedFixedValue != null)
defaultAttributesCache.Add (attr);
}
}
if (defaultAttributesCache.Count == 0)
defaultAttributes = emptyAttributeArray;
else
defaultAttributes = (XsAttribute [])
defaultAttributesCache.ToArray (
typeof (XsAttribute));
defaultAttributesCache.Clear ();
// 5. wild IDs was already checked above.
}
// 3.2.4 Attribute Locally Valid and 3.4.4
private void AssessAttributeLocallyValid (XsAttribute attr)
{
// 2. - 4.
if (attr.AttributeType == null)
HandleError ("Attribute type is missing for " + attr.QualifiedName);
XsDatatype dt = attr.AttributeType as XsDatatype;
if (dt == null)
dt = ((SimpleType) attr.AttributeType).Datatype;
// It is a bit heavy process, so let's omit as long as possible ;-)
if (dt != SimpleType.AnySimpleType || attr.ValidatedFixedValue != null) {
string normalized = dt.Normalize (reader.Value);
object parsedValue = null;
// check part of 3.14.4 StringValid
SimpleType st = attr.AttributeType as SimpleType;
if (st != null)
ValidateRestrictedSimpleTypeValue (st, ref dt, normalized);
try {
parsedValue = dt.ParseValue (normalized, reader.NameTable, NamespaceManager);
} catch (Exception ex) { // FIXME: (wishlist) It is bad manner ;-(
HandleError ("Attribute value is invalid against its data type " + dt.TokenizedType, ex);
}
if (attr.ValidatedFixedValue != null &&
attr.ValidatedFixedValue != normalized) {
HandleError ("The value of the attribute " + attr.QualifiedName + " does not match with its fixed value.");
parsedValue = dt.ParseValue (attr.ValidatedFixedValue, reader.NameTable, NamespaceManager);
}
#region ID Constraints
if (this.checkIdentity) {
string error = idManager.AssessEachAttributeIdentityConstraint (dt, parsedValue, ((QName) elementQNameStack [elementQNameStack.Count - 1]).Name);
if (error != null)
HandleError (error);
}
#endregion
}
}
private void AssessAttributeLocallyValidUse (XsAttribute attr)
{
// This is extra check than spec 3.5.4
if (attr.ValidatedUse == XmlSchemaUse.Prohibited)
HandleError ("Attribute " + attr.QualifiedName + " is prohibited in this context.");
}
private void AssessEndElementSchemaValidity ()
{
ValidateEndSimpleContent ();
ValidateEndElementParticle (); // validate against childrens' state.
// 3.3.4 Assess ElementLocallyValidElement 5: value constraints.
// 3.3.4 Assess ElementLocallyValidType 3.1.3. = StringValid(3.14.4)
// => ValidateEndSimpleContent().
#region Key Constraints
if (checkKeyConstraints)
ValidateEndElementKeyConstraints ();
#endregion
// Reset xsi:nil, if required.
if (xsiNilDepth == reader.Depth)
xsiNilDepth = -1;
}
#region Key Constraints
private void ValidateEndElementKeyConstraints ()
{
// Reset Identity constraints.
for (int i = 0; i < keyTables.Count; i++) {
XsdKeyTable seq = this.keyTables [i] as XsdKeyTable;
if (seq.StartDepth == reader.Depth) {
EndIdentityValidation (seq);
} else {
for (int k = 0; k < seq.Entries.Count; k++) {
XsdKeyEntry entry = seq.Entries [k] as XsdKeyEntry;
// Remove finished (maybe key not found) entries.
if (entry.StartDepth == reader.Depth) {
if (entry.KeyFound)
seq.FinishedEntries.Add (entry);
else if (seq.SourceSchemaIdentity is XmlSchemaKey)
HandleError ("Key sequence is missing.");
seq.Entries.RemoveAt (k);
k--;
}
// Pop validated key depth to find two or more fields.
else {
for (int j = 0; j < entry.KeyFields.Count; j++) {
XsdKeyEntryField kf = entry.KeyFields [j];
if (!kf.FieldFound && kf.FieldFoundDepth == reader.Depth) {
kf.FieldFoundDepth = 0;
kf.FieldFoundPath = null;
}
}
}
}
}
}
for (int i = 0; i < keyTables.Count; i++) {
XsdKeyTable seq = this.keyTables [i] as XsdKeyTable;
if (seq.StartDepth == reader.Depth) {
keyTables.RemoveAt (i);
i--;
}
}
}
// 3.11.4 Identity Constraint Satisfied
private void ValidateKeySelectors ()
{
if (tmpKeyrefPool != null)
tmpKeyrefPool.Clear ();
if (Context.Element != null && Context.Element.Constraints.Count > 0) {
// (a) Create new key sequences, if required.
for (int i = 0; i < Context.Element.Constraints.Count; i++) {
XmlSchemaIdentityConstraint ident = (XmlSchemaIdentityConstraint) Context.Element.Constraints [i];
XsdKeyTable seq = CreateNewKeyTable (ident);
if (ident is XmlSchemaKeyref) {
if (tmpKeyrefPool == null)
tmpKeyrefPool = new ArrayList ();
tmpKeyrefPool.Add (seq);
}
}
}
// (b) Evaluate current key sequences.
for (int i = 0; i < keyTables.Count; i++) {
XsdKeyTable seq = (XsdKeyTable) keyTables [i];
if (seq.SelectorMatches (this.elementQNameStack, reader.Depth) != null) {
// creates and registers new entry.
XsdKeyEntry entry = new XsdKeyEntry (seq, reader.Depth, readerLineInfo);
seq.Entries.Add (entry);
}
}
}
private void ValidateKeyFields ()
{
// (c) Evaluate field paths.
for (int i = 0; i < keyTables.Count; i++) {
XsdKeyTable seq = (XsdKeyTable) keyTables [i];
// If possible, create new field entry candidates.
for (int j = 0; j < seq.Entries.Count; j++) {
try {
ProcessKeyEntry (seq.Entries [j]);
} catch (ValException ex) {
HandleError (ex);
}
}
}
}
private void ProcessKeyEntry (XsdKeyEntry entry)
{
bool isNil = XsiNilDepth == Depth;
entry.ProcessMatch (false, elementQNameStack, this, NameTable, BaseURI, SchemaType, NamespaceManager, readerLineInfo, Depth, null, null, null, isNil, CurrentKeyFieldConsumers);
if (MoveToFirstAttribute ()) {
try {
do {
switch (NamespaceURI) {
case XmlNamespaceManager.XmlnsXmlns:
case XmlSchema.InstanceNamespace:
continue;
}
XmlSchemaDatatype dt = SchemaType as XmlSchemaDatatype;
XmlSchemaSimpleType st = SchemaType as XmlSchemaSimpleType;
if (dt == null && st != null)
dt = st.Datatype;
object identity = null;
if (dt != null)
identity = dt.ParseValue (Value, NameTable, NamespaceManager);
if (identity == null)
identity = Value;
entry.ProcessMatch (true, elementQNameStack, this, NameTable, BaseURI, SchemaType, NamespaceManager, readerLineInfo, Depth, LocalName, NamespaceURI, identity, false, CurrentKeyFieldConsumers);
} while (MoveToNextAttribute ());
} finally {
MoveToElement ();
}
}
}
private XsdKeyTable CreateNewKeyTable (XmlSchemaIdentityConstraint ident)
{
XsdKeyTable seq = new XsdKeyTable (ident);
seq.StartDepth = reader.Depth;
this.keyTables.Add (seq);
return seq;
}
private void ValidateSimpleContentIdentity (
XmlSchemaDatatype dt, string value)
{
// Identity field value
if (currentKeyFieldConsumers != null) {
while (this.currentKeyFieldConsumers.Count > 0) {
XsdKeyEntryField field = this.currentKeyFieldConsumers [0] as XsdKeyEntryField;
if (field.Identity != null)
HandleError ("Two or more identical field was found. Former value is '" + field.Identity + "' .");
object identity = null; // This means empty value
if (dt != null) {
try {
identity = dt.ParseValue (value, NameTable, NamespaceManager);
} catch (Exception ex) { // FIXME: (wishlist) This is bad manner ;-(
HandleError ("Identity value is invalid against its data type " + dt.TokenizedType, ex);
}
}
if (identity == null)
identity = value;
if (!field.SetIdentityField (identity, reader.Depth == xsiNilDepth, dt as XsdAnySimpleType, this.Depth, readerLineInfo))
HandleError ("Two or more identical key value was found: '" + value + "' .");
this.currentKeyFieldConsumers.RemoveAt (0);
}
}
}
private void EndIdentityValidation (XsdKeyTable seq)
{
ArrayList errors = null;
for (int i = 0; i < seq.Entries.Count; i++) {
XsdKeyEntry entry = (XsdKeyEntry) seq.Entries [i];
if (entry.KeyFound)
continue;
if (seq.SourceSchemaIdentity is XmlSchemaKey) {
if (errors == null)
errors = new ArrayList ();
errors.Add ("line " + entry.SelectorLineNumber + "position " + entry.SelectorLinePosition);
}
}
if (errors != null)
HandleError ("Invalid identity constraints were found. Key was not found. "
+ String.Join (", ", errors.ToArray (typeof (string)) as string []));
// If it is keyref, then find reference target
XmlSchemaKeyref xsdKeyref = seq.SourceSchemaIdentity as XmlSchemaKeyref;
if (xsdKeyref != null)
EndKeyrefValidation (seq, xsdKeyref.Target);
}
private void EndKeyrefValidation (XsdKeyTable seq, XmlSchemaIdentityConstraint targetIdent)
{
for (int i = this.keyTables.Count - 1; i >= 0; i--) {
XsdKeyTable target = this.keyTables [i] as XsdKeyTable;
if (target.SourceSchemaIdentity != targetIdent)
continue;
seq.ReferencedKey = target;
for (int j = 0; j < seq.FinishedEntries.Count; j++) {
XsdKeyEntry entry = (XsdKeyEntry) seq.FinishedEntries [j];
for (int k = 0; k < target.FinishedEntries.Count; k++) {
XsdKeyEntry targetEntry = (XsdKeyEntry) target.FinishedEntries [k];
if (entry.CompareIdentity (targetEntry)) {
entry.KeyRefFound = true;
break;
}
}
}
}
if (seq.ReferencedKey == null)
HandleError ("Target key was not found.");
ArrayList errors = null;
for (int i = 0; i < seq.FinishedEntries.Count; i++) {
XsdKeyEntry entry = (XsdKeyEntry) seq.FinishedEntries [i];
if (!entry.KeyRefFound) {
if (errors == null)
errors = new ArrayList ();
errors.Add (" line " + entry.SelectorLineNumber + ", position " + entry.SelectorLinePosition);
}
}
if (errors != null)
HandleError ("Invalid identity constraints were found. Referenced key was not found: "
+ String.Join (" / ", errors.ToArray (typeof (string)) as string []));
}
#endregion
// Overrided Methods
public override void Close ()
{
reader.Close ();
}
public override string GetAttribute (int i)
{
switch (reader.NodeType) {
case XmlNodeType.XmlDeclaration:
case XmlNodeType.DocumentType:
return reader.GetAttribute (i);
}
if (reader.AttributeCount > i)
reader.GetAttribute (i);
int defIdx = i - reader.AttributeCount;
if (i < AttributeCount)
return defaultAttributes [defIdx].DefaultValue;
throw new ArgumentOutOfRangeException ("i", i, "Specified attribute index is out of range.");
}
public override string GetAttribute (string name)
{
switch (reader.NodeType) {
case XmlNodeType.XmlDeclaration:
case XmlNodeType.DocumentType:
return reader.GetAttribute (name);
}
string value = reader.GetAttribute (name);
if (value != null)
return value;
QName qname = SplitQName (name);
return GetDefaultAttribute (qname.Name, qname.Namespace);
}
private QName SplitQName (string name)
{
if (!XmlChar.IsName (name))
throw new ArgumentException ("Invalid name was specified.", "name");
Exception ex = null;
QName qname = XmlSchemaUtil.ToQName (reader, name, out ex);
if (ex != null)
return QName.Empty;
else
return qname;
}
public override string GetAttribute (string localName, string ns)
{
switch (reader.NodeType) {
case XmlNodeType.XmlDeclaration:
case XmlNodeType.DocumentType:
return reader.GetAttribute (localName, ns);
}
string value = reader.GetAttribute (localName, ns);
if (value != null)
return value;
return GetDefaultAttribute (localName, ns);
}
private string GetDefaultAttribute (string localName, string ns)
{
int idx = this.FindDefaultAttribute (localName, ns);
if (idx < 0)
return null;
string value = defaultAttributes [idx].ValidatedDefaultValue;
if (value == null)
value = defaultAttributes [idx].ValidatedFixedValue;
return value;
}
private int FindDefaultAttribute (string localName, string ns)
{
for (int i = 0; i < this.defaultAttributes.Length; i++) {
XsAttribute attr = defaultAttributes [i];
if (attr.QualifiedName.Name == localName &&
(ns == null || attr.QualifiedName.Namespace == ns))
return i;
}
return -1;
}
public bool HasLineInfo ()
{
return readerLineInfo != null && readerLineInfo.HasLineInfo ();
}
public override string LookupNamespace (string prefix)
{
return reader.LookupNamespace (prefix);
}
public override void MoveToAttribute (int i)
{
switch (reader.NodeType) {
case XmlNodeType.XmlDeclaration:
case XmlNodeType.DocumentType:
reader.MoveToAttribute (i);
return;
}
currentAttrType = null;
if (i < reader.AttributeCount) {
reader.MoveToAttribute (i);
this.currentDefaultAttribute = -1;
this.defaultAttributeConsumed = false;
}
if (i < AttributeCount) {
this.currentDefaultAttribute = i - reader.AttributeCount;
this.defaultAttributeConsumed = false;
}
else
throw new ArgumentOutOfRangeException ("i", i, "Attribute index is out of range.");
}
public override bool MoveToAttribute (string name)
{
switch (reader.NodeType) {
case XmlNodeType.XmlDeclaration:
case XmlNodeType.DocumentType:
return reader.MoveToAttribute (name);
}
currentAttrType = null;
bool b = reader.MoveToAttribute (name);
if (b) {
this.currentDefaultAttribute = -1;
this.defaultAttributeConsumed = false;
return true;
}
return MoveToDefaultAttribute (name, null);
}
public override bool MoveToAttribute (string localName, string ns)
{
switch (reader.NodeType) {
case XmlNodeType.XmlDeclaration:
case XmlNodeType.DocumentType:
return reader.MoveToAttribute (localName, ns);
}
currentAttrType = null;
bool b = reader.MoveToAttribute (localName, ns);
if (b) {
this.currentDefaultAttribute = -1;
this.defaultAttributeConsumed = false;
return true;
}
return MoveToDefaultAttribute (localName, ns);
}
private bool MoveToDefaultAttribute (string localName, string ns)
{
int idx = this.FindDefaultAttribute (localName, ns);
if (idx < 0)
return false;
currentDefaultAttribute = idx;
defaultAttributeConsumed = false;
return true;
}
public override bool MoveToElement ()
{
currentDefaultAttribute = -1;
defaultAttributeConsumed = false;
currentAttrType = null;
return reader.MoveToElement ();
}
public override bool MoveToFirstAttribute ()
{
switch (reader.NodeType) {
case XmlNodeType.XmlDeclaration:
case XmlNodeType.DocumentType:
return reader.MoveToFirstAttribute ();
}
currentAttrType = null;
if (reader.AttributeCount > 0) {
bool b = reader.MoveToFirstAttribute ();
if (b) {
currentDefaultAttribute = -1;
defaultAttributeConsumed = false;
}
return b;
}
if (this.defaultAttributes.Length > 0) {
currentDefaultAttribute = 0;
defaultAttributeConsumed = false;
return true;
}
else
return false;
}
public override bool MoveToNextAttribute ()
{
switch (reader.NodeType) {
case XmlNodeType.XmlDeclaration:
case XmlNodeType.DocumentType:
return reader.MoveToNextAttribute ();
}
currentAttrType = null;
if (currentDefaultAttribute >= 0) {
if (defaultAttributes.Length == currentDefaultAttribute + 1)
return false;
currentDefaultAttribute++;
defaultAttributeConsumed = false;
return true;
}
bool b = reader.MoveToNextAttribute ();
if (b) {
currentDefaultAttribute = -1;
defaultAttributeConsumed = false;
return true;
}
if (defaultAttributes.Length > 0) {
currentDefaultAttribute = 0;
defaultAttributeConsumed = false;
return true;
}
else
return false;
}
private XmlSchema ReadExternalSchema (string uri)
{
Uri absUri = resolver.ResolveUri ((BaseURI != "" ? new Uri (BaseURI) : null), uri);
string absUriString = absUri != null ? absUri.ToString () : String.Empty;
XmlTextReader xtr = null;
try {
xtr = new XmlTextReader (absUriString,
(Stream) resolver.GetEntity (
absUri, null, typeof (Stream)),
NameTable);
return XmlSchema.Read (
xtr, ValidationEventHandler);
} finally {
if (xtr != null)
xtr.Close ();
}
}
private void ExamineAdditionalSchema ()
{
if (resolver == null || ValidationType == ValidationType.None)
return;
XmlSchema schema = null;
string schemaLocation = reader.GetAttribute ("schemaLocation", XmlSchema.InstanceNamespace);
bool schemaAdded = false;
if (schemaLocation != null) {
string [] tmp = null;
try {
schemaLocation = XsDatatype.FromName ("token", XmlSchema.Namespace).Normalize (schemaLocation);
tmp = schemaLocation.Split (XmlChar.WhitespaceChars);
} catch (Exception ex) {
if (schemas.Count == 0)
HandleError ("Invalid schemaLocation attribute format.", ex, true);
tmp = new string [0];
}
if (tmp.Length % 2 != 0)
if (schemas.Count == 0)
HandleError ("Invalid schemaLocation attribute format.");
int i=0;
do {
try {
for (; i < tmp.Length; i += 2) {
schema = ReadExternalSchema (tmp [i + 1]);
if (schema.TargetNamespace == null)
schema.TargetNamespace = tmp [i];
else if (schema.TargetNamespace != tmp [i])
HandleError ("Specified schema has different target namespace.");
if (schema != null) {
if (!schemas.Contains (schema.TargetNamespace)) {
schemaAdded = true;
schemas.Add (schema);
}
schema = null;
}
}
} catch (Exception) {
if (!schemas.Contains (tmp [i]))
HandleError (String.Format ("Could not resolve schema location URI: {0}",
i + 1 < tmp.Length ? tmp [i + 1] : String.Empty), null, true);
i += 2;
continue;
}
} while (i < tmp.Length);
}
string noNsSchemaLocation = reader.GetAttribute ("noNamespaceSchemaLocation", XmlSchema.InstanceNamespace);
if (noNsSchemaLocation != null) {
try {
schema = ReadExternalSchema (noNsSchemaLocation);
} catch (Exception) { // FIXME: (wishlist) It is bad manner ;-(
if (schemas.Count != 0)
HandleError ("Could not resolve schema location URI: " + noNsSchemaLocation, null, true);
}
if (schema != null && schema.TargetNamespace != null)
HandleError ("Specified schema has different target namespace.");
}
if (schema != null) {
if (!schemas.Contains (schema.TargetNamespace)) {
schemaAdded = true;
schemas.Add (schema);
}
}
// FIXME: should call Reprocess()?
if (schemaAdded)
schemas.Compile ();
}
public override bool Read ()
{
validationStarted = true;
currentDefaultAttribute = -1;
defaultAttributeConsumed = false;
currentAttrType = null;
defaultAttributes = emptyAttributeArray;
bool result = reader.Read ();
// FIXME: schemaLocation could be specified
// at any Depth.
if (reader.Depth == 0 &&
reader.NodeType == XmlNodeType.Element) {
// If the reader is DTDValidatingReader (it
// is the default behavior of
// XmlValidatingReader) and DTD didn't appear,
// we could just use its source XmlReader.
DTDValidatingReader dtdr = reader as DTDValidatingReader;
if (dtdr != null && dtdr.DTD == null)
reader = dtdr.Source;
ExamineAdditionalSchema ();
}
if (schemas.Count == 0)
return result;
if (!schemas.IsCompiled)
schemas.Compile ();
#region ID Constraints
if (this.checkIdentity)
idManager.OnStartElement ();
// 3.3.4 ElementLocallyValidElement 7 = Root Valid.
if (!result && this.checkIdentity &&
idManager.HasMissingIDReferences ())
HandleError ("There are missing ID references: " + idManager.GetMissingIDString ());
#endregion
switch (reader.NodeType) {
case XmlNodeType.Element:
#region Key Constraints
if (checkKeyConstraints)
this.elementQNameStack.Add (new QName (reader.LocalName, reader.NamespaceURI));
#endregion
// If there is no schema information, then no validation is performed.
if (skipValidationDepth < 0 || reader.Depth <= skipValidationDepth) {
ValidateEndSimpleContent ();
AssessStartElementSchemaValidity ();
}
if (reader.IsEmptyElement)
goto case XmlNodeType.EndElement;
else if (xsiNilDepth < reader.Depth)
shouldValidateCharacters = true;
break;
case XmlNodeType.EndElement:
if (reader.Depth == skipValidationDepth)
skipValidationDepth = -1;
else if (skipValidationDepth < 0 || reader.Depth <= skipValidationDepth)
AssessEndElementSchemaValidity ();
if (checkKeyConstraints)
elementQNameStack.RemoveAt (elementQNameStack.Count - 1);
break;
case XmlNodeType.CDATA:
case XmlNodeType.SignificantWhitespace:
case XmlNodeType.Whitespace:
case XmlNodeType.Text:
if (skipValidationDepth >= 0 && reader.Depth > skipValidationDepth)
break;
ComplexType ct = Context.ActualType as ComplexType;
if (ct != null) {
switch (ct.ContentType) {
case XmlSchemaContentType.ElementOnly:
if (reader.NodeType != XmlNodeType.Whitespace)
HandleError (String.Format ("Not allowed character content is found (current content model '{0}' is element-only).", ct.QualifiedName));
break;
case XmlSchemaContentType.Empty:
HandleError (String.Format ("Not allowed character content is found (current element content model '{0}' is empty).", ct.QualifiedName));
break;
}
}
ValidateCharacters ();
break;
}
return result;
}
public override bool ReadAttributeValue ()
{
if (currentDefaultAttribute < 0)
return reader.ReadAttributeValue ();
if (this.defaultAttributeConsumed)
return false;
defaultAttributeConsumed = true;
return true;
}
// XmlReader.ReadString() should call derived this.Read().
public override string ReadString ()
{
return base.ReadString ();
}
// This class itself does not have this feature.
public override void ResolveEntity ()
{
reader.ResolveEntity ();
}
}
internal class XsdValidationContext
{
public XsdValidationContext ()
{
}
object xsi_type;
public object XsiType { get { return xsi_type; } set { xsi_type = value; } } // xsi:type
internal XsdValidationState State;
Stack element_stack = new Stack ();
// Some of them might be missing (See the spec section 5.3, and also 3.3.4).
public XsElement Element {
get { return element_stack.Count > 0 ? element_stack.Peek () as XsElement : null; }
}
public void PushCurrentElement (XsElement element)
{
element_stack.Push (element);
}
public void PopCurrentElement ()
{
element_stack.Pop ();
}
// Note that it represents current element's type.
public object ActualType {
get {
// FIXME: actually this should also be stacked
if (element_stack.Count == 0)
return null;
if (XsiType != null)
return XsiType;
else
return Element != null ? Element.ElementType : null;
}
}
#if NET_2_0
public XmlSchemaType ActualSchemaType {
get {
object at = ActualType;
if (at == null)
return null;
XmlSchemaType st = at as XmlSchemaType;
if (st == null)
st = XmlSchemaType.GetBuiltInSimpleType (
((XmlSchemaDatatype) at).TypeCode);
return st;
}
}
#endif
public bool IsInvalid {
get { return State == XsdValidationState.Invalid; }
}
public object Clone ()
{
return MemberwiseClone ();
}
public void EvaluateStartElement (
string localName, string ns)
{
State = State.EvaluateStartElement (localName, ns);
}
public bool EvaluateEndElement ()
{
return State.EvaluateEndElement ();
}
}
internal class XsdIDManager
{
public XsdIDManager ()
{
}
Hashtable idList = new Hashtable ();
ArrayList missingIDReferences;
string thisElementId;
private ArrayList MissingIDReferences {
get {
if (missingIDReferences == null)
missingIDReferences = new ArrayList ();
return missingIDReferences;
}
}
public void OnStartElement ()
{
thisElementId = null;
}
// 3.4.4-5 wild IDs
public string AssessEachAttributeIdentityConstraint (
XsDatatype dt, object parsedValue, string elementName)
{
// Validate identity constraints.
string str = parsedValue as string;
switch (dt.TokenizedType) {
case XmlTokenizedType.ID:
if (thisElementId != null)
return "ID type attribute was already assigned in the containing element.";
else
thisElementId = str;
if (idList.ContainsKey (str))
return "Duplicate ID value was found.";
else
idList.Add (str, elementName);
if (MissingIDReferences.Contains (str))
MissingIDReferences.Remove (str);
break;
case XmlTokenizedType.IDREF:
if (!idList.Contains (str) && !MissingIDReferences.Contains (str))
MissingIDReferences.Add (str);
break;
case XmlTokenizedType.IDREFS:
string [] idrefs = (string []) parsedValue;
for (int i = 0; i < idrefs.Length; i++) {
string id = idrefs [i];
if (!idList.Contains (id) && !MissingIDReferences.Contains (str))
MissingIDReferences.Add (id);
}
break;
}
return null;
}
public bool HasMissingIDReferences ()
{
return missingIDReferences != null
&& missingIDReferences.Count > 0;
}
public string GetMissingIDString ()
{
return String.Join (" ",
MissingIDReferences.ToArray (typeof (string))
as string []);
}
}
}