//
// (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;
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
-namespace Mono.Xml
+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
{
- public class XsdValidatingReader : XmlReader, IXmlLineInfo, IHasXmlSchemaInfo //, IHasXmlParserContext
+ internal class XsdValidatingReader : XmlReader, IXmlLineInfo, IHasXmlSchemaInfo, IHasXmlParserContext, IXmlNamespaceResolver
{
+ static readonly XsAttribute [] emptyAttributeArray =
+ new XsAttribute [0];
+
XmlReader reader;
- XmlValidatingReader xvReader;
- bool laxElementValidation = true;
- bool reportNoValidationError;
- XmlSchemaCollection schemas = new XmlSchemaCollection ();
+ XmlResolver resolver;
+ IHasXmlSchemaInfo sourceReaderSchemaInfo;
+ IXmlLineInfo readerLineInfo;
+ ValidationType validationType;
+ XmlSchemaSet schemas = new XmlSchemaSet ();
bool namespaces = true;
- XsdValidationState currentValidationState;
-// XsdAttributeValidationStateCollection attributeValidationStates;
- object elementXsiType;
- StringBuilder storedCharacters = new StringBuilder ();
+#region ID Constraints
+ bool checkIdentity = true;
+ XsdIDManager idManager = new XsdIDManager ();
+#endregion
- // [int Depth] -> XsdAutomata.
- // Some of them might be missing (See the spec section 5.3).
- Hashtable automataTable = new Hashtable ();
+#region Key Constraints
+ bool checkKeyConstraints = true;
+ ArrayList keyTables = new ArrayList ();
+ ArrayList currentKeyFieldConsumers;
+ ArrayList tmpKeyrefPool;
+#endregion
+ ArrayList elementQNameStack = new ArrayList ();
+
+ XsdParticleStateManager state = new XsdParticleStateManager ();
- StringCollection defaultAttributes = new StringCollection ();
- int currentDefaultAttribute;
+ int skipValidationDepth = -1;
+ int xsiNilDepth = -1;
+ StringBuilder storedCharacters = new StringBuilder ();
+ bool shouldValidateCharacters;
- // Property Cache.
- int attributeCount;
+ XsAttribute [] defaultAttributes = emptyAttributeArray;
+ int currentDefaultAttribute = -1;
+ ArrayList defaultAttributesCache = new ArrayList ();
+ bool defaultAttributeConsumed;
+ object currentAttrType;
#region .ctor
public XsdValidatingReader (XmlReader reader)
- : this (reader, null)
- {
- }
-
- public XsdValidatingReader (XmlReader reader, XmlReader validatingReader)
{
this.reader = reader;
- xvReader = validatingReader as XmlValidatingReader;
- if (xvReader != null) {
- if (xvReader.ValidationType == ValidationType.None)
- reportNoValidationError = true;
+ 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
-// Non-overrides
+ // Public Non-overrides
+
+ public int XsiNilDepth {
+ get { return xsiNilDepth; }
+ }
public bool Namespaces {
get { return namespaces; }
set { namespaces = value; }
}
- public XmlReader Reader {
- get { return reader; }
+ // This is required to resolve xsi:schemaLocation
+ public XmlResolver XmlResolver {
+ set {
+ resolver = value;
+ }
}
// This should be changed before the first Read() call.
- public XmlSchemaCollection Schemas {
+ public XmlSchemaSet Schemas {
get { return schemas; }
+ set {
+ if (ReadState != ReadState.Initial)
+ throw new InvalidOperationException ("Schemas must be set before the first call to Read().");
+ schemas = value;
+ }
}
public object SchemaType {
switch (NodeType) {
case XmlNodeType.Element:
- if (elementXsiType != null)
- return elementXsiType;
- else if (currentValidationState != null)
- return currentValidationState.Element.ElementType;
+ if (Context.ActualType != null)
+ return Context.ActualType;
else
- return null;
+ return SourceReaderSchemaType;
case XmlNodeType.Attribute:
- throw new NotImplementedException ();
+ 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 null;
+ return SourceReaderSchemaType;
}
}
}
- // This property is never used in Mono.
+ private object SourceReaderSchemaType {
+ get { return this.sourceReaderSchemaInfo != null ? sourceReaderSchemaInfo.SchemaType : null; }
+ }
+
public ValidationType ValidationType {
- get {
- if (reportNoValidationError)
- return ValidationType.None;
- else
- return ValidationType.Schema;
+ get { return validationType; }
+ set {
+ if (ReadState != ReadState.Initial)
+ throw new InvalidOperationException ("ValidationType must be set before reading.");
+ validationType = value;
}
}
- public XmlResolver XmlResolver {
- set { throw new NotImplementedException (); }
+ IDictionary IXmlNamespaceResolver.GetNamespacesInScope (XmlNamespaceScope scope)
+ {
+ IXmlNamespaceResolver resolver = reader as IXmlNamespaceResolver;
+ if (resolver == null)
+ throw new NotSupportedException ("The input XmlReader does not implement IXmlNamespaceResolver and thus this validating reader cannot collect in-scope namespaces.");
+ return resolver.GetNamespacesInScope (scope);
+ }
+
+ string IXmlNamespaceResolver.LookupPrefix (string ns)
+ {
+ return ((IXmlNamespaceResolver) this).LookupPrefix (ns, false);
+ }
+
+ string IXmlNamespaceResolver.LookupPrefix (string ns, bool atomizedNames)
+ {
+ IXmlNamespaceResolver resolver = reader as IXmlNamespaceResolver;
+ if (resolver == null)
+ throw new NotSupportedException ("The input XmlReader does not implement IXmlNamespaceResolver and thus this validating reader cannot execute namespace prefix lookup.");
+ return resolver.LookupPrefix (ns, atomizedNames);
}
- // TODO: provide XmlNamespaceManager to ParseValue() if possible
+ // It is used only for independent XmlReader use, not for XmlValidatingReader.
+#if NET_2_0
+ [Obsolete]
+ public override object ReadTypedValue ()
+#else
public object ReadTypedValue ()
+#endif
{
+ object o = XmlSchemaUtil.ReadTypedValue (this,
+ SchemaType, ParserContext.NamespaceManager,
+ storedCharacters);
+ storedCharacters.Length = 0;
+ return o;
+ }
+
+ private object ReadTypedValueCore ()
+ {
+ XsDatatype dt = SchemaType as XsDatatype;
+ SimpleType st = SchemaType as SimpleType;
+ if (st != null)
+ dt = st.Datatype;
+ if (dt == null)
+ return null;
+
switch (NodeType) {
case XmlNodeType.Element:
- XmlSchemaDatatype xsDatatype = currentValidationState.Datatype;
- if (xsDatatype != null)
- return xsDatatype.ParseValue (ReadString (), NameTable, null);
- else
+ if (IsEmptyElement)
return null;
+
+ storedCharacters.Length = 0;
+ bool loop = true;
+ do {
+ Read ();
+ switch (NodeType) {
+ case XmlNodeType.SignificantWhitespace:
+ case XmlNodeType.Text:
+ case XmlNodeType.CDATA:
+ storedCharacters.Append (Value);
+ break;
+ case XmlNodeType.Comment:
+ break;
+ default:
+ loop = false;
+ break;
+ }
+ } while (loop && !EOF && ReadState == ReadState.Interactive);
+ return dt.ParseValue (storedCharacters.ToString (), NameTable, ParserContext.NamespaceManager);
case XmlNodeType.Attribute:
- throw new NotImplementedException ();
-// xsDatatype = attributeValidationStates [LocalName, NamespaceURI].Datatype;
-// if (xsDatatype != null)
-// return xsDatatype.ParseValue (Value, NameTable, null);
-// else
-// return null;
- default:
- return null;
+ return dt.ParseValue (Value, NameTable, ParserContext.NamespaceManager);
}
+ return null;
}
- public ValidationEventHandler ValidationEventHandler;
-
-// Overrided Properties
+ // Public Overriden Properties
public override int AttributeCount {
get {
- if (NodeType == XmlNodeType.Element)
- return attributeCount;
- else if (IsDefault)
- return 0;
- else
- return reader.AttributeCount;
+ return reader.AttributeCount + defaultAttributes.Length;
}
}
}
public override int Depth {
- get { return reader.Depth; }
+ get {
+ if (currentDefaultAttribute < 0)
+ return reader.Depth;
+ if (this.defaultAttributeConsumed)
+ return reader.Depth + 2;
+ return reader.Depth + 1;
+ }
}
public override bool EOF {
}
public override bool HasValue {
- get { throw new NotImplementedException (); }
+ get {
+ if (currentDefaultAttribute < 0)
+ return reader.HasValue;
+ return true;
+ }
}
public override bool IsDefault {
- // TODO: handle default node
- get { return false; }
+ get {
+ if (currentDefaultAttribute < 0)
+ return reader.IsDefault;
+ return true;
+ }
}
public override bool IsEmptyElement {
- // TODO: consider default attributes
- get { return reader.IsEmptyElement; }
+ get {
+ if (currentDefaultAttribute < 0)
+ return reader.IsEmptyElement;
+ return false;
+ }
}
public override string this [int i] {
get { return GetAttribute (localName, ns); }
}
- int IXmlLineInfo.LineNumber {
- get { throw new NotImplementedException (); }
+ public int LineNumber {
+ get { return readerLineInfo != null ? readerLineInfo.LineNumber : 0; }
}
- int IXmlLineInfo.LinePosition {
- get { throw new NotImplementedException (); }
+ public int LinePosition {
+ get { return readerLineInfo != null ? readerLineInfo.LinePosition : 0; }
}
public override string LocalName {
- // TODO: handle default node
- get { return reader.LocalName; }
+ get {
+ if (currentDefaultAttribute < 0)
+ return reader.LocalName;
+ if (defaultAttributeConsumed)
+ return String.Empty;
+ return defaultAttributes [currentDefaultAttribute].QualifiedName.Name;
+ }
}
public override string Name {
- // TODO: handle default node
- get { return reader.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 {
- // TODO: handle default node
- get { return reader.NamespaceURI; }
+ get {
+ if (currentDefaultAttribute < 0)
+ return reader.NamespaceURI;
+ if (defaultAttributeConsumed)
+ return String.Empty;
+ return defaultAttributes [currentDefaultAttribute].QualifiedName.Namespace;
+ }
}
public override XmlNameTable NameTable {
}
public override XmlNodeType NodeType {
- // TODO: handle default node
- get { return reader.NodeType; }
+ get {
+ if (currentDefaultAttribute < 0)
+ return reader.NodeType;
+ if (defaultAttributeConsumed)
+ return XmlNodeType.Text;
+ return XmlNodeType.Attribute;
+ }
+ }
+
+ public XmlParserContext ParserContext {
+ get { return XmlSchemaUtil.GetParserContext (reader); }
}
public override string Prefix {
- // TODO: handle default node
- get { return reader.Prefix; }
+ get {
+ if (currentDefaultAttribute < 0)
+ return reader.Prefix;
+ if (defaultAttributeConsumed)
+ return String.Empty;
+ QName qname = defaultAttributes [currentDefaultAttribute].QualifiedName;
+ string prefix = this.ParserContext.NamespaceManager.LookupPrefix (qname.Namespace, false);
+ if (prefix == null)
+ return String.Empty;
+ else
+ return prefix;
+ }
}
public override char QuoteChar {
}
public override string Value {
- // TODO: handle default node
- get { return reader.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 { throw new NotImplementedException (); }
+ 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 { throw new NotImplementedException (); }
+ 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)
{
- if (reportNoValidationError)
+ 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;
- ValidationEventArgs e = new ValidationEventArgs (
- new XmlSchemaException (error, null), error, XmlSeverityType.Error);
+ ValException schemaException = new ValException (error,
+ this, this.BaseURI, null, innerException);
+ HandleError (schemaException, isWarning);
+ }
- if (this.ValidationEventHandler != null)
- this.ValidationEventHandler (this, e);
- else
-#if NON_MONO_ENV
- this.xvReader.OnValidationEvent (this, e);
-#else
+ 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;
-#endif
+ }
+
+ 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 (schemas.Count == 0) // No validation is performed.
+ 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.SetElement (state.CurrentElement);
+ }
+
+ private void ValidateEndElementParticle ()
+ {
+ if (Context.State != null) {
+ if (!Context.EvaluateEndElement ()) {
+ HandleError ("Invalid end element: " + reader.Name);
+ }
+ }
+ 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;
- // Creating element automata, if current does not exist.
- if (currentValidationState == null) {
- XmlSchemaElement root = null;
- foreach (XmlSchema target in schemas) {
- XmlSchema matches = target.Schemas [reader.NamespaceURI];
- if (matches != null) {
- root = target.Elements [new XmlQualifiedName (reader.LocalName, reader.NamespaceURI)] as XmlSchemaElement;
- if (root != null) {
- XsdValidationStateFactory factory = new XsdValidationStateFactory ();
- currentValidationState = factory.Create (root);
+ 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;
+ dt = ct.Datatype;
+ switch (ct.ContentType) {
+ case XmlSchemaContentType.ElementOnly:
+ 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;
+ }
+
+ // 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);
+ 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, ParserContext.NamespaceManager);
+ } catch (Exception ex) { // FIXME: (wishlist) better exception handling ;-(
+ HandleError ("List type value contains one or more invalid values.", ex);
+ break;
+ }
}
else
- HandleError ("Invalid start element. Element declaration for " + reader.LocalName + " is missing.");
+ 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, ParserContext.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)) {
+ HandleError ("Specified value was invalid against the facets.");
+ break;
+ }
+ }
+ validatedDatatype = st.Datatype;
+ break;
+ }
+ }
+ if (validatedDatatype != null) {
+ try {
+ validatedDatatype.ParseValue (value, NameTable, ParserContext.NamespaceManager);
+ } catch (Exception ex) { // FIXME: (wishlist) It is bad manner ;-(
+ HandleError ("Invalidly typed data was specified.", ex);
+ }
+ }
+ }
+
+ 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.SetElement (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;
}
}
- if (root == null && reader.NamespaceURI != String.Empty)
- HandleError ("Invalid start element. Element declaration for " + reader.LocalName + " is missing.");
+ 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);
}
+ }
- if (currentValidationState == null)
- return; // no validation.
+ // 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 ();
+ }
- if (!currentValidationState.EvaluateStartElement (reader.LocalName, reader.NamespaceURI))
- HandleError ("Invalid start element: " + reader.LocalName);
+ // 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.
+ }
- automataTable [reader.Depth] = currentValidationState;
- XmlSchemaElement el = currentValidationState.Element;
- XmlSchemaComplexType ctype = el.ElementType as XmlSchemaComplexType;
- if (ctype != null && ctype.ContentTypeParticle != null) {
- currentValidationState = currentValidationState.Factory.Create (ctype.ContentTypeParticle);
+ // 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;
+ try {
+ parsedValue = dt.ParseValue (normalized, reader.NameTable, this.ParserContext.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, this.ParserContext.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
}
+ }
- // TODO: Attribute validation
+ 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 ValidateEndElementParticle ()
+ private void AssessEndElementSchemaValidity ()
{
- if (currentValidationState != null) {
- if (!currentValidationState.EvaluateEndElement ()) {
- HandleError ("Invalid end element: " + reader.Name);
+ ValidateEndElementParticle (); // validate against childrens' state.
+
+ ValidateEndSimpleContent ();
+
+ // 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--;
}
}
- currentValidationState = automataTable [reader.Depth] as XsdValidationState;
- automataTable [reader.Depth + 1] = null;
}
- private void ValidateCharacters ()
+ // 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, ParserContext.NamespaceManager, readerLineInfo, Depth, null, null, null, isNil, CurrentKeyFieldConsumers);
+ if (MoveToFirstAttribute ()) {
+ try {
+ do {
+ switch (NamespaceURI) {
+ case XmlNamespaceManager.XmlnsXmlns:
+ case XmlSchema.InstanceNamespace:
+ continue;
+ }
+ entry.ProcessMatch (true, elementQNameStack, this, NameTable, BaseURI, SchemaType, ParserContext.NamespaceManager, readerLineInfo, Depth, LocalName, NamespaceURI, Value, 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, ParserContext.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)
{
- // TODO: value context validation here.
+ 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
+ // Overrided Methods
public override void Close ()
{
reader.Close ();
}
- // MonoTODO
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);
-// else if (defaultAttributes.Count)
- throw new NotImplementedException ();
+ int defIdx = i - reader.AttributeCount;
+ if (i < AttributeCount)
+ return defaultAttributes [defIdx].DefaultValue;
+
+ throw new ArgumentOutOfRangeException ("i", i, "Specified attribute index is out of range.");
}
- // MonoTODO
public override string GetAttribute (string name)
{
- return reader.GetAttribute (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;
}
- // MonoTODO
public override string GetAttribute (string localName, string ns)
{
- return reader.GetAttribute (localName, 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;
}
- // When it is default attribute, does it works?
- bool IXmlLineInfo.HasLineInfo ()
+ public bool HasLineInfo ()
{
- throw new NotImplementedException ();
+ return readerLineInfo != null && readerLineInfo.HasLineInfo ();
}
- // MonoTODO
public override string LookupNamespace (string prefix)
{
return reader.LookupNamespace (prefix);
}
- // MonoTODO
+ string IXmlNamespaceResolver.LookupNamespace (string prefix, bool atomizedNames)
+ {
+ IXmlNamespaceResolver res = reader as IXmlNamespaceResolver;
+ if (res != null)
+ return res.LookupNamespace (prefix, atomizedNames);
+ else
+ return reader.LookupNamespace (prefix);
+ }
+
public override void MoveToAttribute (int i)
{
- reader.MoveToAttribute (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.");
}
- // MonoTODO
public override bool MoveToAttribute (string name)
{
- return reader.MoveToAttribute (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);
}
- // MonoTODO
public override bool MoveToAttribute (string localName, string ns)
{
- return reader.MoveToAttribute (localName);
+ 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 ()
{
- // TODO: handle default node
+ currentDefaultAttribute = -1;
+ defaultAttributeConsumed = false;
+ currentAttrType = null;
return reader.MoveToElement ();
}
public override bool MoveToFirstAttribute ()
{
- // TODO: handle default node
- return reader.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 ()
{
- // TODO: handle default node
- return reader.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)
+ 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) {
+ HandleError ("Invalid schemaLocation attribute format.", ex, true);
+ tmp = new string [0];
+ }
+ if (tmp.Length % 2 != 0)
+ HandleError ("Invalid schemaLocation attribute format.");
+ for (int i = 0; i < tmp.Length; i += 2) {
+ try {
+ schema = ReadExternalSchema (tmp [i + 1]);
+ } catch (Exception) { // FIXME: (wishlist) It is bad manner ;-(
+ HandleError ("Could not resolve schema location URI: " + tmp [i + 1], null, true);
+ continue;
+ }
+ 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;
+ string noNsSchemaLocation = reader.GetAttribute ("noNamespaceSchemaLocation", XmlSchema.InstanceNamespace);
+ if (noNsSchemaLocation != null) {
+ try {
+ schema = ReadExternalSchema (noNsSchemaLocation);
+ } catch (Exception) { // FIXME: (wishlist) It is bad manner ;-(
+ 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 ()
{
+ currentDefaultAttribute = -1;
+ defaultAttributeConsumed = false;
+ currentAttrType = null;
+#region ID Constraints
+ if (this.checkIdentity)
+ idManager.OnStartElement ();
+#endregion
+ defaultAttributes = emptyAttributeArray;
+
bool result = reader.Read ();
+#region ID Constraints
+ // 3.3.4 ElementLocallyValidElement 7 = Root Valid.
+ if (!result && this.checkIdentity &&
+ idManager.HasMissingIDReferences ())
+ HandleError ("There are missing ID references: " + idManager.GetMissingIDString ());
+#endregion
+
+ // FIXME: schemaLocation could be specified
+ // at any Depth.
+ if (reader.Depth == 0 &&
+ reader.NodeType == XmlNodeType.Element)
+ ExamineAdditionalSchema ();
+ if (schemas.Count == 0)
+ return result;
+ if (!schemas.IsCompiled)
+ schemas.Compile ();
switch (reader.NodeType) {
case XmlNodeType.Element:
- ValidateStartElementParticle ();
- // TODO: validate xsi:nil, create xsi:type, and so on.
- // TODO: validate attributes
+#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
+ shouldValidateCharacters = true;
break;
-
case XmlNodeType.EndElement:
- ValidateEndElementParticle ();
- // TODO: validate content data type.
+ 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.Text:
+ // FIXME: does this check make sense?
+ ComplexType ct = Context.ActualType as ComplexType;
+ if (ct != null && storedCharacters.Length > 0) {
+ switch (ct.ContentType) {
+ case XmlSchemaContentType.ElementOnly:
+ case XmlSchemaContentType.Empty:
+ HandleError ("Not allowed character content was found.");
+ break;
+ }
+ }
+
ValidateCharacters ();
break;
}
public override bool ReadAttributeValue ()
{
- // TODO: handle default node
- return reader.ReadAttributeValue ();
+ if (currentDefaultAttribute < 0)
+ return reader.ReadAttributeValue ();
+
+ if (this.defaultAttributeConsumed)
+ return false;
+
+ defaultAttributeConsumed = true;
+ return true;
}
#if NET_1_0
}
}
+ internal class XsdValidationContext
+ {
+ public XsdValidationContext ()
+ {
+ }
+
+ // Some of them might be missing (See the spec section 5.3, and also 3.3.4).
+ public XsElement Element;
+ public object XsiType; // xsi:type
+ internal XsdValidationState State;
+
+ // Note that it represents current element's type.
+ public object ActualType {
+ get {
+ 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 ();
+ }
+
+ public void SetElement (XsElement element)
+ {
+ Element = element;
+ }
+ }
+
+ 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.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.Add (id);
+ }
+ break;
+ }
+ return null;
+ }
+
+ public object FindID (string name)
+ {
+ return idList [name];
+ }
+
+ public bool HasMissingIDReferences ()
+ {
+ return missingIDReferences != null
+ && missingIDReferences.Count > 0;
+ }
+
+ public string GetMissingIDString ()
+ {
+ return String.Join (" ",
+ MissingIDReferences.ToArray (typeof (string))
+ as string []);
+ }
+ }
}