/*
* Copyright 2013 Guidewire Software, Inc.
*/
package gw.internal.xml.xsd.typeprovider.validator;
import gw.internal.ext.org.apache.xerces.impl.xpath.regex.RegularExpression;
import gw.internal.xml.XmlSimpleValueValidationContext;
import gw.internal.xml.xsd.typeprovider.primitive.XmlSchemaPrimitiveType;
import gw.internal.xml.xsd.typeprovider.schema.XmlSchemaFacet;
import gw.internal.xml.xsd.typeprovider.schema.XmlSchemaFractionDigitsFacet;
import gw.internal.xml.xsd.typeprovider.schema.XmlSchemaLengthFacet;
import gw.internal.xml.xsd.typeprovider.schema.XmlSchemaMaxExclusiveFacet;
import gw.internal.xml.xsd.typeprovider.schema.XmlSchemaMaxInclusiveFacet;
import gw.internal.xml.xsd.typeprovider.schema.XmlSchemaMaxLengthFacet;
import gw.internal.xml.xsd.typeprovider.schema.XmlSchemaMinExclusiveFacet;
import gw.internal.xml.xsd.typeprovider.schema.XmlSchemaMinInclusiveFacet;
import gw.internal.xml.xsd.typeprovider.schema.XmlSchemaMinLengthFacet;
import gw.internal.xml.xsd.typeprovider.schema.XmlSchemaPatternFacet;
import gw.internal.xml.xsd.typeprovider.schema.XmlSchemaTotalDigitsFacet;
import gw.internal.xml.xsd.typeprovider.schema.XmlSchemaWhiteSpaceFacet;
import gw.xml.XmlSimpleValueException;
import java.util.Comparator;
import java.util.HashMap;
import java.util.Map;
@SuppressWarnings({"UnusedDeclaration"})
public abstract class XmlSchemaFacetValidator<T extends XmlSchemaFacet> {
private static final Map<Class<? extends XmlSchemaFacet>, XmlSchemaFacetValidator> _validators = new HashMap<Class<? extends XmlSchemaFacet>, XmlSchemaFacetValidator>();
static {
// _validators.put( XmlSchemaEnumerationFacet.class, new XmlSchemaFacetValidator<XmlSchemaEnumerationFacet>() {
// @Override
// protected String validateInternal( XmlSchemaEnumerationFacet facet, String value ) {
// // Handled elsewhere, so this facet should never be passed to this method
// }
// } );
_validators.put( XmlSchemaFractionDigitsFacet.class, new XmlSchemaFacetValidator<XmlSchemaFractionDigitsFacet>() {
@Override
protected void validateInternal( XmlSchemaFractionDigitsFacet facet, String value, XmlSimpleValueValidationContext validationContext, XmlSchemaPrimitiveType primitiveType) throws XmlSimpleValueException {
if ( primitiveType != XmlSchemaPrimitiveType.DECIMAL ) {
throwDueToWrongPrimitiveType( "fractionDigits", primitiveType );
}
int idx = value.lastIndexOf( '.' );
int fractionDigits = 0;
if ( idx >= 0 ) {
fractionDigits = value.length() - idx - 1;
}
int maxDigits = Integer.parseInt( facet.getValue() );
if ( fractionDigits > maxDigits ) {
throw new XmlSimpleValueException( "fractionDigits " + fractionDigits + " > " + maxDigits );
}
}
} );
_validators.put( XmlSchemaLengthFacet.class, new XmlSchemaFacetValidator<XmlSchemaLengthFacet>() {
@Override
protected void validateInternal( XmlSchemaLengthFacet facet, String value, XmlSimpleValueValidationContext validationContext, XmlSchemaPrimitiveType primitiveType) throws XmlSimpleValueException {
int length = Integer.parseInt( facet.getValue() );
Integer actualLength = getActualLength( "length", value, validationContext, primitiveType );
if ( actualLength != null && actualLength != length ) {
throw new XmlSimpleValueException( "length " + actualLength + " != " + length );
}
}
} );
_validators.put( XmlSchemaMaxExclusiveFacet.class, new XmlSchemaFacetValidator<XmlSchemaMaxExclusiveFacet>() {
@Override
protected void validateInternal( XmlSchemaMaxExclusiveFacet facet, String value, XmlSimpleValueValidationContext validationContext, XmlSchemaPrimitiveType primitiveType) throws XmlSimpleValueException {
Comparator<String> comparator = primitiveType.getValueComparator();
if ( comparator != null ) {
int result = comparator.compare( value, facet.getValue() );
if ( result >= 0 ) {
throw new XmlSimpleValueException( "value must be less than " + facet.getValue() );
}
}
}
} );
_validators.put( XmlSchemaMaxInclusiveFacet.class, new XmlSchemaFacetValidator<XmlSchemaMaxInclusiveFacet>() {
@Override
protected void validateInternal(XmlSchemaMaxInclusiveFacet facet, String value, XmlSimpleValueValidationContext validationContext, XmlSchemaPrimitiveType primitiveType) throws XmlSimpleValueException {
Comparator<String> comparator = primitiveType.getValueComparator();
if ( comparator != null ) {
int result = comparator.compare( value, facet.getValue() );
if ( result > 0 ) {
throw new XmlSimpleValueException( "value must be no greater than " + facet.getValue() );
}
}
}
} );
_validators.put( XmlSchemaMaxLengthFacet.class, new XmlSchemaFacetValidator<XmlSchemaMaxLengthFacet>() {
@Override
protected void validateInternal(XmlSchemaMaxLengthFacet facet, String value, XmlSimpleValueValidationContext validationContext, XmlSchemaPrimitiveType primitiveType) throws XmlSimpleValueException {
int length = Integer.parseInt( facet.getValue() );
Integer actualLength = getActualLength( "maxLength", value, validationContext, primitiveType );
if ( actualLength != null && actualLength > length ) {
throw new XmlSimpleValueException( "length " + actualLength + " > " + length );
}
}
} );
_validators.put( XmlSchemaMinLengthFacet.class, new XmlSchemaFacetValidator<XmlSchemaMinLengthFacet>() {
@Override
protected void validateInternal(XmlSchemaMinLengthFacet facet, String value, XmlSimpleValueValidationContext validationContext, XmlSchemaPrimitiveType primitiveType) throws XmlSimpleValueException {
int length = Integer.parseInt( facet.getValue() );
Integer actualLength = getActualLength( "minLength", value, validationContext, primitiveType );
if ( actualLength != null && actualLength < length ) {
throw new XmlSimpleValueException( "length " + actualLength + " < " + length );
}
}
} );
_validators.put( XmlSchemaMinExclusiveFacet.class, new XmlSchemaFacetValidator<XmlSchemaMinExclusiveFacet>() {
@Override
protected void validateInternal(XmlSchemaMinExclusiveFacet facet, String value, XmlSimpleValueValidationContext validationContext, XmlSchemaPrimitiveType primitiveType) throws XmlSimpleValueException {
Comparator<String> comparator = primitiveType.getValueComparator();
if ( comparator != null ) {
int result = comparator.compare( value, facet.getValue() );
if ( result <= 0 ) {
throw new XmlSimpleValueException( "value must be greater than " + facet.getValue() );
}
}
}
} );
_validators.put( XmlSchemaMinInclusiveFacet.class, new XmlSchemaFacetValidator<XmlSchemaMinInclusiveFacet>() {
@Override
protected void validateInternal(XmlSchemaMinInclusiveFacet facet, String value, XmlSimpleValueValidationContext validationContext, XmlSchemaPrimitiveType primitiveType) throws XmlSimpleValueException {
Comparator<String> comparator = primitiveType.getValueComparator();
if ( comparator != null ) {
int result = comparator.compare( value, facet.getValue() );
if ( result < 0 ) {
throw new XmlSimpleValueException( "value must be no less than " + facet.getValue() );
}
}
}
} );
_validators.put( XmlSchemaTotalDigitsFacet.class, new XmlSchemaFacetValidator<XmlSchemaTotalDigitsFacet>() {
@Override
protected void validateInternal(XmlSchemaTotalDigitsFacet facet, String value, XmlSimpleValueValidationContext validationContext, XmlSchemaPrimitiveType primitiveType) throws XmlSimpleValueException {
// TODO dlank - disabling this impl since it's wrong. The spec doesn't restrict total digits in the output string,
// rather it restricts total *significant* digits. In the case of a precisionDecimal, trailing zeroes are significant,
// in a regular decimal they are not. totalDigits also does not restrict NaN or +/-INF values. precisionDecimal is new
// to the spec and might be removed I believe, so we might not need to support that case. In any case, the whole point
// of these validators is to give the user early feedback, rather than to strictly validate anything. For example, we
// don't restrict lengths of xsd:list values at all, since it would prevent people from incrementally filling lists
// in Gosu, so it might be ok to leave this disabled permanently.
// if ( primitiveType != XmlSchemaPrimitiveType.DECIMAL ) {
// throwDueToWrongPrimitiveType( "totalDigits", primitiveType );
// }
// int digits = Integer.parseInt( value );
// int actualDigits = 0;
// for ( int i = 0; i < value.length(); i++ ) {
// if ( Character.isDigit( value.charAt( i ) ) ) {
// actualDigits++;
// }
// }
// if ( actualDigits > digits ) {
// throw new XmlSimpleValueException( "number of digits " + actualDigits + " > " + digits );
// }
}
} );
_validators.put( XmlSchemaWhiteSpaceFacet.class, new XmlSchemaFacetValidator<XmlSchemaWhiteSpaceFacet>() {
@Override
protected void validateInternal( XmlSchemaWhiteSpaceFacet facet, String value, XmlSimpleValueValidationContext validationContext, XmlSchemaPrimitiveType primitiveType) throws XmlSimpleValueException {
// handled elsewhere
}
} );
}
private static Integer getActualLength( String facetName, String value, XmlSimpleValueValidationContext validationContext, XmlSchemaPrimitiveType primitiveType ) {
Integer actualLength;
switch ( primitiveType ) {
case STRING:
case ANYURI: {
actualLength = value.length();
break;
}
case HEXBINARY: {
byte[] bytes = validationContext.getByteArray( primitiveType, value );
actualLength = bytes.length;
break;
}
case LIST: {
// don't validate length, so users can incrementally add new members without issues
actualLength = null;
break;
}
default: {
actualLength = 0;
throwDueToWrongPrimitiveType( facetName, primitiveType );
break;
}
}
return actualLength;
}
private static void throwDueToWrongPrimitiveType( String facetName, XmlSchemaPrimitiveType primitiveType ) throws XmlSimpleValueException {
throw new XmlSimpleValueException( "Found " + facetName + " constraint on simple type extending primitive datatype " + primitiveType.getQName() );
}
public static void validate(XmlSchemaFacet facet, String value, XmlSimpleValueValidationContext validationContext, XmlSchemaPrimitiveType primitiveType) throws XmlSimpleValueException {
//noinspection unchecked
getValidator( facet.getClass() ).validateInternal( facet, value, validationContext, primitiveType );
}
protected abstract void validateInternal(T facet, String value, XmlSimpleValueValidationContext validationContext, XmlSchemaPrimitiveType primitiveType) throws XmlSimpleValueException;
private static XmlSchemaFacetValidator getValidator( Class clazz ) {
return _validators.get( clazz );
}
}