namespace System.Xml.Schema
{
- public class XmlSchemaValidator
+ public sealed class XmlSchemaValidator
{
enum Transition {
None,
int xsiNilDepth = -1;
int skipValidationDepth = -1;
- SOMObject currentType;
-
+ // LAMESPEC: XmlValueGetter is bogus by design because there
+ // is no way to get associated schema type for current value.
+ // Here XmlSchemaValidatingReader needs "current type"
+ // information to validate attribute values.
+ internal XmlSchemaDatatype CurrentAttributeType;
#endregion
set { xmlResolver = value; }
}
- [MonoTODO]
public Uri SourceUri {
get { return sourceUri; }
- // FIXME: actually there seems no setter, but then
- // it will never make sense.
set { sourceUri = value; }
}
#endregion
private bool IgnoreWarnings {
get { return (options & ValidationFlags
- .IgnoreValidationWarnings) != 0; }
+ .ReportValidationWarnings) == 0; }
}
private bool IgnoreIdentity {
get { return (options & ValidationFlags
- .IgnoreIdentityConstraints) != 0; }
+ .ProcessIdentityConstraints) == 0; }
}
#endregion
al.Add (p);
}
- [MonoTODO ("Need some tests.")]
+ [MonoTODO] // Need some tests.
// Its behavior is not obvious. For example, it does not
// contain groups (xs:sequence/xs:choice/xs:all). Since it
// might contain xs:any, it could not be of type element[].
typeof (XmlSchemaParticle));
}
- public void GetUnspecifiedDefaultAttributes (ArrayList list)
+ public void GetUnspecifiedDefaultAttributes (ArrayList defaultAttributeList)
{
+ if (defaultAttributeList == null)
+ throw new ArgumentNullException ("defaultAttributeList");
+
if (transition != Transition.StartTag)
throw new InvalidOperationException ("Method 'GetUnsoecifiedDefaultAttributes' works only when the validator state is inside a start tag.");
foreach (XmlSchemaAttribute attr
in GetExpectedAttributes ())
if (attr.ValidatedDefaultValue != null || attr.ValidatedFixedValue != null)
- list.Add (attr);
+ defaultAttributeList.Add (attr);
- list.AddRange (defaultAttributes);
- }
-
- public object FindID (string name)
- {
- // It looks returning the element's local name (string)
- return idManager.FindID (name);
+ defaultAttributeList.AddRange (defaultAttributes);
}
// State Controller
public void AddSchema (XmlSchema schema)
{
+ if (schema == null)
+ throw new ArgumentNullException ("schema");
schemas.Add (schema);
schemas.Compile ();
}
public void Initialize ()
{
- Initialize (null);
- }
-
- public void Initialize (SOMObject startType)
- {
- this.startType = startType;
transition = Transition.Content;
state = new XsdParticleStateManager ();
if (!schemas.IsCompiled)
schemas.Compile ();
}
+ public void Initialize (SOMObject partialValidationType)
+ {
+ if (partialValidationType == null)
+ throw new ArgumentNullException ("partialValidationType");
+ this.startType = partialValidationType;
+ Initialize ();
+ }
+
// It must be called at the end of the validation (to check
// identity constraints etc.).
public void EndValidation ()
}
// I guess it is for validation error recovery
- [MonoTODO ("Find out how XmlSchemaInfo is used.")]
+ [MonoTODO] // FIXME: Find out how XmlSchemaInfo is used.
public void SkipToEndElement (XmlSchemaInfo info)
{
CheckState (Transition.Content);
state.PopContext ();
}
+ public object ValidateAttribute (
+ string localName,
+ string ns,
+ string attributeValue,
+ XmlSchemaInfo info)
+ {
+ if (attributeValue == null)
+ throw new ArgumentNullException ("attributeValue");
+ return ValidateAttribute (localName, ns, delegate () { return attributeValue; }, info);
+ }
+
// I guess this weird XmlValueGetter is for such case that
// value might not be required (and thus it improves
// performance in some cases. Doh).
- // The return value is typed primitive, is possible.
+ // The return value is typed primitive, if possible.
// AttDeriv
public object ValidateAttribute (
string localName,
XmlValueGetter attributeValue,
XmlSchemaInfo info)
{
+ if (localName == null)
+ throw new ArgumentNullException ("localName");
+ if (ns == null)
+ throw new ArgumentNullException ("ns");
+ if (attributeValue == null)
+ throw new ArgumentNullException ("attributeValue");
+
CheckState (Transition.StartTag);
QName qname = new QName (localName, ns);
if (schemas.Count == 0)
return null;
- // 3.3.4 Element Locally Valid (Type) - attribute
- if (Context.ActualType is ComplexType)
- return AssessAttributeElementLocallyValidType (localName, ns, attributeValue, info);
- else
- HandleError ("Current simple type cannot accept attributes other than schema instance namespace.");
+ if (Context.Element != null && Context.XsiType == null) {
+
+ // 3.3.4 Element Locally Valid (Type) - attribute
+ if (Context.ActualType is ComplexType)
+ return AssessAttributeElementLocallyValidType (localName, ns, attributeValue, info);
+ else
+ HandleError ("Current simple type cannot accept attributes other than schema instance namespace.");
+ }
return null;
}
string schemaLocation,
string noNsSchemaLocation)
{
+ if (localName == null)
+ throw new ArgumentNullException ("localName");
+ if (ns == null)
+ throw new ArgumentNullException ("ns");
+
CheckState (Transition.Content);
transition = Transition.StartTag;
}
}
- public object ValidateEndElement (XmlSchemaInfo schemaInfo)
+ public object ValidateEndElement (XmlSchemaInfo info)
{
- return ValidateEndElement (schemaInfo, null);
+ return ValidateEndElement (info, null);
}
// The return value is typed primitive, if supplied.
// represented by current simple content type. (try passing
// some kind of object to this method to check the behavior.)
// EndTagDeriv
- [MonoTODO ("Handle 'var' parameter.")]
- public object ValidateEndElement (XmlSchemaInfo schemaInfo,
+ [MonoTODO] // FIXME: Handle 'var' parameter.
+ public object ValidateEndElement (XmlSchemaInfo info,
object var)
{
// If it is going to validate an empty element, then
// first validate end of attributes.
if (transition == Transition.StartTag)
- ValidateEndOfAttributes ();
+ ValidateEndOfAttributes (info);
CheckState (Transition.Content);
if (depth == skipValidationDepth)
skipValidationDepth = -1;
else if (skipValidationDepth < 0 || depth <= skipValidationDepth)
- ret = AssessEndElementSchemaValidity (schemaInfo);
+ ret = AssessEndElementSchemaValidity (info);
return ret;
}
// StartTagCloseDeriv
- public void ValidateEndOfAttributes ()
+ // FIXME: fill validity inside this invocation.
+ public void ValidateEndOfAttributes (XmlSchemaInfo info)
{
try {
CheckState (Transition.StartTag);
if (schemas.Count == 0)
return;
- AssessCloseStartElementSchemaValidity ();
+ if (skipValidationDepth < 0 || depth <= skipValidationDepth)
+ AssessCloseStartElementSchemaValidity (info);
+ depth++;
} finally {
occuredAtts.Clear ();
}
}
+ // LAMESPEC: It should also receive XmlSchemaInfo so that
+ // a validator application can receive simple type or
+ // or content type validation errors.
+ public void ValidateText (string value)
+ {
+ if (value == null)
+ throw new ArgumentNullException ("value");
+ ValidateText (delegate () { return value; });
+ }
+
// TextDeriv ... without text. Maybe typed check is done by
// ValidateAtomicValue().
public void ValidateText (XmlValueGetter getter)
{
+ if (getter == null)
+ throw new ArgumentNullException ("getter");
+
CheckState (Transition.Content);
if (schemas.Count == 0)
return;
ComplexType ct = Context.ActualType as ComplexType;
- if (ct != null && storedCharacters.Length > 0) {
+ if (ct != null) {
switch (ct.ContentType) {
- case XmlSchemaContentType.ElementOnly:
case XmlSchemaContentType.Empty:
HandleError ("Not allowed character content was found.");
break;
+ case XmlSchemaContentType.ElementOnly:
+ string s = storedCharacters.ToString ();
+ if (s.Length > 0 && !XmlChar.IsWhitespace (s))
+ HandleError ("Not allowed character content was found.");
+ break;
}
}
ValidateCharacters (getter);
}
- // TextDeriv...?
- [MonoTODO]
- public void ValidateWhitespace (XmlValueGetter getter)
+ public void ValidateWhitespace (string value)
{
- CheckState (Transition.Content);
- if (schemas.Count == 0)
- return;
+ if (value == null)
+ throw new ArgumentNullException ("value");
+ ValidateWhitespace (delegate () { return value; });
+ }
-// throw new NotImplementedException ();
+ // TextDeriv. It should do the same as ValidateText() in our actual implementation (whitespaces are conditioned).
+ public void ValidateWhitespace (XmlValueGetter getter)
+ {
+ ValidateText (getter);
}
#endregion
throw new InvalidOperationException ("Initialize() must be called before processing validation.");
else
throw new InvalidOperationException (
- String.Format ("Unexpected attempt to validation state transition from {0} to {1} was happened.",
+ String.Format ("Unexpected attempt to validate state transition from {0} to {1}.",
transition,
expected));
}
if (Context.IsInvalid)
HandleError ("Invalid start element: " + ns + ":" + localName);
- Context.SetElement (state.CurrentElement);
+ Context.PushCurrentElement (state.CurrentElement);
}
private void AssessOpenStartElementSchemaValidity (
// [Schema Validity Assessment (Element) 1.1]
if (Context.Element == null) {
state.CurrentElement = FindElement (localName, ns);
- Context.SetElement (state.CurrentElement);
+ Context.PushCurrentElement (state.CurrentElement);
}
#region Key Constraints
#endregion
}
- private void AssessCloseStartElementSchemaValidity ()
+ private void AssessCloseStartElementSchemaValidity (XmlSchemaInfo info)
{
if (Context.XsiType != null)
- AssessCloseStartElementLocallyValidType ();
+ AssessCloseStartElementLocallyValidType (info);
else if (Context.Element != null) {
// element locally valid is checked only when
// xsi:type does not exist.
AssessElementLocallyValidElement ();
if (Context.Element.ElementType != null)
- AssessCloseStartElementLocallyValidType ();
+ AssessCloseStartElementLocallyValidType (info);
}
if (Context.Element == null) {
next = state.Create (XmlSchemaParticle.Empty);
}
Context.State = next;
-
- depth++;
}
// It must be invoked after xsi:nil turned out not to be in
}
// 3.3.4 Element Locally Valid (Type)
- private void AssessCloseStartElementLocallyValidType ()
+ private void AssessCloseStartElementLocallyValidType (XmlSchemaInfo info)
{
object schemaType = Context.ActualType;
if (schemaType == null) { // 1.
// Attributes are checked in ValidateAttribute().
} else if (cType != null) {
// 3.2. Also, 2. is checked there.
- AssessCloseStartElementLocallyValidComplexType (cType);
+ AssessCloseStartElementLocallyValidComplexType (cType, info);
}
}
// 3.4.4 Element Locally Valid (Complex Type)
- private void AssessCloseStartElementLocallyValidComplexType (ComplexType cType)
+ // FIXME: use SchemaInfo for somewhere (? it is passed to ValidateEndOfAttributes() for some reason)
+ private void AssessCloseStartElementLocallyValidComplexType (ComplexType cType, XmlSchemaInfo info)
{
// 1.
if (cType.IsAbstract) {
XsAttribute attdecl = attMatch as XsAttribute;
if (attdecl != null) {
AssessAttributeLocallyValidUse (attdecl);
- return AssessAttributeLocallyValid (attdecl, getter);
+ return AssessAttributeLocallyValid (attdecl, info, getter);
} // otherwise anyAttribute or null.
return null;
}
// 3.2.4 Attribute Locally Valid and 3.4.4
- private object AssessAttributeLocallyValid (XsAttribute attr, XmlValueGetter getter)
+ private object AssessAttributeLocallyValid (XsAttribute attr, XmlSchemaInfo info, XmlValueGetter getter)
{
// 2. - 4.
if (attr.AttributeType == 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 (getter ());
object parsedValue = null;
try {
- parsedValue = dt.ParseValue (normalized, nameTable, nsResolver);
- } catch (Exception ex) { // FIXME: (wishlist) It is bad manner ;-(
- HandleError ("Attribute value is invalid against its data type " + dt.TokenizedType, ex);
+ CurrentAttributeType = dt;
+ parsedValue = getter ();
+ } catch (Exception ex) { // It is inevitable and bad manner.
+ HandleError (String.Format ("Attribute value is invalid against its data type {0}", dt != null ? dt.TokenizedType : default (XmlTokenizedType)), 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, nameTable, nsResolver);
+
+ // check part of 3.14.4 StringValid
+ SimpleType st = attr.AttributeType as SimpleType;
+ if (st != null)
+ ValidateRestrictedSimpleTypeValue (st, ref dt, new XmlAtomicValue (parsedValue, attr.AttributeSchemaType).Value);
+
+ if (attr.ValidatedFixedValue != null &&
+ !XmlSchemaUtil.AreSchemaDatatypeEqual (
+ attr.AttributeSchemaType.Datatype as XsdAnySimpleType, attr.ValidatedFixedTypedValue, dt as XsdAnySimpleType, parsedValue)) {
+ HandleError (String.Format ("The value of the attribute {0} does not match with its fixed value '{1}' in the space of type {2}", attr.QualifiedName, attr.ValidatedFixedValue, dt));
+ parsedValue = attr.ValidatedFixedTypedValue;
}
#region ID Constraints
if (!IgnoreIdentity) {
private object AssessEndElementSchemaValidity (
XmlSchemaInfo info)
{
- ValidateEndElementParticle (); // validate against childrens' state.
-
object ret = ValidateEndSimpleContent (info);
+ 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 ().
HandleError ("Invalid end element. There are still required content items.");
}
}
+ Context.PopCurrentElement ();
state.PopContext ();
}
if (xsiNilDepth >= 0 && xsiNilDepth < depth)
HandleError ("Element item appeared, while current element context is nil.");
- if (shouldValidateCharacters)
+ if (shouldValidateCharacters) {
+ CurrentAttributeType = null;
storedCharacters.Append (getter ());
+ }
}
dt = ct.Datatype;
switch (ct.ContentType) {
case XmlSchemaContentType.ElementOnly:
+ if (value.Length > 0 && !XmlChar.IsWhitespace (value))
+ HandleError ("Character content not allowed in an elementOnly model.");
+ break;
case XmlSchemaContentType.Empty:
if (value.Length > 0)
- HandleError ("Character content not allowed.");
+ HandleError ("Character content not allowed in an empty model.");
break;
}
}
if (info != null) {
info.IsNil = xsiNilDepth >= 0;
info.SchemaElement = null;
- info.SchemaType = st;
- if (st == null)
+ info.SchemaType = Context.ActualType as XmlSchemaType;
+ if (info.SchemaType == null)
info.SchemaType = XmlSchemaType.GetBuiltInSimpleType (dt.TypeCode);
info.SchemaAttribute = null;
info.IsDefault = false; // FIXME: might be true
if (itemDatatype != null) {
try {
retValues [vi] = itemDatatype.ParseValue (each, nameTable, nsResolver);
- } catch (Exception ex) { // FIXME: (wishlist) better exception handling ;-(
+ } catch (Exception ex) { // It is inevitable and bad manner.
HandleError ("List type value contains one or more invalid values.", ex);
break;
}
if (itemDatatype != null) {
try {
ret = itemDatatype.ParseValue (each, nameTable, nsResolver);
- } catch (Exception) { // FIXME: (wishlist) better exception handling ;-(
+ } catch (Exception) { // It is inevitable and bad manner.
continue;
}
}
// mmm, will check later.
SimpleType baseType = st.BaseXmlSchemaType as SimpleType;
if (baseType != null) {
- ret = AssessStringValid (baseType, dt, normalized);
+ ret = AssessStringValid (baseType, dt, value);
}
- if (!str.ValidateValueWithFacets (normalized, nameTable)) {
+ if (!str.ValidateValueWithFacets (value, nameTable, nsResolver)) {
HandleError ("Specified value was invalid against the facets.");
break;
}
if (validatedDatatype != null) {
try {
ret = validatedDatatype.ParseValue (value, nameTable, nsResolver);
- } catch (Exception ex) { // FIXME: (wishlist) It is bad manner ;-(
+ } catch (Exception ex) { // It is inevitable and bad manner.
HandleError (String.Format ("Invalidly typed data was specified."), ex);
}
}
return ret;
}
+ private void ValidateRestrictedSimpleTypeValue (SimpleType st, ref XsDatatype dt, string normalized)
+ {
+ {
+ string [] values;
+ XsDatatype itemDatatype;
+ SimpleType itemSimpleType;
+ switch (st.DerivedBy) {
+ case XmlSchemaDerivationMethod.List:
+ SimpleTypeList listContent = st.Content as SimpleTypeList;
+ values = normalized.Split (XmlChar.WhitespaceChars);
+ itemDatatype = listContent.ValidatedListItemType as XsDatatype;
+ itemSimpleType = listContent.ValidatedListItemType as SimpleType;
+ for (int vi = 0; vi < values.Length; vi++) {
+ string each = values [vi];
+ if (each == String.Empty)
+ continue;
+ // validate against ValidatedItemType
+ if (itemDatatype != null) {
+ try {
+ itemDatatype.ParseValue (each, nameTable, nsResolver);
+ } catch (Exception ex) { // FIXME: (wishlist) better exception handling ;-(
+ HandleError ("List type value contains one or more invalid values.", ex);
+ break;
+ }
+ }
+ else
+ AssessStringValid (itemSimpleType, itemSimpleType.Datatype, each);
+ }
+ break;
+ case XmlSchemaDerivationMethod.Union:
+ SimpleTypeUnion union = st.Content as SimpleTypeUnion;
+ {
+ string each = normalized;
+ // validate against ValidatedItemType
+ bool passed = false;
+ foreach (object eachType in union.ValidatedTypes) {
+ itemDatatype = eachType as XsDatatype;
+ itemSimpleType = eachType as SimpleType;
+ if (itemDatatype != null) {
+ try {
+ itemDatatype.ParseValue (each, nameTable, nsResolver);
+ } 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, nsResolver)) {
+ HandleError ("Specified value was invalid against the facets.");
+ break;
+ }
+ }
+ dt = st.Datatype;
+ break;
+ }
+ }
+ }
+
#endregion
#region Key Constraints Validation
XsdKeyTable seq = (XsdKeyTable) keyTables [i];
// If possible, create new field entry candidates.
for (int j = 0; j < seq.Entries.Count; j++) {
+ CurrentAttributeType = null;
try {
seq.Entries [j].ProcessMatch (
isAttr,
}
}
- private void ProcessKeyEntryOne (XsdKeyEntry entry, bool isAttr, bool isNil, object schemaType, string attrName, string attrNs, XmlValueGetter getter)
- {
- }
-
private void ValidateEndElementKeyConstraints ()
{
// Reset Identity constraints.
if (dt != null) {
try {
identity = dt.ParseValue (value, nameTable, nsResolver);
- } catch (Exception ex) { // FIXME: (wishlist) This is bad manner ;-(
+ } catch (Exception ex) { // It is inevitable and bad manner.
HandleError ("Identity value is invalid against its data type " + dt.TokenizedType, ex);
}
}
for (int i = 0; i < tmp.Length; i += 2) {
try {
schema = ReadExternalSchema (tmp [i + 1]);
- } catch (Exception ex) { // FIXME: (wishlist) It is bad manner ;-(
+ } catch (Exception ex) { // It is inevitable and bad manner.
HandleError ("Could not resolve schema location URI: " + schemaLocation, ex, true);
continue;
}
try {
schema = ReadExternalSchema (noNsSchemaLocation);
- } catch (Exception ex) { // FIXME: (wishlist) It is bad manner ;-(
+ } catch (Exception ex) { // It is inevitable and bad manner.
HandleError ("Could not resolve schema location URI: " + noNsSchemaLocation, ex, true);
}
if (schema != null && schema.TargetNamespace != null)