// // Mono.Xml.Schema.XsdValidatingReader.cs // // Author: // Atsushi Enomoto (ginga@kit.hi-ho.ne.jp) // // (C)2003 Atsushi Enomoto // // Note: // // This class doesn't support set_XmlResolver, since it isn't common to XmlReader interface. // Try to set that of xml reader which is used to construct this object. // using System; using System.Collections; using System.Collections.Specialized; using System.Text; using System.Xml; using System.Xml.Schema; using Mono.Xml; namespace Mono.Xml.Schema { internal class XsdValidatingReader : XmlReader, IXmlLineInfo, IHasXmlSchemaInfo, IHasXmlParserContext { static char [] wsChars = new char [] {' ', '\t', '\n', '\r'}; XmlReader reader; XmlValidatingReader xvReader; XmlResolver resolver; IHasXmlSchemaInfo sourceReaderSchemaInfo; IXmlLineInfo readerLineInfo; bool laxElementValidation = true; bool reportNoValidationError; XmlSchemaCollection schemas = new XmlSchemaCollection (); bool namespaces = true; Hashtable idList = new Hashtable (); ArrayList missingIDReferences = new ArrayList (); string thisElementId; ArrayList keyTables = new ArrayList (); ArrayList currentKeyFieldConsumers = new ArrayList (); XsdValidationStateManager stateManager = new XsdValidationStateManager (); XsdValidationContext context = new XsdValidationContext (); XsdValidationState childParticleState; int xsiNilDepth = -1; StringBuilder storedCharacters = new StringBuilder (); bool shouldValidateCharacters; int skipValidationDepth = -1; XmlSchemaAttribute [] defaultAttributes = new XmlSchemaAttribute [0]; int currentDefaultAttribute = -1; XmlQualifiedName currentQName; ArrayList elementQNameStack = new ArrayList (); bool popContext; // Property Cache. int nonDefaultAttributeCount; bool defaultAttributeConsumed; // Validation engine cached object ArrayList defaultAttributesCache = new ArrayList (); ArrayList tmpKeyrefPool = new ArrayList (); #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; } #endregion // Provate Properties private XmlQualifiedName CurrentQName { get { if (currentQName == null) currentQName = new XmlQualifiedName (LocalName, NamespaceURI); return currentQName; } } internal ArrayList CurrentKeyFieldConsumers { get { return currentKeyFieldConsumers; } } // 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 { get { return schemas; } } public object SchemaType { get { if (ReadState != ReadState.Interactive) return null; switch (NodeType) { case XmlNodeType.Element: if (context.ActualType != null) return context.ActualType; else if (context.Element != null) return context.Element.ElementType; else return SourceReaderSchemaType; case XmlNodeType.Attribute: XmlSchemaComplexType ct = context.ActualType as XmlSchemaComplexType; if (ct != null) { XmlSchemaAttribute attdef = ct.AttributeUses [CurrentQName] as XmlSchemaAttribute; if (attdef != null) return attdef.AttributeType; } return SourceReaderSchemaType; default: return SourceReaderSchemaType; } } } private object SourceReaderSchemaType { get { return this.sourceReaderSchemaInfo != null ? sourceReaderSchemaInfo.SchemaType : null; } } // This property is never used in Mono. public ValidationType ValidationType { get { if (reportNoValidationError) return ValidationType.None; else return ValidationType.Schema; } } // It is used only for independent XmlReader use, not for XmlValidatingReader. public object ReadTypedValue () { XmlSchemaDatatype dt = SchemaType as XmlSchemaDatatype; XmlSchemaSimpleType st = SchemaType as XmlSchemaSimpleType; if (st != null) dt = st.Datatype; if (dt == null) return null; switch (NodeType) { case XmlNodeType.Element: 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); return dt.ParseValue (storedCharacters.ToString (), NameTable, ParserContext.NamespaceManager); case XmlNodeType.Attribute: return dt.ParseValue (Value, NameTable, ParserContext.NamespaceManager); } return null; } /* public ValueType ReadTypedValueType () { XmlSchemaDatatype dt = SchemaType as XmlSchemaDatatype; XmlSchemaSimpleType st = SchemaType as XmlSchemaSimpleType; if (st != null) dt = st.Datatype; if (dt == null) return null; switch (NodeType) { case XmlNodeType.Element: 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); return dt.ParseValueType (storedCharacters.ToString (), NameTable, ParserContext.NamespaceManager); case XmlNodeType.Attribute: return dt.ParseValueType (Value, NameTable, ParserContext.NamespaceManager); } return null; } */ public ValidationEventHandler ValidationEventHandler; // Public Overrided Properties public override int AttributeCount { get { /* if (NodeType == XmlNodeType.Element) return attributeCount; else if (IsDefault) return 0; else return reader.AttributeCount; */ return nonDefaultAttributeCount + 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); } } int IXmlLineInfo.LineNumber { get { return readerLineInfo != null ? readerLineInfo.LineNumber : 0; } } int IXmlLineInfo.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; XmlQualifiedName 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); } } public override string Prefix { get { if (currentDefaultAttribute < 0) return reader.Prefix; if (defaultAttributeConsumed) return String.Empty; XmlQualifiedName 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 { 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; } } XmlQualifiedName qnameXmlLang = new XmlQualifiedName ("lang", XmlNamespaceManager.XmlnsXml); 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 XmlQualifiedName QualifyName (string name) { int colonAt = name.IndexOf (':'); if (colonAt < 0) return new XmlQualifiedName (name, null); else return new XmlQualifiedName (name.Substring (colonAt + 1), LookupNamespace (name.Substring (0, colonAt))); } 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 (reportNoValidationError) // extra quick check return; XmlSchemaException schemaException = new XmlSchemaException (error, this, this.BaseURI, null, innerException); HandleError (schemaException, isWarning); } private void HandleError (XmlSchemaException schemaException) { HandleError (schemaException, false); } private void HandleError (XmlSchemaException schemaException, bool isWarning) { if (reportNoValidationError) return; ValidationEventArgs e = new ValidationEventArgs (schemaException, schemaException.Message, isWarning ? XmlSeverityType.Warning : XmlSeverityType.Error); if (this.ValidationEventHandler != null) this.ValidationEventHandler (this, e); else if (xvReader != null) xvReader.OnValidationEvent (this, e); else if (e.Severity == XmlSeverityType.Error) #if NON_MONO_ENV this.xvReader.OnValidationEvent (this, e); #else throw e.Exception; #endif } private XmlSchemaElement FindElement (string name, string ns) { foreach (XmlSchema target in schemas) { XmlSchema matches = target.Schemas [ns]; if (matches != null) { XmlSchemaElement result = target.Elements [new XmlQualifiedName (name, ns)] as XmlSchemaElement; if (result != null) return result; } } return null; } private XmlSchemaType FindType (XmlQualifiedName qname) { foreach (XmlSchema target in schemas) { XmlSchemaType type = target.SchemaTypes [qname] as XmlSchemaType; if (type != null) return type; } return null; } private void ValidateStartElementParticle () { stateManager.CurrentElement = null; context.ParticleState = context.ParticleState.EvaluateStartElement (reader.LocalName, reader.NamespaceURI); if (context.ParticleState == XsdValidationState.Invalid) HandleError ("Invalid start element: " + reader.NamespaceURI + ":" + reader.LocalName); context.Element = stateManager.CurrentElement; if (context.Element != null) context.SchemaType = context.Element.ElementType; } private void ValidateEndElementParticle () { if (childParticleState != null) { if (!childParticleState.EvaluateEndElement ()) { HandleError ("Invalid end element: " + reader.Name); } } context.PopScope (reader.Depth); } // 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."); storedCharacters.Append (reader.Value); } // Utility for missing validation completion related to child items. private void ValidateEndCharacters () { if (context.ActualType == null) return; string value = storedCharacters.ToString (); if (storedCharacters.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; } } XmlSchemaDatatype dt = context.ActualType as XmlSchemaDatatype; XmlSchemaSimpleType st = context.ActualType as XmlSchemaSimpleType; if (dt == null) { if (st != null) { dt = st.Datatype; } else { XmlSchemaComplexType ct = context.ActualType as XmlSchemaComplexType; dt = ct.Datatype; switch (ct.ContentType) { case XmlSchemaContentType.ElementOnly: case XmlSchemaContentType.Empty: if (storedCharacters.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); } // Identity field value 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)) HandleError ("Two or more identical key value was found: '" + value + "' ."); this.currentKeyFieldConsumers.RemoveAt (0); } shouldValidateCharacters = false; } // 3.14.4 String Valid private void AssessStringValid (XmlSchemaSimpleType st, XmlSchemaDatatype dt, string value) { XmlSchemaDatatype validatedDatatype = dt; if (st != null) { string normalized = validatedDatatype.Normalize (value); string [] values; XmlSchemaDatatype itemDatatype; XmlSchemaSimpleType itemSimpleType; switch (st.DerivedBy) { case XmlSchemaDerivationMethod.List: XmlSchemaSimpleTypeList listContent = st.Content as XmlSchemaSimpleTypeList; values = normalized.Split (wsChars); itemDatatype = listContent.ValidatedListItemType as XmlSchemaDatatype; itemSimpleType = listContent.ValidatedListItemType as XmlSchemaSimpleType; 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 AssessStringValid (itemSimpleType, itemSimpleType.Datatype, each); } break; case XmlSchemaDerivationMethod.Union: XmlSchemaSimpleTypeUnion union = st.Content as XmlSchemaSimpleTypeUnion; { string each = normalized; // validate against ValidatedItemType bool passed = false; foreach (object eachType in union.ValidatedTypes) { itemDatatype = eachType as XmlSchemaDatatype; itemSimpleType = eachType as XmlSchemaSimpleType; 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 (XmlSchemaException) { continue; } } passed = true; break; } if (!passed) { HandleError ("Union type value contains one or more invalid values."); break; } } break; case XmlSchemaDerivationMethod.Restriction: XmlSchemaSimpleTypeRestriction str = st.Content as XmlSchemaSimpleTypeRestriction; // 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. XmlSchemaSimpleType baseType = st.BaseXmlSchemaType as XmlSchemaSimpleType; 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 GetLocalTypeDefinition (string name) { object xsiType = null; XmlQualifiedName typeQName = QualifyName (name); if (typeQName == XmlSchemaComplexType.AnyTypeName) xsiType = XmlSchemaComplexType.AnyType; else if (XmlSchemaUtil.IsBuiltInDatatypeName (typeQName)) xsiType = XmlSchemaDatatype.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; XmlSchemaComplexType baseComplexType = baseType as XmlSchemaComplexType; XmlSchemaComplexType xsiComplexType = xsiSchemaType as XmlSchemaComplexType; 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 (XmlSchemaException ex) { // HandleError ("Locally specified schema complex type derivation failed. " + ex.Message, ex); HandleError (ex); } else { XmlSchemaSimpleType xsiSimpleType = xsiType as XmlSchemaSimpleType; if (xsiSimpleType != null) { try { xsiSimpleType.ValidateTypeDerivationOK (baseType, null, null, true); } catch (XmlSchemaException ex) { // HandleError ("Locally specified schema simple type derivation failed. " + ex.Message, ex); HandleError (ex); } } else if (xsiType is XmlSchemaDatatype) { // 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."); context.Load (reader.Depth); if (childParticleState != null) { context.ParticleState = childParticleState; childParticleState = null; } // If validation state exists, then first assess particle validity. context.SchemaType = null; if (context.ParticleState != null) { 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 = GetLocalTypeDefinition (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."); } XmlSchemaComplexType xsiComplexType = xsiType as XmlSchemaComplexType; 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.LocalTypeDefinition = xsiType; } } } else context.LocalTypeDefinition = null; // Create Validation Root, if not exist. // [Schema Validity Assessment (Element) 1.1] if (context.Element == null) context.Element = FindElement (reader.LocalName, reader.NamespaceURI); if (context.Element != null) { if (xsiTypeName == null) { context.SchemaType = context.Element.ElementType; AssessElementLocallyValidElement (context.Element, xsiNilValue); // 1.1.2 } } else { XmlSchema schema; switch (stateManager.ProcessContents) { case XmlSchemaContentProcessing.Skip: break; case XmlSchemaContentProcessing.Lax: /* schema = schemas [reader.NamespaceURI]; if (schema != null && !schema.missedSubComponents) HandleError ("Element declaration for " + reader.LocalName + " is missing."); */ break; default: schema = schemas [reader.NamespaceURI]; if (xsiTypeName == null && (schema == null || !schema.missedSubComponents)) HandleError ("Element declaration for " + reader.LocalName + " is missing."); break; } } if (stateManager.ProcessContents == XmlSchemaContentProcessing.Skip) skipValidationDepth = reader.Depth; // Finally, create child particle state. XmlSchemaComplexType xsComplexType = SchemaType as XmlSchemaComplexType; if (xsComplexType != null) childParticleState = stateManager.Create (xsComplexType.ValidatableParticle); else if (stateManager.ProcessContents == XmlSchemaContentProcessing.Lax) childParticleState = stateManager.Create (XmlSchemaAny.AnyTypeContent); else childParticleState = stateManager.Create (XmlSchemaParticle.Empty); AssessStartIdentityConstraints (); context.PushScope (reader.Depth); } // 3.3.4 Element Locally Valid (Element) private void AssessElementLocallyValidElement (XmlSchemaElement element, string xsiNilValue) { XmlQualifiedName qname = new XmlQualifiedName (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 else if (xsiNilValue == "true") { // AssessElementSchemaValidity() and ValidateCharacters() if (element.ValidatedFixedValue != null) HandleError ("Schema instance nil was specified, where the element declaration for " + qname + "has fixed value constraints."); } // 4. string xsiType = reader.GetAttribute ("type", XmlSchema.InstanceNamespace); if (xsiType != null) { context.LocalTypeDefinition = GetLocalTypeDefinition (xsiType); AssessLocalTypeDerivationOK (context.LocalTypeDefinition, element.ElementType, element.BlockResolved); } else context.LocalTypeDefinition = 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; } XmlSchemaComplexType cType = schemaType as XmlSchemaComplexType; XmlSchemaSimpleType sType = schemaType as XmlSchemaSimpleType; 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 (XmlSchemaComplexType cType) { // 1. if (cType.IsAbstract) HandleError ("Target complex type is abstract."); // 2 (xsi:nil and content prohibition) // See AssessStartElementSchemaValidity() and ValidateCharacters() string elementNs = reader.NamespaceURI; // 3. attribute uses and // 5. wild IDs while (reader.MoveToNextAttribute ()) { if (reader.NamespaceURI == "http://www.w3.org/2000/xmlns/") continue; else if (reader.NamespaceURI == XmlSchema.InstanceNamespace) continue; XmlQualifiedName qname = new XmlQualifiedName (reader.LocalName, reader.NamespaceURI); object attMatch = FindAttributeDeclaration (cType, qname, elementNs); if (attMatch == null) HandleError ("Attribute declaration was not found for " + qname); else { XmlSchemaAttribute attdecl = attMatch as XmlSchemaAttribute; if (attdecl == null) { // i.e. anyAttribute XmlSchemaAnyAttribute anyAttrMatch = attMatch as XmlSchemaAnyAttribute; } else { AssessAttributeLocallyValidUse (attdecl); AssessAttributeLocallyValid (attdecl, true); } } } reader.MoveToElement (); // Collect default attributes. // 4. foreach (DictionaryEntry entry in cType.AttributeUses) { XmlSchemaAttribute attr = (XmlSchemaAttribute) 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) defaultAttributesCache.Add (attr); else if (attr.ValidatedFixedValue != null) defaultAttributesCache.Add (attr); } } defaultAttributes = (XmlSchemaAttribute []) defaultAttributesCache.ToArray (typeof (XmlSchemaAttribute)); context.DefaultAttributes = defaultAttributes; defaultAttributesCache.Clear (); // 5. wild IDs was already checked above. } // Spec 3.10.4 Item Valid (Wildcard) private bool AttributeWildcardItemValid (XmlSchemaAnyAttribute anyAttr, XmlQualifiedName qname) { if (anyAttr.HasValueAny) return true; if (anyAttr.HasValueOther && (anyAttr.TargetNamespace == "" || reader.NamespaceURI != anyAttr.TargetNamespace)) return true; if (anyAttr.HasValueTargetNamespace && reader.NamespaceURI == anyAttr.TargetNamespace) return true; if (anyAttr.HasValueLocal && reader.NamespaceURI == "") return true; for (int i = 0; i < anyAttr.ResolvedNamespaces.Count; i++) if (anyAttr.ResolvedNamespaces [i] == reader.NamespaceURI) return true; return false; } private XmlSchemaObject FindAttributeDeclaration (XmlSchemaComplexType cType, XmlQualifiedName qname, string elementNs) { XmlSchemaObject result = cType.AttributeUses [qname]; if (result != null) return result; if (cType.AttributeWildcard == null) return null; if (!AttributeWildcardItemValid (cType.AttributeWildcard, qname)) return null; if (cType.AttributeWildcard.ResolvedProcessContents == XmlSchemaContentProcessing.Skip) return cType.AttributeWildcard; foreach (XmlSchema schema in schemas) { foreach (DictionaryEntry entry in schema.Attributes) { XmlSchemaAttribute attr = (XmlSchemaAttribute) entry.Value; if (attr.QualifiedName == qname) return attr; } } if (cType.AttributeWildcard.ResolvedProcessContents == XmlSchemaContentProcessing.Lax) return cType.AttributeWildcard; else return null; } // 3.2.4 Attribute Locally Valid and 3.4.4 - 5.wildIDs private void AssessAttributeLocallyValid (XmlSchemaAttribute attr, bool checkWildIDs) { // 1. switch (reader.NamespaceURI) { case XmlNamespaceManager.XmlnsXml: case XmlNamespaceManager.XmlnsXmlns: case XmlSchema.InstanceNamespace: break; } // 2. - 4. if (attr.AttributeType == null) HandleError ("Attribute type is missing for " + attr.QualifiedName); XmlSchemaDatatype dt = attr.AttributeType as XmlSchemaDatatype; if (dt == null) dt = ((XmlSchemaSimpleType) attr.AttributeType).Datatype; // It is a bit heavy process, so let's omit as long as possible ;-) if (dt != XmlSchemaSimpleType.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."); if (checkWildIDs) AssessEachAttributeIdentityConstraint (dt, normalized, parsedValue); } } private void AssessEachAttributeIdentityConstraint (XmlSchemaDatatype dt, string normalized, object parsedValue) { // Get normalized value and (if required) parsedValue if missing. switch (dt.TokenizedType) { case XmlTokenizedType.IDREFS: if (normalized == null) normalized = dt.Normalize (reader.Value); if (parsedValue == null) parsedValue = dt.ParseValue (normalized, reader.NameTable, ParserContext.NamespaceManager); break; case XmlTokenizedType.ID: case XmlTokenizedType.IDREF: if (normalized == null) normalized = dt.Normalize (reader.Value); break; } // Validate identity constraints. switch (dt.TokenizedType) { case XmlTokenizedType.ID: if (thisElementId != null) HandleError ("ID type attribute was already assigned in the containing element."); thisElementId = normalized; if (idList.Contains (normalized)) HandleError ("Duplicate ID value was found."); else idList.Add (normalized, normalized); if (missingIDReferences.Contains (normalized)) missingIDReferences.Remove (normalized); break; case XmlTokenizedType.IDREF: if (!idList.Contains (normalized)) missingIDReferences.Add (normalized); 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; } } private void AssessAttributeLocallyValidUse (XmlSchemaAttribute 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 () { if (childParticleState == null) childParticleState = context.ParticleState; ValidateEndElementParticle (); // validate against childrens' state. if (shouldValidateCharacters) { ValidateEndCharacters (); shouldValidateCharacters = false; } // 3.3.4 Assess ElementLocallyValidElement 5: value constraints. // 3.3.4 Assess ElementLocallyValidType 3.1.3. = StringValid(3.14.4) // => ValidateEndCharacters(). // Reset Identity constraints. for (int i = 0; i < keyTables.Count; i++) { XsdKeyTable keyTable = this.keyTables [i] as XsdKeyTable; if (keyTable.StartDepth == reader.Depth) { EndIdentityValidation (keyTable); } else { for (int k = 0; k < keyTable.Entries.Count; k++) { XsdKeyEntry entry = keyTable.Entries [k] as XsdKeyEntry; // Remove finished (maybe key not found) entries. if (entry.StartDepth == reader.Depth) { if (entry.KeyFound) keyTable.FinishedEntries.Add (entry); else if (entry.KeySequence.SourceSchemaIdentity is XmlSchemaKey) HandleError ("Key sequence is missing."); keyTable.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 keyseq = this.keyTables [i] as XsdKeyTable; if (keyseq.StartDepth == reader.Depth) { keyTables.RemoveAt (i); i--; } } // Reset xsi:nil, if required. if (xsiNilDepth == reader.Depth) xsiNilDepth = -1; } // 3.11.4 Identity Constraint Satisfied private void AssessStartIdentityConstraints () { 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) 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) != null) { // creates and registers new entry. XsdKeyEntry entry = new XsdKeyEntry (seq, reader); seq.Entries.Add (entry); } } // (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++) { XsdKeyEntry entry = seq.Entries [j] as XsdKeyEntry; try { entry.FieldMatches (this.elementQNameStack, this); } catch (Exception ex) { // FIXME: (wishlist) It is bad manner ;-( HandleError ("Identity field value is invalid against its data type.", ex); } } } } private XsdKeyTable CreateNewKeyTable (XmlSchemaIdentityConstraint ident) { XsdKeyTable seq = new XsdKeyTable (ident, this); seq.StartDepth = reader.Depth; XmlSchemaKeyref keyref = ident as XmlSchemaKeyref; this.keyTables.Add (seq); return seq; } private void EndIdentityValidation (XsdKeyTable seq) { ArrayList errors = new ArrayList (); for (int i = 0; i < seq.Entries.Count; i++) { XsdKeyEntry entry = (XsdKeyEntry) seq.Entries [i]; if (entry.KeyFound) continue; if (seq.SourceSchemaIdentity is XmlSchemaKey) errors.Add ("line " + entry.SelectorLineNumber + "position " + entry.SelectorLinePosition); } if (errors.Count > 0) HandleError ("Invalid identity constraints were found. Key was not found. " + String.Join (", ", errors.ToArray (typeof (string)) as string [])); errors.Clear (); // Find reference target XmlSchemaKeyref xsdKeyref = seq.SourceSchemaIdentity as XmlSchemaKeyref; if (xsdKeyref != null) { for (int i = this.keyTables.Count - 1; i >= 0; i--) { XsdKeyTable target = this.keyTables [i] as XsdKeyTable; if (target.SourceSchemaIdentity == xsdKeyref.Target) { 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."); for (int i = 0; i < seq.FinishedEntries.Count; i++) { XsdKeyEntry entry = (XsdKeyEntry) seq.FinishedEntries [i]; if (!entry.KeyRefFound) errors.Add (" line " + entry.SelectorLineNumber + ", position " + entry.SelectorLinePosition); } if (errors.Count > 0) HandleError ("Invalid identity constraints were found. Referenced key was not found: " + String.Join (" / ", errors.ToArray (typeof (string)) as string [])); } } // 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 - nonDefaultAttributeCount; 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; XmlQualifiedName qname = SplitQName (name); return GetDefaultAttribute (qname.Name, qname.Namespace); } private XmlQualifiedName SplitQName (string name) { if (!XmlChar.IsName (name)) throw new ArgumentException ("Invalid name was specified.", "name"); Exception ex = null; XmlQualifiedName qname = XmlSchemaUtil.ToQName (reader, name, out ex); if (ex != null) return XmlQualifiedName.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++) { XmlSchemaAttribute attr = defaultAttributes [i]; if (attr.QualifiedName.Name == localName && (ns == null || attr.QualifiedName.Namespace == ns)) return i; } return -1; } bool IXmlLineInfo.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; } currentQName = null; if (i < this.nonDefaultAttributeCount) { reader.MoveToAttribute (i); this.currentDefaultAttribute = -1; this.defaultAttributeConsumed = false; } if (i < AttributeCount) { this.currentDefaultAttribute = i - nonDefaultAttributeCount; 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); } currentQName = 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); } currentQName = 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; currentQName = null; return reader.MoveToElement (); } public override bool MoveToFirstAttribute () { switch (reader.NodeType) { case XmlNodeType.XmlDeclaration: case XmlNodeType.DocumentType: return reader.MoveToFirstAttribute (); } currentQName = null; if (this.nonDefaultAttributeCount > 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 (); } currentQName = 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 void ExamineAdditionalSchema () { XmlSchema schema = null; string schemaLocation = reader.GetAttribute ("schemaLocation", XmlSchema.InstanceNamespace); if (schemaLocation != null) { string [] tmp = null; try { schemaLocation = XmlSchemaDatatype.FromName ("token").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) { Uri absUri = null; XmlTextReader xtr = null; try { absUri = new Uri ((this.BaseURI != "" ? new Uri (BaseURI) : null), tmp [i + 1]); xtr = new XmlTextReader (absUri.ToString ()); schema = XmlSchema.Read (xtr, null); } catch (Exception) { // FIXME: (wishlist) It is bad manner ;-( HandleError ("Could not resolve schema location URI: " + absUri, null, true); continue; } finally { if (xtr != null) xtr.Close (); } if (schema.TargetNamespace == null) schema.TargetNamespace = tmp [i]; else if (schema.TargetNamespace != tmp [i]) HandleError ("Specified schema has different target namespace."); } } if (schema != null) { try { schemas.Add (schema, resolver); } catch (XmlSchemaException ex) { HandleError (ex); } } schema = null; string noNsSchemaLocation = reader.GetAttribute ("noNamespaceSchemaLocation", XmlSchema.InstanceNamespace); if (noNsSchemaLocation != null) { Uri absUri = null; XmlTextReader xtr = null; try { absUri = new Uri ((this.BaseURI != "" ? new Uri (BaseURI) : null), noNsSchemaLocation); xtr = new XmlTextReader (absUri.ToString ()); schema = XmlSchema.Read (xtr, null); } catch (Exception) { // FIXME: (wishlist) It is bad manner ;-( HandleError ("Could not resolve schema location URI: " + absUri, null, true); } finally { if (xtr != null) xtr.Close (); } if (schema != null && schema.TargetNamespace != null) HandleError ("Specified schema has different target namespace."); } if (schema != null) { try { schema.Compile (ValidationEventHandler, resolver); schemas.Add (schema); } catch (XmlSchemaException ex) { HandleError (ex); } } } public override bool Read () { nonDefaultAttributeCount = 0; currentDefaultAttribute = -1; defaultAttributeConsumed = false; currentQName = null; thisElementId = null; defaultAttributes = new XmlSchemaAttribute [0]; if (popContext) { elementQNameStack.RemoveAt (elementQNameStack.Count - 1); popContext = false; } bool result = reader.Read (); // 3.3.4 ElementLocallyValidElement 7 = Root Valid. if (!result && missingIDReferences.Count > 0) HandleError ("There are missing ID references: " + String.Join (" ", this.missingIDReferences.ToArray (typeof (string)) as string [])); switch (reader.NodeType) { case XmlNodeType.XmlDeclaration: this.nonDefaultAttributeCount = reader.AttributeCount; break; case XmlNodeType.Element: nonDefaultAttributeCount = reader.AttributeCount; if (reader.Depth == 0) ExamineAdditionalSchema (); this.elementQNameStack.Add (new XmlQualifiedName (reader.LocalName, reader.NamespaceURI)); // If there is no schema information, then no validation is performed. if (schemas.Count == 0) break; if (skipValidationDepth < 0 || reader.Depth <= skipValidationDepth) { if (shouldValidateCharacters) { ValidateEndCharacters (); shouldValidateCharacters = false; } AssessStartElementSchemaValidity (); storedCharacters.Length = 0; } else { context.Clear (); } if (reader.IsEmptyElement) goto case XmlNodeType.EndElement; else shouldValidateCharacters = true; break; case XmlNodeType.EndElement: if (reader.Depth == skipValidationDepth) { skipValidationDepth = -1; context.Clear (); } else AssessEndElementSchemaValidity (); storedCharacters.Length = 0; childParticleState = null; popContext = true; break; case XmlNodeType.CDATA: case XmlNodeType.SignificantWhitespace: case XmlNodeType.Text: XmlSchemaComplexType ct = context.ActualType as XmlSchemaComplexType; 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; } return result; } public override bool ReadAttributeValue () { if (currentDefaultAttribute < 0) return reader.ReadAttributeValue (); if (this.defaultAttributeConsumed) return false; defaultAttributeConsumed = true; return true; } #if NET_1_0 public override string ReadInnerXml () { // MS.NET 1.0 has a serious bug here. It skips validation. return reader.ReadInnerXml (); } public override string ReadOuterXml () { // MS.NET 1.0 has a serious bug here. It skips validation. return reader.ReadOuterXml (); } #endif // XmlReader.ReadString() should call derived this.Read(). public override string ReadString () { #if NET_1_0 return reader.ReadString (); #else return base.ReadString (); #endif } // This class itself does not have this feature. public override void ResolveEntity () { reader.ResolveEntity (); } internal class XsdValidationContext { Hashtable contextStack; public XsdValidationContext () { contextStack = new Hashtable (); } // Some of them might be missing (See the spec section 5.3, and also 3.3.4). public XmlSchemaElement Element; public XsdValidationState ParticleState; public XmlSchemaAttribute [] DefaultAttributes; // Some of them might be missing (See the spec section 5.3). public object SchemaType; public object LocalTypeDefinition; public object ActualType { get { if (LocalTypeDefinition != null) return LocalTypeDefinition; else return SchemaType; } } public void Clear () { Element = null; SchemaType = null; ParticleState = null; LocalTypeDefinition = null; } public void PushScope (int depth) { contextStack [depth] = this.MemberwiseClone (); } public void PopScope (int depth) { Load (depth); contextStack.Remove (depth + 1); } public void Load (int depth) { Clear (); XsdValidationContext restored = (XsdValidationContext) contextStack [depth]; if (restored != null) { this.Element = restored.Element; this.ParticleState = restored.ParticleState; this.SchemaType = restored.SchemaType; this.LocalTypeDefinition = restored.LocalTypeDefinition; } } } /* internal class XsdValidityState { ArrayList currentParticles = new ArrayList (); ArrayList occured = new ArrayList (); Hashtable xsAllConsumed = new Hashtable (); XmlSchemaParticle parciele; int particleDepth; public XsdValidityState (XmlSchemaParticle particle) { this.parciele = particle; currentParticles.Add (particle); } } */ } }