namespace Mono.Xml.Schema
{
- internal class XsdValidatingReader : XmlReader, IXmlLineInfo, IHasXmlSchemaInfo, IHasXmlParserContext, IXmlNamespaceResolver
+ internal class XsdValidatingReader : XmlReader, IXmlLineInfo, IHasXmlSchemaInfo, IHasXmlParserContext
{
static readonly XsAttribute [] emptyAttributeArray =
new XsAttribute [0];
ValidationType validationType;
XmlSchemaSet schemas = new XmlSchemaSet ();
bool namespaces = true;
+ bool validationStarted;
#region ID Constraints
bool checkIdentity = true;
public XmlSchemaSet Schemas {
get { return schemas; }
set {
- if (ReadState != ReadState.Initial)
+ if (validationStarted)
throw new InvalidOperationException ("Schemas must be set before the first call to Read().");
schemas = value;
}
public ValidationType ValidationType {
get { return validationType; }
set {
- if (ReadState != ReadState.Initial)
+ if (validationStarted)
throw new InvalidOperationException ("ValidationType must be set before reading.");
validationType = value;
}
}
-#if NET_2_0
- IDictionary<string, string>
-#else
- IDictionary
-#endif
- 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)
- {
- 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);
- }
-
// 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,
+ SchemaType, NamespaceManager,
storedCharacters);
storedCharacters.Length = 0;
return o;
get { return XmlSchemaUtil.GetParserContext (reader); }
}
+ internal XmlNamespaceManager NamespaceManager {
+ get { return ParserContext != null ? ParserContext.NamespaceManager : null; }
+ }
+
public override string Prefix {
get {
if (currentDefaultAttribute < 0)
if (defaultAttributeConsumed)
return String.Empty;
QName qname = defaultAttributes [currentDefaultAttribute].QualifiedName;
- string prefix = this.ParserContext.NamespaceManager.LookupPrefix (qname.Namespace, false);
+ string prefix = NamespaceManager != null ? NamespaceManager.LookupPrefix (qname.Namespace, false) : null;
if (prefix == null)
return String.Empty;
else
if (Context.IsInvalid)
HandleError ("Invalid start element: " + reader.NamespaceURI + ":" + reader.LocalName);
- Context.SetElement (state.CurrentElement);
+ Context.PushCurrentElement (state.CurrentElement);
}
private void ValidateEndElementParticle ()
HandleError ("Invalid end element: " + reader.Name);
}
}
+ Context.PopCurrentElement ();
state.PopContext ();
}
dt = ct.Datatype;
switch (ct.ContentType) {
case XmlSchemaContentType.ElementOnly:
+ if (value.Length > 0 && !XmlChar.IsWhitespace (value))
+ HandleError ("Character content not allowed.");
+ break;
case XmlSchemaContentType.Empty:
if (value.Length > 0)
HandleError ("Character content not allowed.");
XsDatatype validatedDatatype = dt;
if (st != null) {
string normalized = validatedDatatype.Normalize (value);
+ ValidateRestrictedSimpleTypeValue (st, ref validatedDatatype, normalized);
+ }
+ if (validatedDatatype != null) {
+ try {
+ validatedDatatype.ParseValue (value, NameTable, NamespaceManager);
+ } catch (Exception ex) { // FIXME: (wishlist) It is bad manner ;-(
+ HandleError ("Invalidly typed data was specified.", ex);
+ }
+ }
+ }
+
+ void ValidateRestrictedSimpleTypeValue (SimpleType st, ref XsDatatype dt, string normalized)
+ {
+ {
string [] values;
XsDatatype itemDatatype;
SimpleType itemSimpleType;
// validate against ValidatedItemType
if (itemDatatype != null) {
try {
- itemDatatype.ParseValue (each, NameTable, ParserContext.NamespaceManager);
+ itemDatatype.ParseValue (each, NameTable, NamespaceManager);
} catch (Exception ex) { // FIXME: (wishlist) better exception handling ;-(
HandleError ("List type value contains one or more invalid values.", ex);
break;
itemSimpleType = eachType as SimpleType;
if (itemDatatype != null) {
try {
- itemDatatype.ParseValue (each, NameTable, ParserContext.NamespaceManager);
+ itemDatatype.ParseValue (each, NameTable, NamespaceManager);
} catch (Exception) { // FIXME: (wishlist) better exception handling ;-(
continue;
}
if (baseType != null) {
AssessStringValid(baseType, dt, normalized);
}
- if (!str.ValidateValueWithFacets (normalized, NameTable)) {
+ if (!str.ValidateValueWithFacets (normalized, NameTable, NamespaceManager)) {
HandleError ("Specified value was invalid against the facets.");
break;
}
}
- validatedDatatype = st.Datatype;
+ dt = 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)
// [Schema Validity Assessment (Element) 1.1]
if (Context.Element == null) {
state.CurrentElement = FindElement (reader.LocalName, reader.NamespaceURI);
- Context.SetElement (state.CurrentElement);
+ Context.PushCurrentElement (state.CurrentElement);
}
if (Context.Element != null) {
if (Context.XsiType == null) {
if (dt != SimpleType.AnySimpleType || attr.ValidatedFixedValue != null) {
string normalized = dt.Normalize (reader.Value);
object parsedValue = null;
+
+ // check part of 3.14.4 StringValid
+ SimpleType st = attr.AttributeType as SimpleType;
+ if (st != null)
+ ValidateRestrictedSimpleTypeValue (st, ref dt, normalized);
+
try {
- parsedValue = dt.ParseValue (normalized, reader.NameTable, this.ParserContext.NamespaceManager);
+ parsedValue = dt.ParseValue (normalized, reader.NameTable, NamespaceManager);
} catch (Exception ex) { // FIXME: (wishlist) It is bad manner ;-(
HandleError ("Attribute value is invalid against its data type " + dt.TokenizedType, ex);
}
- if (attr.ValidatedFixedValue != null && attr.ValidatedFixedValue != normalized) {
+
+ 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);
+ parsedValue = dt.ParseValue (attr.ValidatedFixedValue, reader.NameTable, NamespaceManager);
}
#region ID Constraints
if (this.checkIdentity) {
private void AssessEndElementSchemaValidity ()
{
- ValidateEndElementParticle (); // validate against childrens' state.
-
ValidateEndSimpleContent ();
+ ValidateEndElementParticle (); // validate against childrens' state.
+
// 3.3.4 Assess ElementLocallyValidElement 5: value constraints.
// 3.3.4 Assess ElementLocallyValidType 3.1.3. = StringValid(3.14.4)
// => ValidateEndSimpleContent().
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);
+ entry.ProcessMatch (false, elementQNameStack, this, NameTable, BaseURI, SchemaType, NamespaceManager, readerLineInfo, Depth, null, null, null, isNil, CurrentKeyFieldConsumers);
if (MoveToFirstAttribute ()) {
try {
do {
case XmlSchema.InstanceNamespace:
continue;
}
- entry.ProcessMatch (true, elementQNameStack, this, NameTable, BaseURI, SchemaType, ParserContext.NamespaceManager, readerLineInfo, Depth, LocalName, NamespaceURI, Value, false, CurrentKeyFieldConsumers);
+ XmlSchemaDatatype dt = SchemaType as XmlSchemaDatatype;
+ XmlSchemaSimpleType st = SchemaType as XmlSchemaSimpleType;
+ if (dt == null && st != null)
+ dt = st.Datatype;
+ object identity = null;
+ if (dt != null)
+ identity = dt.ParseValue (Value, NameTable, NamespaceManager);
+ if (identity == null)
+ identity = Value;
+ entry.ProcessMatch (true, elementQNameStack, this, NameTable, BaseURI, SchemaType, NamespaceManager, readerLineInfo, Depth, LocalName, NamespaceURI, identity, false, CurrentKeyFieldConsumers);
} while (MoveToNextAttribute ());
} finally {
MoveToElement ();
object identity = null; // This means empty value
if (dt != null) {
try {
- identity = dt.ParseValue (value, NameTable, ParserContext.NamespaceManager);
+ identity = dt.ParseValue (value, NameTable, NamespaceManager);
} catch (Exception ex) { // FIXME: (wishlist) This is bad manner ;-(
HandleError ("Identity value is invalid against its data type " + dt.TokenizedType, ex);
}
private void ExamineAdditionalSchema ()
{
- if (resolver == null)
+ if (resolver == null || ValidationType == ValidationType.None)
return;
XmlSchema schema = null;
string schemaLocation = reader.GetAttribute ("schemaLocation", XmlSchema.InstanceNamespace);
if (tmp.Length % 2 != 0)
if (schemas.Count == 0)
HandleError ("Invalid schemaLocation attribute format.");
- for (int i = 0; i < tmp.Length; i += 2) {
+ int i=0;
+ do {
try {
- schema = ReadExternalSchema (tmp [i + 1]);
- } catch (Exception) { // FIXME: (wishlist) It is bad manner ;-(
+ for (; i < tmp.Length; i += 2) {
+ schema = ReadExternalSchema (tmp [i + 1]);
+ if (schema.TargetNamespace == null)
+ schema.TargetNamespace = tmp [i];
+ else if (schema.TargetNamespace != tmp [i])
+ HandleError ("Specified schema has different target namespace.");
+ if (schema != null) {
+ if (!schemas.Contains (schema.TargetNamespace)) {
+ schemaAdded = true;
+ schemas.Add (schema);
+ }
+ schema = null;
+ }
+ }
+ } catch (Exception) {
if (!schemas.Contains (tmp [i]))
HandleError (String.Format ("Could not resolve schema location URI: {0}",
i + 1 < tmp.Length ? tmp [i + 1] : String.Empty), null, true);
+ i += 2;
continue;
}
- 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);
- }
+ } while (i < tmp.Length);
}
- schema = null;
string noNsSchemaLocation = reader.GetAttribute ("noNamespaceSchemaLocation", XmlSchema.InstanceNamespace);
if (noNsSchemaLocation != null) {
try {
public override bool Read ()
{
+ validationStarted = true;
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)
+ reader.NodeType == XmlNodeType.Element) {
+ // If the reader is DTDValidatingReader (it
+ // is the default behavior of
+ // XmlValidatingReader) and DTD didn't appear,
+ // we could just use its source XmlReader.
+ DTDValidatingReader dtdr = reader as DTDValidatingReader;
+ if (dtdr != null && dtdr.DTD == null)
+ reader = dtdr.Source;
+
ExamineAdditionalSchema ();
+ }
if (schemas.Count == 0)
return result;
if (!schemas.IsCompiled)
schemas.Compile ();
+#region ID Constraints
+ if (this.checkIdentity)
+ idManager.OnStartElement ();
+
+ // 3.3.4 ElementLocallyValidElement 7 = Root Valid.
+ if (!result && this.checkIdentity &&
+ idManager.HasMissingIDReferences ())
+ HandleError ("There are missing ID references: " + idManager.GetMissingIDString ());
+#endregion
+
switch (reader.NodeType) {
case XmlNodeType.Element:
#region Key Constraints
if (reader.IsEmptyElement)
goto case XmlNodeType.EndElement;
- else
+ else if (xsiNilDepth < reader.Depth)
shouldValidateCharacters = true;
break;
case XmlNodeType.EndElement:
case XmlNodeType.CDATA:
case XmlNodeType.SignificantWhitespace:
+ case XmlNodeType.Whitespace:
case XmlNodeType.Text:
- // FIXME: does this check make sense?
+ if (skipValidationDepth >= 0 && reader.Depth > skipValidationDepth)
+ break;
+
ComplexType ct = Context.ActualType as ComplexType;
- if (ct != null && storedCharacters.Length > 0) {
+ if (ct != null) {
switch (ct.ContentType) {
case XmlSchemaContentType.ElementOnly:
+ if (reader.NodeType != XmlNodeType.Whitespace)
+ HandleError (String.Format ("Not allowed character content is found (current content model '{0}' is element-only).", ct.QualifiedName));
+ break;
case XmlSchemaContentType.Empty:
- HandleError ("Not allowed character content was found.");
+ HandleError (String.Format ("Not allowed character content is found (current element content model '{0}' is empty).", ct.QualifiedName));
break;
}
}
{
}
- // 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
+ object xsi_type;
+ public object XsiType { get { return xsi_type; } set { xsi_type = value; } } // xsi:type
internal XsdValidationState State;
+ Stack element_stack = new Stack ();
+
+ // Some of them might be missing (See the spec section 5.3, and also 3.3.4).
+ public XsElement Element {
+ get { return element_stack.Count > 0 ? element_stack.Peek () as XsElement : null; }
+ }
+
+ public void PushCurrentElement (XsElement element)
+ {
+ element_stack.Push (element);
+ }
+
+ public void PopCurrentElement ()
+ {
+ element_stack.Pop ();
+ }
// Note that it represents current element's type.
public object ActualType {
get {
+ // FIXME: actually this should also be stacked
+ if (element_stack.Count == 0)
+ return null;
if (XsiType != null)
return XsiType;
else
{
return State.EvaluateEndElement ();
}
-
- public void SetElement (XsElement element)
- {
- Element = element;
- }
}
internal class XsdIDManager
MissingIDReferences.Remove (str);
break;
case XmlTokenizedType.IDREF:
- if (!idList.Contains (str))
+ if (!idList.Contains (str) && !MissingIDReferences.Contains (str))
MissingIDReferences.Add (str);
break;
case XmlTokenizedType.IDREFS:
string [] idrefs = (string []) parsedValue;
for (int i = 0; i < idrefs.Length; i++) {
string id = idrefs [i];
- if (!idList.Contains (id))
+ if (!idList.Contains (id) && !MissingIDReferences.Contains (str))
MissingIDReferences.Add (id);
}
break;
return null;
}
- public object FindID (string name)
- {
- return idList [name];
- }
-
public bool HasMissingIDReferences ()
{
return missingIDReferences != null