http://git-wip-us.apache.org/repos/asf/bval/blob/a43c0b0c/bval-jsr/src/main/java/org/apache/bval/jsr/xml/ValidationMappingParser.java ---------------------------------------------------------------------- diff --git a/bval-jsr/src/main/java/org/apache/bval/jsr/xml/ValidationMappingParser.java b/bval-jsr/src/main/java/org/apache/bval/jsr/xml/ValidationMappingParser.java index b260a9e..21248d6 100644 --- a/bval-jsr/src/main/java/org/apache/bval/jsr/xml/ValidationMappingParser.java +++ b/bval-jsr/src/main/java/org/apache/bval/jsr/xml/ValidationMappingParser.java @@ -18,730 +18,122 @@ package org.apache.bval.jsr.xml; import java.io.IOException; import java.io.InputStream; -import java.io.Serializable; import java.lang.annotation.Annotation; -import java.lang.reflect.Array; -import java.lang.reflect.Constructor; -import java.lang.reflect.Field; -import java.lang.reflect.Member; -import java.lang.reflect.Method; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collection; -import java.util.Collections; -import java.util.EnumSet; -import java.util.HashSet; import java.util.List; import java.util.Set; +import java.util.stream.Stream; import javax.validation.Constraint; import javax.validation.ConstraintValidator; -import javax.validation.Payload; import javax.validation.ValidationException; -import javax.xml.bind.JAXBContext; -import javax.xml.bind.JAXBElement; -import javax.xml.bind.JAXBException; -import javax.xml.bind.Unmarshaller; -import javax.xml.transform.stream.StreamSource; -import javax.xml.validation.Schema; import org.apache.bval.jsr.ApacheValidatorFactory; -import org.apache.bval.jsr.ConstraintAnnotationAttributes; -import org.apache.bval.jsr.util.IOs; -import org.apache.bval.util.FieldAccess; -import org.apache.bval.util.MethodAccess; -import org.apache.bval.util.ObjectUtils; -import org.apache.bval.util.StringUtils; +import org.apache.bval.jsr.metadata.XmlBuilder; +import org.apache.bval.jsr.util.Exceptions; +import org.apache.bval.util.Validate; import org.apache.bval.util.reflection.Reflection; -import org.apache.commons.weaver.privilizer.Privileged; import org.apache.commons.weaver.privilizer.Privilizing; import org.apache.commons.weaver.privilizer.Privilizing.CallTo; +import org.xml.sax.InputSource; /** - * Uses JAXB to parse constraints.xml based on validation-mapping-1.0.xsd.<br> + * Uses JAXB to parse constraints.xml based on the validation-mapping XML schema. */ @Privilizing(@CallTo(Reflection.class)) public class ValidationMappingParser { - private static final String VALIDATION_MAPPING_XSD = "META-INF/validation-mapping-1.1.xsd"; + private static final SchemaManager SCHEMA_MANAGER = new SchemaManager.Builder() + .add(null, "http://jboss.org/xml/ns/javax/validation/mapping", "META-INF/validation-mapping-1.0.xsd") + .add(XmlBuilder.Version.v11.getId(), "http://jboss.org/xml/ns/javax/validation/mapping", + "META-INF/validation-mapping-1.1.xsd") + .add(XmlBuilder.Version.v20.getId(), "http://xmlns.jcp.org/xml/ns/javax/validation/mapping", + "META-INF/validation-mapping-2.0.xsd") + .build(); - private static final Set<ConstraintAnnotationAttributes> RESERVED_PARAMS = Collections - .unmodifiableSet(EnumSet.of(ConstraintAnnotationAttributes.GROUPS, ConstraintAnnotationAttributes.MESSAGE, - ConstraintAnnotationAttributes.PAYLOAD, ConstraintAnnotationAttributes.VALIDATION_APPLIES_TO)); - - private final Set<Class<?>> processedClasses; private final ApacheValidatorFactory factory; public ValidationMappingParser(ApacheValidatorFactory factory) { - this.factory = factory; - this.processedClasses = new HashSet<Class<?>>(); + this.factory = Validate.notNull(factory, "factory"); } /** * Parse files with constraint mappings and collect information in the factory. - * - * @param xmlStreams - one or more contraints.xml file streams to parse + * + * @param xmlStreams + * - one or more contraints.xml file streams to parse */ public void processMappingConfig(Set<InputStream> xmlStreams) throws ValidationException { for (final InputStream xmlStream : xmlStreams) { - ConstraintMappingsType mapping = parseXmlMappings(xmlStream); - - final String defaultPackage = mapping.getDefaultPackage(); - processConstraintDefinitions(mapping.getConstraintDefinition(), defaultPackage); - for (final BeanType bean : mapping.getBean()) { - Class<?> beanClass = loadClass(bean.getClazz(), defaultPackage); - if (!processedClasses.add(beanClass)) { - // spec: A given class must not be described more than once amongst all - // the XML mapping descriptors. - throw new ValidationException(beanClass.getName() + " has already be configured in xml."); - } - - boolean ignoreAnnotations = bean.getIgnoreAnnotations() == null ? true : bean.getIgnoreAnnotations(); - factory.getAnnotationIgnores().setDefaultIgnoreAnnotation(beanClass, ignoreAnnotations); - processClassLevel(bean.getClassType(), beanClass, defaultPackage); - processConstructorLevel(bean.getConstructor(), beanClass, defaultPackage, ignoreAnnotations); - processFieldLevel(bean.getField(), beanClass, defaultPackage, ignoreAnnotations); - final Collection<String> potentialMethodName = - processPropertyLevel(bean.getGetter(), beanClass, defaultPackage, ignoreAnnotations); - processMethodLevel(bean.getMethod(), beanClass, defaultPackage, ignoreAnnotations, potentialMethodName); - processedClasses.add(beanClass); - } + final ConstraintMappingsType mapping = parseXmlMappings(xmlStream); + processConstraintDefinitions(mapping.getConstraintDefinition(), mapping.getDefaultPackage()); + new XmlBuilder(mapping).forBeans().forEach(factory.getMetadataBuilders()::registerCustomBuilder); } } - /** @param in XML stream to parse using the validation-mapping-1.0.xsd */ + /** + * @param in + * XML stream to parse using the validation-mapping-1.0.xsd + */ private ConstraintMappingsType parseXmlMappings(final InputStream in) { - ConstraintMappingsType mappings; try { - final JAXBContext jc = JAXBContext.newInstance(ConstraintMappingsType.class); - final Unmarshaller unmarshaller = jc.createUnmarshaller(); - unmarshaller.setSchema(getSchema()); - final StreamSource stream = new StreamSource(in); - final JAXBElement<ConstraintMappingsType> root = - unmarshaller.unmarshal(stream, ConstraintMappingsType.class); - mappings = root.getValue(); - } catch (final JAXBException e) { + return SCHEMA_MANAGER.unmarshal(new InputSource(in), ConstraintMappingsType.class); + } catch (Exception e) { throw new ValidationException("Failed to parse XML deployment descriptor file.", e); } finally { - IOs.closeQuietly(in); try { in.reset(); // can be read several times + we ensured it was re-readable in addMapping() } catch (final IOException e) { // no-op } } - return mappings; - } - - /** @return validation-mapping-1.0.xsd based schema */ - private Schema getSchema() { - return ValidationParser.getSchema(VALIDATION_MAPPING_XSD); - } - - private void processClassLevel(ClassType classType, Class<?> beanClass, String defaultPackage) { - if (classType == null) { - return; - } - - // ignore annotation - if (classType.getIgnoreAnnotations() != null) { - factory.getAnnotationIgnores().setIgnoreAnnotationsOnClass(beanClass, classType.getIgnoreAnnotations()); - } - - // group sequence - Class<?>[] groupSequence = createGroupSequence(classType.getGroupSequence(), defaultPackage); - if (groupSequence != null) { - factory.addDefaultSequence(beanClass, groupSequence); - } - - // constraints - for (ConstraintType constraint : classType.getConstraint()) { - MetaConstraint<?, ?> metaConstraint = createConstraint(constraint, beanClass, null, defaultPackage); - factory.addMetaConstraint(beanClass, metaConstraint); - } - } - - @SuppressWarnings("unchecked") - private <A extends Annotation, T> MetaConstraint<?, ?> createConstraint(final ConstraintType constraint, - final Class<T> beanClass, final Member member, final String defaultPackage) { - - final Class<A> annotationClass = (Class<A>) loadClass(constraint.getAnnotation(), defaultPackage); - final AnnotationProxyBuilder<A> annoBuilder = new AnnotationProxyBuilder<A>(annotationClass); - - if (constraint.getMessage() != null) { - annoBuilder.setMessage(constraint.getMessage()); - } - annoBuilder.setGroups(getGroups(constraint.getGroups(), defaultPackage)); - annoBuilder.setPayload(getPayload(constraint.getPayload(), defaultPackage)); - - for (final ElementType elementType : constraint.getElement()) { - final String name = elementType.getName(); - checkValidName(name); - - final Class<?> returnType = getAnnotationParameterType(annotationClass, name); - final Object elementValue = getElementValue(elementType, returnType, defaultPackage); - annoBuilder.putValue(name, elementValue); - } - return new MetaConstraint<T, A>(beanClass, member, annoBuilder.createAnnotation()); - } - - private void checkValidName(String name) { - for (ConstraintAnnotationAttributes attr : RESERVED_PARAMS) { - if (attr.getAttributeName().equals(name)) { - throw new ValidationException(name + " is a reserved parameter name."); - } - } - } - - private <A extends Annotation> Class<?> getAnnotationParameterType(final Class<A> annotationClass, - final String name) { - final Method m = Reflection.getPublicMethod(annotationClass, name); - if (m == null) { - throw new ValidationException( - "Annotation of type " + annotationClass.getName() + " does not contain a parameter " + name + "."); - } - return m.getReturnType(); - } - - private Object getElementValue(ElementType elementType, Class<?> returnType, String defaultPackage) { - removeEmptyContentElements(elementType); - - boolean isArray = returnType.isArray(); - if (!isArray) { - if (elementType.getContent().size() != 1) { - throw new ValidationException("Attempt to specify an array where single value is expected."); - } - return getSingleValue(elementType.getContent().get(0), returnType, defaultPackage); - } - List<Object> values = new ArrayList<Object>(); - for (Serializable s : elementType.getContent()) { - values.add(getSingleValue(s, returnType.getComponentType(), defaultPackage)); - } - return values.toArray((Object[]) Array.newInstance(returnType.getComponentType(), values.size())); - } - - private void removeEmptyContentElements(ElementType elementType) { - List<Serializable> contentToDelete = new ArrayList<Serializable>(); - for (Serializable content : elementType.getContent()) { - if (content instanceof String && ((String) content).matches("[\\n ].*")) { - contentToDelete.add(content); - } - } - elementType.getContent().removeAll(contentToDelete); - } - - @SuppressWarnings("unchecked") - private Object getSingleValue(Serializable serializable, Class<?> returnType, String defaultPackage) { - if (serializable instanceof String) { - String value = (String) serializable; - return convertToResultType(returnType, value, defaultPackage); - } - if (serializable instanceof JAXBElement<?>) { - JAXBElement<?> elem = (JAXBElement<?>) serializable; - if (String.class.equals(elem.getDeclaredType())) { - String value = (String) elem.getValue(); - return convertToResultType(returnType, value, defaultPackage); - } - if (AnnotationType.class.equals(elem.getDeclaredType())) { - AnnotationType annotationType = (AnnotationType) elem.getValue(); - try { - Class<? extends Annotation> annotationClass = (Class<? extends Annotation>) returnType; - return createAnnotation(annotationType, annotationClass, defaultPackage); - } catch (ClassCastException e) { - throw new ValidationException("Unexpected parameter value"); - } - } - } - throw new ValidationException("Unexpected parameter value"); - } - - private Object convertToResultType(Class<?> returnType, String value, String defaultPackage) { - /** - * Class is represented by the fully qualified class name of the class. - * spec: Note that if the raw string is unqualified, - * default package is taken into account. - */ - if (returnType.equals(String.class)) { - return value; - } - if (returnType.equals(Class.class)) { - ClassLoader cl = Reflection.getClassLoader(ValidationMappingParser.class); - try { - return Reflection.toClass(toQualifiedClassName(value, defaultPackage), cl); - } catch (Exception e) { - throw new ValidationException(e); - } - } - if (returnType.isEnum()) { - try { - @SuppressWarnings({ "rawtypes", "unchecked" }) - final Enum e = Enum.valueOf(returnType.asSubclass(Enum.class), value); - return e; - } catch (IllegalArgumentException e) { - throw new ValidationException(e); - } - } - if (Byte.class.equals(returnType) || byte.class.equals(returnType)) { // spec mandates it - return Byte.parseByte(value); - } - if (Short.class.equals(returnType) || short.class.equals(returnType)) { - return Short.parseShort(value); - } - if (Integer.class.equals(returnType) || int.class.equals(returnType)) { - return Integer.parseInt(value); - } - if (Long.class.equals(returnType) || long.class.equals(returnType)) { - return Long.parseLong(value); - } - if (Float.class.equals(returnType) || float.class.equals(returnType)) { - return Float.parseFloat(value); - } - if (Double.class.equals(returnType) || double.class.equals(returnType)) { - return Double.parseDouble(value); - } - if (Boolean.class.equals(returnType) || boolean.class.equals(returnType)) { - return Boolean.parseBoolean(value); - } - if (Character.class.equals(returnType) || char.class.equals(returnType)) { - if (value.length() > 1) { - throw new IllegalArgumentException("a char has a length of 1"); - } - return value.charAt(0); - } - throw new ValidationException(String.format("Unknown annotation value type %s", returnType.getName())); - } - - private <A extends Annotation> Annotation createAnnotation(AnnotationType annotationType, Class<A> returnType, - String defaultPackage) { - AnnotationProxyBuilder<A> metaAnnotation = new AnnotationProxyBuilder<A>(returnType); - for (ElementType elementType : annotationType.getElement()) { - String name = elementType.getName(); - Class<?> parameterType = getAnnotationParameterType(returnType, name); - Object elementValue = getElementValue(elementType, parameterType, defaultPackage); - metaAnnotation.putValue(name, elementValue); - } - return metaAnnotation.createAnnotation(); - } - - private Class<?>[] getGroups(GroupsType groupsType, String defaultPackage) { - if (groupsType == null) { - return ObjectUtils.EMPTY_CLASS_ARRAY; - } - - List<Class<?>> groupList = new ArrayList<Class<?>>(); - for (String groupClass : groupsType.getValue()) { - groupList.add(loadClass(groupClass, defaultPackage)); - } - return groupList.toArray(new Class[groupList.size()]); - } - - @SuppressWarnings("unchecked") - private Class<? extends Payload>[] getPayload(PayloadType payloadType, String defaultPackage) { - if (payloadType == null) { - return new Class[] {}; - } - - List<Class<? extends Payload>> payloadList = new ArrayList<Class<? extends Payload>>(); - for (String groupClass : payloadType.getValue()) { - Class<?> payload = loadClass(groupClass, defaultPackage); - if (!Payload.class.isAssignableFrom(payload)) { - throw new ValidationException( - "Specified payload class " + payload.getName() + " does not implement javax.validation.Payload"); - } - payloadList.add((Class<? extends Payload>) payload); - } - return payloadList.toArray(new Class[payloadList.size()]); - } - - private Class<?>[] createGroupSequence(GroupSequenceType groupSequenceType, String defaultPackage) { - if (groupSequenceType != null) { - Class<?>[] groupSequence = new Class<?>[groupSequenceType.getValue().size()]; - int i = 0; - for (String groupName : groupSequenceType.getValue()) { - Class<?> group = loadClass(groupName, defaultPackage); - groupSequence[i++] = group; - } - return groupSequence; - } - return null; - } - - private <A> void processMethodLevel(final List<MethodType> methods, final Class<A> beanClass, - final String defaultPackage, final boolean parentIgnoreAnn, final Collection<String> getters) { - final List<String> methodNames = new ArrayList<String>(); - for (final MethodType methodType : methods) { - final String methodName = methodType.getName(); - if (methodNames.contains(methodName) || getters.contains(methodName)) { - throw new ValidationException( - methodName + " is defined more than once in mapping xml for bean " + beanClass.getName()); - } - methodNames.add(methodName); - - final Method method = - Reflection.getDeclaredMethod(beanClass, methodName, toTypes(methodType.getParameter(), defaultPackage)); - if (method == null) { - throw new ValidationException(beanClass.getName() + " does not contain the method " + methodName); - } - - // ignore annotations - final boolean ignoreMethodAnnotation = - methodType.getIgnoreAnnotations() == null ? parentIgnoreAnn : methodType.getIgnoreAnnotations(); - factory.getAnnotationIgnores().setIgnoreAnnotationsOnMember(method, ignoreMethodAnnotation); - - final boolean ignoreAnn; - if (methodType.getIgnoreAnnotations() == null) { - ignoreAnn = parentIgnoreAnn; - } else { - ignoreAnn = methodType.getIgnoreAnnotations(); - } - - // constraints - int i = 0; - for (final ParameterType p : methodType.getParameter()) { - for (final ConstraintType constraintType : p.getConstraint()) { - final MetaConstraint<?, ?> constraint = - createConstraint(constraintType, beanClass, method, defaultPackage); - constraint.setIndex(i); - factory.addMetaConstraint(beanClass, constraint); - } - if (p.getValid() != null) { - final MetaConstraint<?, ?> constraint = new MetaConstraint<A, Annotation>(beanClass, method, - AnnotationProxyBuilder.ValidAnnotation.INSTANCE); - constraint.setIndex(i); - factory.addMetaConstraint(beanClass, constraint); - } - - if (p.getConvertGroup() != null) { - for (final GroupConversionType groupConversion : p.getConvertGroup()) { - final Class<?> from = loadClass(groupConversion.getFrom(), defaultPackage); - final Class<?> to = loadClass(groupConversion.getTo(), defaultPackage); - final MetaConstraint<?, ?> constraint = new MetaConstraint<A, Annotation>(beanClass, method, - new AnnotationProxyBuilder.ConvertGroupAnnotation(from, to)); - constraint.setIndex(i); - factory.addMetaConstraint(beanClass, constraint); - } - } - - boolean ignoreParametersAnnotation = - p.getIgnoreAnnotations() == null ? ignoreMethodAnnotation : p.getIgnoreAnnotations(); - factory.getAnnotationIgnores().setIgnoreAnnotationsOnParameter(method, i, ignoreParametersAnnotation); - - i++; - } - - final ReturnValueType returnValue = methodType.getReturnValue(); - if (returnValue != null) { - for (final ConstraintType constraintType : returnValue.getConstraint()) { - final MetaConstraint<?, ?> constraint = - createConstraint(constraintType, beanClass, method, defaultPackage); - factory.addMetaConstraint(beanClass, constraint); - } - if (returnValue.getValid() != null) { - final MetaConstraint<?, ?> constraint = new MetaConstraint<A, Annotation>(beanClass, method, - AnnotationProxyBuilder.ValidAnnotation.INSTANCE); - factory.addMetaConstraint(beanClass, constraint); - } - - if (returnValue.getConvertGroup() != null) { - for (final GroupConversionType groupConversion : returnValue.getConvertGroup()) { - final Class<?> from = loadClass(groupConversion.getFrom(), defaultPackage); - final Class<?> to = loadClass(groupConversion.getTo(), defaultPackage); - final MetaConstraint<?, ?> constraint = new MetaConstraint<A, Annotation>(beanClass, method, - new AnnotationProxyBuilder.ConvertGroupAnnotation(from, to)); - factory.addMetaConstraint(beanClass, constraint); - } - } - factory.getAnnotationIgnores().setIgnoreAnnotationOnReturn(method, - returnValue.getIgnoreAnnotations() == null ? ignoreAnn : returnValue.getIgnoreAnnotations()); - } - - final CrossParameterType crossParameter = methodType.getCrossParameter(); - if (crossParameter != null) { - for (final ConstraintType constraintType : crossParameter.getConstraint()) { - final MetaConstraint<?, ?> constraint = - createConstraint(constraintType, beanClass, method, defaultPackage); - factory.addMetaConstraint(beanClass, constraint); - } - factory.getAnnotationIgnores().setIgnoreAnnotationOnCrossParameter(method, - crossParameter.getIgnoreAnnotations() != null ? crossParameter.getIgnoreAnnotations() : ignoreAnn); - } - } - } - - private <A> void processConstructorLevel(final List<ConstructorType> constructors, final Class<A> beanClass, - final String defaultPackage, final boolean parentIgnore) { - for (final ConstructorType constructorType : constructors) { - final Constructor<?> constructor = - Reflection.getDeclaredConstructor(beanClass, toTypes(constructorType.getParameter(), defaultPackage)); - if (constructor == null) { - throw new ValidationException( - beanClass.getName() + " does not contain the constructor " + constructorType); - } - - // ignore annotations - final boolean ignoreMethodAnnotation = - constructorType.getIgnoreAnnotations() == null ? parentIgnore : constructorType.getIgnoreAnnotations(); - factory.getAnnotationIgnores().setIgnoreAnnotationsOnMember(constructor, ignoreMethodAnnotation); - - final boolean ignoreAnn; - if (constructorType.getIgnoreAnnotations() == null) { - ignoreAnn = parentIgnore; - } else { - ignoreAnn = constructorType.getIgnoreAnnotations(); - } - - // constraints - int i = 0; - for (final ParameterType p : constructorType.getParameter()) { - for (final ConstraintType constraintType : p.getConstraint()) { - final MetaConstraint<?, ?> constraint = - createConstraint(constraintType, beanClass, constructor, defaultPackage); - constraint.setIndex(i); - factory.addMetaConstraint(beanClass, constraint); - } - if (p.getValid() != null) { - final MetaConstraint<?, ?> constraint = new MetaConstraint<A, Annotation>(beanClass, constructor, - AnnotationProxyBuilder.ValidAnnotation.INSTANCE); - constraint.setIndex(i); - factory.addMetaConstraint(beanClass, constraint); - } - - if (p.getConvertGroup() != null) { - for (final GroupConversionType groupConversion : p.getConvertGroup()) { - final Class<?> from = loadClass(groupConversion.getFrom(), defaultPackage); - final Class<?> to = loadClass(groupConversion.getTo(), defaultPackage); - final MetaConstraint<?, ?> constraint = new MetaConstraint<A, Annotation>(beanClass, - constructor, new AnnotationProxyBuilder.ConvertGroupAnnotation(from, to)); - constraint.setIndex(i); - factory.addMetaConstraint(beanClass, constraint); - } - } - - boolean ignoreParametersAnnotation = - p.getIgnoreAnnotations() == null ? ignoreMethodAnnotation : p.getIgnoreAnnotations(); - if (ignoreParametersAnnotation || (ignoreMethodAnnotation && p.getIgnoreAnnotations() == null)) { - // TODO what ? - } - factory.getAnnotationIgnores().setIgnoreAnnotationsOnParameter(constructor, i, - p.getIgnoreAnnotations() != null ? p.getIgnoreAnnotations() : ignoreAnn); - - i++; - } - - final ReturnValueType returnValue = constructorType.getReturnValue(); - if (returnValue != null) { - for (final ConstraintType constraintType : returnValue.getConstraint()) { - final MetaConstraint<?, ?> constraint = - createConstraint(constraintType, beanClass, constructor, defaultPackage); - constraint.setIndex(-1); - factory.addMetaConstraint(beanClass, constraint); - } - if (returnValue.getValid() != null) { - final MetaConstraint<?, ?> constraint = new MetaConstraint<A, Annotation>(beanClass, constructor, - AnnotationProxyBuilder.ValidAnnotation.INSTANCE); - constraint.setIndex(-1); - factory.addMetaConstraint(beanClass, constraint); - } - - if (returnValue.getConvertGroup() != null) { - for (final GroupConversionType groupConversion : returnValue.getConvertGroup()) { - final Class<?> from = loadClass(groupConversion.getFrom(), defaultPackage); - final Class<?> to = loadClass(groupConversion.getTo(), defaultPackage); - final MetaConstraint<?, ?> constraint = new MetaConstraint<A, Annotation>(beanClass, - constructor, new AnnotationProxyBuilder.ConvertGroupAnnotation(from, to)); - constraint.setIndex(-1); - factory.addMetaConstraint(beanClass, constraint); - } - } - factory.getAnnotationIgnores().setIgnoreAnnotationOnReturn(constructor, - returnValue.getIgnoreAnnotations() != null ? returnValue.getIgnoreAnnotations() : ignoreAnn); - } - - final CrossParameterType crossParameter = constructorType.getCrossParameter(); - if (crossParameter != null) { - for (final ConstraintType constraintType : crossParameter.getConstraint()) { - final MetaConstraint<?, ?> constraint = - createConstraint(constraintType, beanClass, constructor, defaultPackage); - factory.addMetaConstraint(beanClass, constraint); - } - factory.getAnnotationIgnores().setIgnoreAnnotationOnCrossParameter(constructor, - crossParameter.getIgnoreAnnotations() != null ? crossParameter.getIgnoreAnnotations() : ignoreAnn); - } - } - } - - private Class<?>[] toTypes(final List<ParameterType> parameter, final String defaultPck) { - if (parameter == null) { - return null; - } - final Class<?>[] types = new Class<?>[parameter.size()]; - int i = 0; - for (final ParameterType type : parameter) { - types[i++] = loadClass(type.getType(), defaultPck); - } - return types; - } - - private <A> void processFieldLevel(List<FieldType> fields, Class<A> beanClass, String defaultPackage, - boolean ignoreAnnotations) { - final List<String> fieldNames = new ArrayList<String>(); - for (FieldType fieldType : fields) { - String fieldName = fieldType.getName(); - if (fieldNames.contains(fieldName)) { - throw new ValidationException( - fieldName + " is defined more than once in mapping xml for bean " + beanClass.getName()); - } - fieldNames.add(fieldName); - - final Field field = Reflection.getDeclaredField(beanClass, fieldName); - if (field == null) { - throw new ValidationException(beanClass.getName() + " does not contain the fieldType " + fieldName); - } - - // ignore annotations - final boolean ignoreFieldAnnotation = - fieldType.getIgnoreAnnotations() == null ? ignoreAnnotations : fieldType.getIgnoreAnnotations(); - factory.getAnnotationIgnores().setIgnoreAnnotationsOnMember(field, ignoreFieldAnnotation); - - // valid - if (fieldType.getValid() != null) { - factory.addValid(beanClass, new FieldAccess(field)); - } - - for (final GroupConversionType conversion : fieldType.getConvertGroup()) { - final Class<?> from = loadClass(conversion.getFrom(), defaultPackage); - final Class<?> to = loadClass(conversion.getTo(), defaultPackage); - final MetaConstraint<?, ?> constraint = new MetaConstraint<A, Annotation>(beanClass, field, - new AnnotationProxyBuilder.ConvertGroupAnnotation(from, to)); - factory.addMetaConstraint(beanClass, constraint); - } - - // constraints - for (ConstraintType constraintType : fieldType.getConstraint()) { - MetaConstraint<?, ?> constraint = createConstraint(constraintType, beanClass, field, defaultPackage); - factory.addMetaConstraint(beanClass, constraint); - } - } - } - - private <A> Collection<String> processPropertyLevel(List<GetterType> getters, Class<A> beanClass, - String defaultPackage, boolean ignoreAnnotatino) { - List<String> getterNames = new ArrayList<String>(); - for (GetterType getterType : getters) { - final String getterName = getterType.getName(); - final String methodName = "get" + StringUtils.capitalize(getterType.getName()); - if (getterNames.contains(methodName)) { - throw new ValidationException( - getterName + " is defined more than once in mapping xml for bean " + beanClass.getName()); - } - getterNames.add(methodName); - - final Method method = getGetter(beanClass, getterName); - if (method == null) { - throw new ValidationException(beanClass.getName() + " does not contain the property " + getterName); - } - - // ignore annotations - final boolean ignoreGetterAnnotation = - getterType.getIgnoreAnnotations() == null ? ignoreAnnotatino : getterType.getIgnoreAnnotations(); - factory.getAnnotationIgnores().setIgnoreAnnotationsOnMember(method, ignoreGetterAnnotation); - - // valid - if (getterType.getValid() != null) { - factory.addValid(beanClass, new MethodAccess(getterName, method)); - } - - // ConvertGroup - for (final GroupConversionType conversion : getterType.getConvertGroup()) { - final Class<?> from = loadClass(conversion.getFrom(), defaultPackage); - final Class<?> to = loadClass(conversion.getTo(), defaultPackage); - final MetaConstraint<?, ?> constraint = new MetaConstraint<A, Annotation>(beanClass, method, - new AnnotationProxyBuilder.ConvertGroupAnnotation(from, to)); - factory.addMetaConstraint(beanClass, constraint); - } - - // constraints - for (ConstraintType constraintType : getterType.getConstraint()) { - MetaConstraint<?, ?> metaConstraint = - createConstraint(constraintType, beanClass, method, defaultPackage); - factory.addMetaConstraint(beanClass, metaConstraint); - } - } - - return getterNames; } @SuppressWarnings("unchecked") private void processConstraintDefinitions(List<ConstraintDefinitionType> constraintDefinitionList, String defaultPackage) { for (ConstraintDefinitionType constraintDefinition : constraintDefinitionList) { - String annotationClassName = constraintDefinition.getAnnotation(); + final String annotationClassName = constraintDefinition.getAnnotation(); - Class<?> clazz = loadClass(annotationClassName, defaultPackage); - if (!clazz.isAnnotation()) { - throw new ValidationException(annotationClassName + " is not an annotation"); - } - Class<? extends Annotation> annotationClass = (Class<? extends Annotation>) clazz; + final Class<?> clazz = loadClass(annotationClassName, defaultPackage); + Exceptions.raiseUnless(clazz.isAnnotation(), ValidationException::new, "%s is not an annotation", + annotationClassName); - ValidatedByType validatedByType = constraintDefinition.getValidatedBy(); - List<Class<? extends ConstraintValidator<?, ?>>> classes = - new ArrayList<Class<? extends ConstraintValidator<?, ?>>>(); - /* - If include-existing-validator is set to false, - ConstraintValidator defined on the constraint annotation are ignored. - */ - if (validatedByType.getIncludeExistingValidators() != null - && validatedByType.getIncludeExistingValidators()) { - /* - If set to true, the list of ConstraintValidators described in XML - are concatenated to the list of ConstraintValidator described on the - annotation to form a new array of ConstraintValidator evaluated. - */ - classes.addAll(findConstraintValidatorClasses(annotationClass)); - } - for (String validatorClassName : validatedByType.getValue()) { - Class<? extends ConstraintValidator<?, ?>> validatorClass; - validatorClass = (Class<? extends ConstraintValidator<?, ?>>) loadClass(validatorClassName); + final Class<? extends Annotation> annotationClass = clazz.asSubclass(Annotation.class); - if (!ConstraintValidator.class.isAssignableFrom(validatorClass)) { - throw new ValidationException(validatorClass + " is not a constraint validator class"); - } + Exceptions.raiseIf(factory.getConstraintsCache().containsConstraintValidator(annotationClass), + ValidationException::new, "Constraint validator for %s already configured.", annotationClass); + final ValidatedByType validatedByType = constraintDefinition.getValidatedBy(); + /* + * If include-existing-validator is set to false, ConstraintValidator defined on the constraint annotation + * are ignored. + */ + Stream<Class<? extends ConstraintValidator<?, ?>>> validators = + validatedByType.getValue().stream().map(this::loadClass) + .peek(validatorClass -> Exceptions.raiseUnless( + ConstraintValidator.class.isAssignableFrom(validatorClass), ValidationException::new, + "%s is not a constraint validator class", validatorClass)) + .map(validatorClass -> (Class<? extends ConstraintValidator<?, ?>>) validatorClass + .asSubclass(ConstraintValidator.class)); + + if (Boolean.TRUE.equals(validatedByType.getIncludeExistingValidators())) { /* - Annotation based ConstraintValidator come before XML based - ConstraintValidator in the array. The new list is returned - by ConstraintDescriptor.getConstraintValidatorClasses(). + * If set to true, the list of ConstraintValidators described in XML are concatenated to the list of + * ConstraintValidator described on the annotation to form a new array of ConstraintValidator evaluated. */ - if (!classes.contains(validatorClass)) - classes.add(validatorClass); - } - if (factory.getConstraintsCache().containsConstraintValidator(annotationClass)) { - throw new ValidationException( - "Constraint validator for " + annotationClass.getName() + " already configured."); - } else { - factory.getConstraintsCache().putConstraintValidator(annotationClass, - classes.toArray(new Class[classes.size()])); + validators = Stream.concat(Stream.of(findConstraintValidatorClasses(annotationClass)), validators); } + factory.getConstraintsCache().putConstraintValidator(annotationClass, + validators.distinct().toArray(Class[]::new)); } } - private List<Class<? extends ConstraintValidator<? extends Annotation, ?>>> findConstraintValidatorClasses( + private Class<? extends ConstraintValidator<? extends Annotation, ?>>[] findConstraintValidatorClasses( Class<? extends Annotation> annotationType) { - List<Class<? extends ConstraintValidator<? extends Annotation, ?>>> classes = - new ArrayList<Class<? extends ConstraintValidator<? extends Annotation, ?>>>(); - Class<? extends ConstraintValidator<?, ?>>[] validator = + final Class<? extends ConstraintValidator<?, ?>>[] validator = factory.getDefaultConstraints().getValidatorClasses(annotationType); - if (validator == null) { - /* Collections.addAll() would be more straightforward here, but there is an Oracle compiler bug of some sort - * that precludes this: - */ - Class<? extends ConstraintValidator<?, ?>>[] validatedBy = - annotationType.getAnnotation(Constraint.class).validatedBy(); - classes.addAll(Arrays.asList(validatedBy)); - } else { - Collections.addAll(classes, validator); - } - return classes; + + return validator == null ? annotationType.getAnnotation(Constraint.class).validatedBy() : validator; } private Class<?> loadClass(String className, String defaultPackage) { @@ -751,42 +143,27 @@ public class ValidationMappingParser { private String toQualifiedClassName(String className, String defaultPackage) { if (!isQualifiedClass(className)) { if (className.startsWith("[L") && className.endsWith(";")) { - className = "[L" + defaultPackage + "." + className.substring(2); + className = "[L" + defaultPackage + '.' + className.substring(2); } else { - className = defaultPackage + "." + className; + className = defaultPackage + '.' + className; } } return className; } private boolean isQualifiedClass(String clazz) { - return clazz.contains("."); - } - - @Privileged - private static Method getGetter(Class<?> clazz, String propertyName) { - try { - final String p = StringUtils.capitalize(propertyName); - try { - return clazz.getMethod("get" + p); - } catch (NoSuchMethodException e) { - return clazz.getMethod("is" + p); - } - } catch (NoSuchMethodException e) { - return null; - } + return clazz.indexOf('.') >= 0; } private Class<?> loadClass(final String className) { ClassLoader loader = Reflection.getClassLoader(ValidationMappingParser.class); - if (loader == null) + if (loader == null) { loader = getClass().getClassLoader(); - + } try { - return Class.forName(className, true, loader); + return Reflection.toClass(className, loader); } catch (ClassNotFoundException ex) { - throw new ValidationException("Unable to load class: " + className, ex); + throw Exceptions.create(ValidationException::new, ex, "Unable to load class: %s", className); } } - }
http://git-wip-us.apache.org/repos/asf/bval/blob/a43c0b0c/bval-jsr/src/main/java/org/apache/bval/jsr/xml/ValidationParser.java ---------------------------------------------------------------------- diff --git a/bval-jsr/src/main/java/org/apache/bval/jsr/xml/ValidationParser.java b/bval-jsr/src/main/java/org/apache/bval/jsr/xml/ValidationParser.java index 7b9b14f..a713162 100644 --- a/bval-jsr/src/main/java/org/apache/bval/jsr/xml/ValidationParser.java +++ b/bval-jsr/src/main/java/org/apache/bval/jsr/xml/ValidationParser.java @@ -22,12 +22,14 @@ import java.io.IOException; import java.io.InputStream; import java.net.URL; import java.util.Collection; -import java.util.Enumeration; +import java.util.Collections; +import java.util.EnumSet; import java.util.HashMap; +import java.util.HashSet; import java.util.List; import java.util.Map; -import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.ConcurrentMap; +import java.util.Optional; +import java.util.Set; import java.util.concurrent.CopyOnWriteArrayList; import java.util.concurrent.CopyOnWriteArraySet; import java.util.logging.Level; @@ -40,59 +42,34 @@ import javax.validation.TraversableResolver; import javax.validation.ValidationException; import javax.validation.executable.ExecutableType; import javax.validation.spi.ValidationProvider; -import javax.xml.XMLConstants; -import javax.xml.bind.JAXBContext; -import javax.xml.bind.JAXBElement; -import javax.xml.bind.JAXBException; -import javax.xml.bind.Unmarshaller; -import javax.xml.transform.stream.StreamSource; -import javax.xml.validation.Schema; -import javax.xml.validation.SchemaFactory; import org.apache.bval.jsr.BootstrapConfigurationImpl; import org.apache.bval.jsr.ConfigurationImpl; -import org.apache.bval.jsr.util.IOs; +import org.apache.bval.jsr.metadata.XmlBuilder; +import org.apache.bval.jsr.util.Exceptions; import org.apache.bval.util.reflection.Reflection; import org.apache.commons.weaver.privilizer.Privileged; import org.apache.commons.weaver.privilizer.Privilizing; import org.apache.commons.weaver.privilizer.Privilizing.CallTo; -import org.xml.sax.SAXException; +import org.xml.sax.InputSource; /** * Description: uses jaxb to parse validation.xml<br/> */ @Privilizing(@CallTo(Reflection.class)) public class ValidationParser { + private static final String DEFAULT_VALIDATION_XML_FILE = "META-INF/validation.xml"; - private static final String VALIDATION_CONFIGURATION_XSD = "META-INF/validation-configuration-1.1.xsd"; private static final Logger log = Logger.getLogger(ValidationParser.class.getName()); - private static final ConcurrentMap<String, Schema> SCHEMA_CACHE = new ConcurrentHashMap<String, Schema>(1); - - private ValidationConfigType xmlConfig; - private BootstrapConfigurationImpl bootstrap; - private Collection<ValidationException> exceptions = new CopyOnWriteArrayList<ValidationException>(); - - private ValidationParser() { - // no-op - } - public void applyConfigWithInstantiation(ConfigurationImpl targetConfig) { - if (xmlConfig == null) { - return; - } - - applyProviderClass(xmlConfig, targetConfig); - applyMessageInterpolator(xmlConfig, targetConfig); - applyTraversableResolver(xmlConfig, targetConfig); - applyConstraintFactory(xmlConfig, targetConfig); - applyParameterNameProvider(xmlConfig, targetConfig); - applyMappingStreams(xmlConfig, targetConfig); - applyProperties(xmlConfig, targetConfig); - } - - public BootstrapConfigurationImpl getBootstrap() { - return bootstrap; - } + private static final SchemaManager SCHEMA_MANAGER = new SchemaManager.Builder() + .add(null, "http://jboss.org/xml/ns/javax/validation/configuration", + "META-INF/validation-configuration-1.0.xsd") + .add(XmlBuilder.Version.v11.getId(), "http://jboss.org/xml/ns/javax/validation/configuration", + "META-INF/validation-configuration-1.1.xsd") + .add(XmlBuilder.Version.v20.getId(), "http://xmlns.jcp.org/xml/ns/javax/validation/configuration", + "META-INF/validation-configuration-2.0.xsd") + .build(); public static String getValidationXmlFile(String file) { if (file == null) { @@ -101,19 +78,30 @@ public class ValidationParser { return file; } - public static ValidationParser processValidationConfig(final String file, final ConfigurationImpl targetConfig, final boolean ignoreXml) { + public static ValidationParser processValidationConfig(final String file, final ConfigurationImpl targetConfig, + final boolean ignoreXml) { final ValidationParser parser = new ValidationParser(); if (!ignoreXml) { parser.xmlConfig = parseXmlConfig(file); } - if (parser.xmlConfig != null) { + if (parser.xmlConfig == null) { // default config + final CopyOnWriteArraySet<ExecutableType> executableTypes = new CopyOnWriteArraySet<>(); + executableTypes.add(ExecutableType.CONSTRUCTORS); + executableTypes.add(ExecutableType.NON_GETTER_METHODS); + + parser.bootstrap = new BootstrapConfigurationImpl(null, null, null, null, null, Collections.emptySet(), + true, executableTypes, Collections.emptyMap(), null, Collections.emptySet()); + + targetConfig.setExecutableValidation(executableTypes); + } else { if (parser.xmlConfig.getExecutableValidation() == null) { final ExecutableValidationType value = new ExecutableValidationType(); value.setEnabled(true); - final DefaultValidatedExecutableTypesType defaultValidatedExecutableTypes = new DefaultValidatedExecutableTypesType(); + final DefaultValidatedExecutableTypesType defaultValidatedExecutableTypes = + new DefaultValidatedExecutableTypesType(); value.setDefaultValidatedExecutableTypes(defaultValidatedExecutableTypes); defaultValidatedExecutableTypes.getExecutableType().add(ExecutableType.CONSTRUCTORS); defaultValidatedExecutableTypes.getExecutableType().add(ExecutableType.NON_GETTER_METHODS); @@ -123,35 +111,19 @@ public class ValidationParser { applySimpleConfig(parser.xmlConfig, targetConfig); - parser.bootstrap = new BootstrapConfigurationImpl( - parser.xmlConfig.getDefaultProvider(), - parser.xmlConfig.getConstraintValidatorFactory(), - parser.xmlConfig.getMessageInterpolator(), - parser.xmlConfig.getTraversableResolver(), - parser.xmlConfig.getParameterNameProvider(), - new CopyOnWriteArraySet<String>(parser.xmlConfig.getConstraintMapping()), - parser.xmlConfig.getExecutableValidation().getEnabled(), - new CopyOnWriteArraySet<ExecutableType>(targetConfig.getExecutableValidation()), - toMap(parser.xmlConfig.getProperty())); - } else { // default config - final CopyOnWriteArraySet<ExecutableType> executableTypes = new CopyOnWriteArraySet<ExecutableType>(); - executableTypes.add(ExecutableType.CONSTRUCTORS); - executableTypes.add(ExecutableType.NON_GETTER_METHODS); - - parser.bootstrap = new BootstrapConfigurationImpl( - null, null, null, null, null, - new CopyOnWriteArraySet<String>(), - true, - executableTypes, - new HashMap<String, String>()); - - targetConfig.setExecutableValidation(executableTypes); + parser.bootstrap = new BootstrapConfigurationImpl(parser.xmlConfig.getDefaultProvider(), + parser.xmlConfig.getConstraintValidatorFactory(), parser.xmlConfig.getMessageInterpolator(), + parser.xmlConfig.getTraversableResolver(), parser.xmlConfig.getParameterNameProvider(), + new HashSet<>(parser.xmlConfig.getConstraintMapping()), + parser.xmlConfig.getExecutableValidation().getEnabled(), + new HashSet<>(targetConfig.getExecutableValidation()), toMap(parser.xmlConfig.getProperty()), + parser.xmlConfig.getClockProvider(), new HashSet<>(parser.xmlConfig.getValueExtractor())); } return parser; } private static Map<String, String> toMap(final List<PropertyType> property) { - final Map<String, String> map = new HashMap<String, String>(); + final Map<String, String> map = new HashMap<>(); if (property != null) { for (final PropertyType p : property) { map.put(p.getName(), p.getValue()); @@ -162,78 +134,26 @@ public class ValidationParser { @Privileged private static ValidationConfigType parseXmlConfig(final String validationXmlFile) { - InputStream inputStream = null; - try { - inputStream = getInputStream(getValidationXmlFile(validationXmlFile)); + try (InputStream inputStream = getInputStream(getValidationXmlFile(validationXmlFile))) { if (inputStream == null) { - log.log(Level.FINEST, String.format("No %s found. Using annotation based configuration only.", validationXmlFile)); + log.log(Level.FINEST, + String.format("No %s found. Using annotation based configuration only.", validationXmlFile)); return null; } - log.log(Level.FINEST, String.format("%s found.", validationXmlFile)); - Schema schema = getSchema(); - JAXBContext jc = JAXBContext.newInstance(ValidationConfigType.class); - Unmarshaller unmarshaller = jc.createUnmarshaller(); - unmarshaller.setSchema(schema); - StreamSource stream = new StreamSource(inputStream); - JAXBElement<ValidationConfigType> root = - unmarshaller.unmarshal(stream, ValidationConfigType.class); - return root.getValue(); - } catch (JAXBException e) { - throw new ValidationException("Unable to parse " + validationXmlFile, e); - } catch (IOException e) { + return SCHEMA_MANAGER.unmarshal(new InputSource(inputStream), ValidationConfigType.class); + } catch (Exception e) { throw new ValidationException("Unable to parse " + validationXmlFile, e); - } finally { - IOs.closeQuietly(inputStream); } } protected static InputStream getInputStream(final String path) throws IOException { final ClassLoader loader = Reflection.getClassLoader(ValidationParser.class); - final InputStream inputStream = loader.getResourceAsStream(path); - - if (inputStream != null) { - // spec says: If more than one META-INF/validation.xml file - // is found in the classpath, a ValidationException is raised. - final Enumeration<URL> urls = loader.getResources(path); - if (urls.hasMoreElements()) { - final String url = urls.nextElement().toString(); - while (urls.hasMoreElements()) { - if (!url.equals(urls.nextElement().toString())) { // complain when first duplicate found - throw new ValidationException("More than one " + path + " is found in the classpath"); - } - } - } - } - - return IOs.convertToMarkableInputStream(inputStream); - } - - private static Schema getSchema() { - return getSchema(VALIDATION_CONFIGURATION_XSD); - } - - static Schema getSchema(final String xsd) { - final Schema schema = SCHEMA_CACHE.get(xsd); - if (schema != null) { - return schema; - } - - final ClassLoader loader = Reflection.getClassLoader(ValidationParser.class); - final SchemaFactory sf = SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI); - final URL schemaUrl = loader.getResource(xsd); - try { - Schema s = sf.newSchema(schemaUrl); - final Schema old = SCHEMA_CACHE.putIfAbsent(xsd, s); - if (old != null) { - s = old; - } - return s; - } catch (SAXException e) { - log.log(Level.WARNING, String.format("Unable to parse schema: %s", xsd), e); - return null; - } + final List<URL> urls = Collections.list(loader.getResources(path)); + Exceptions.raiseIf(urls.stream().distinct().count() > 1, ValidationException::new, + "More than one %s is found in the classpath", path); + return urls.isEmpty() ? null : urls.get(0).openStream(); } public static void applySimpleConfig(ValidationConfigType xmlConfig, ConfigurationImpl targetConfig) { @@ -246,37 +166,84 @@ public class ValidationParser { } } - private static void applyExecutableValidation(final ValidationConfigType xmlConfig, final ConfigurationImpl targetConfig) { - final CopyOnWriteArrayList<ExecutableType> executableTypes = new CopyOnWriteArrayList<ExecutableType>(); - if (xmlConfig.getExecutableValidation() != null && xmlConfig.getExecutableValidation().getEnabled() - && xmlConfig.getExecutableValidation().getDefaultValidatedExecutableTypes() != null) { - executableTypes.addAll(xmlConfig.getExecutableValidation().getDefaultValidatedExecutableTypes().getExecutableType()); - } + private static void applyExecutableValidation(final ValidationConfigType xmlConfig, + final ConfigurationImpl targetConfig) { + + final Set<ExecutableType> executableTypes = Optional.of(xmlConfig) + .map(ValidationConfigType::getExecutableValidation).filter(vc -> Boolean.TRUE.equals(vc.getEnabled())) + .map(ExecutableValidationType::getDefaultValidatedExecutableTypes) + .map(DefaultValidatedExecutableTypesType::getExecutableType).map(EnumSet::copyOf) + .orElseGet(() -> EnumSet.noneOf(ExecutableType.class)); if (executableTypes.contains(ExecutableType.ALL)) { executableTypes.clear(); executableTypes.add(ExecutableType.CONSTRUCTORS); executableTypes.add(ExecutableType.NON_GETTER_METHODS); executableTypes.add(ExecutableType.GETTER_METHODS); - } else if (executableTypes.contains(ExecutableType.NONE)) { // if both are present ALL gains + } else if (executableTypes.contains(ExecutableType.NONE)) { // if both are present ALL trumps NONE executableTypes.clear(); } + targetConfig.setExecutableValidation(Collections.unmodifiableSet(executableTypes)); + } + + private static void applyMappingStreams(ValidationConfigType xmlConfig, ConfigurationImpl target) { + for (String rawMappingFileName : xmlConfig.getConstraintMapping()) { + String mappingFileName = rawMappingFileName; + if (mappingFileName.charAt(0) == '/') { + // Classloader needs a path without a starting / + mappingFileName = mappingFileName.substring(1); + } + log.log(Level.FINEST, String.format("Trying to open input stream for %s", mappingFileName)); + try { + final InputStream in = getInputStream(mappingFileName); + Exceptions.raiseIf(in == null, ValidationException::new, + "Unable to open input stream for mapping file %s", mappingFileName); + target.addMapping(in); + } catch (IOException e) { + Exceptions.raise(ValidationException::new, e, "Unable to open input stream for mapping file %s", + mappingFileName); + } + } + } + + private ValidationConfigType xmlConfig; + private BootstrapConfigurationImpl bootstrap; + private Collection<ValidationException> exceptions = new CopyOnWriteArrayList<ValidationException>(); + + private ValidationParser() { + // no-op + } + + public void applyConfigWithInstantiation(ConfigurationImpl targetConfig) { + if (xmlConfig == null) { + return; + } - targetConfig.setExecutableValidation(executableTypes); + applyProviderClass(xmlConfig, targetConfig); + applyMessageInterpolator(xmlConfig, targetConfig); + applyTraversableResolver(xmlConfig, targetConfig); + applyConstraintFactory(xmlConfig, targetConfig); + applyParameterNameProvider(xmlConfig, targetConfig); + applyMappingStreams(xmlConfig, targetConfig); + applyProperties(xmlConfig, targetConfig); + } + + public BootstrapConfigurationImpl getBootstrap() { + return bootstrap; } - private void applyParameterNameProvider(final ValidationConfigType xmlConfig, final ConfigurationImpl targetConfig) { + private void applyParameterNameProvider(final ValidationConfigType xmlConfig, + final ConfigurationImpl targetConfig) { final String parameterNameProvider = xmlConfig.getParameterNameProvider(); - if (targetConfig.getParameterNameProvider() == targetConfig.getDefaultParameterNameProvider()) { // ref == - if (parameterNameProvider != null) { - final Class<?> loaded = loadClass(parameterNameProvider); - if (loaded == null) { - log.log(Level.SEVERE, "Can't load " + parameterNameProvider); - } else { - final Class<? extends ParameterNameProvider> clazz = loaded.asSubclass(ParameterNameProvider.class); - targetConfig.parameterNameProviderClass(clazz); - log.log(Level.INFO, String.format("Using %s as validation provider.", parameterNameProvider)); - } + if (targetConfig.getParameterNameProvider() == targetConfig.getDefaultParameterNameProvider() + && parameterNameProvider != null) { + final Class<?> loaded = loadClass(parameterNameProvider); + if (loaded == null) { + log.log(Level.SEVERE, "Can't load " + parameterNameProvider); + } else { + final Class<? extends ParameterNameProvider> clazz = loaded.asSubclass(ParameterNameProvider.class); + targetConfig.parameterNameProviderClass(clazz); + log.log(Level.INFO, String.format("Using %s as validation provider.", parameterNameProvider)); } } } @@ -293,65 +260,36 @@ public class ValidationParser { } @SuppressWarnings("unchecked") - private void applyMessageInterpolator(ValidationConfigType xmlConfig, - ConfigurationImpl target) { + private void applyMessageInterpolator(ValidationConfigType xmlConfig, ConfigurationImpl target) { String messageInterpolatorClass = xmlConfig.getMessageInterpolator(); - if (target.getMessageInterpolator() == target.getDefaultMessageInterpolator()) { // ref == - if (messageInterpolatorClass != null) { - Class<MessageInterpolator> clazz = (Class<MessageInterpolator>) - loadClass(messageInterpolatorClass); - target.messageInterpolatorClass(clazz); - log.log(Level.INFO, String.format("Using %s as message interpolator.", messageInterpolatorClass)); - } + if (target.getMessageInterpolator() == target.getDefaultMessageInterpolator() + && messageInterpolatorClass != null) { + Class<MessageInterpolator> clazz = (Class<MessageInterpolator>) loadClass(messageInterpolatorClass); + target.messageInterpolatorClass(clazz); + log.log(Level.INFO, String.format("Using %s as message interpolator.", messageInterpolatorClass)); } } @SuppressWarnings("unchecked") - private void applyTraversableResolver(ValidationConfigType xmlConfig, - ConfigurationImpl target) { + private void applyTraversableResolver(ValidationConfigType xmlConfig, ConfigurationImpl target) { String traversableResolverClass = xmlConfig.getTraversableResolver(); - if (target.getTraversableResolver() == target.getDefaultTraversableResolver() && traversableResolverClass != null) { - Class<TraversableResolver> clazz = (Class<TraversableResolver>) - loadClass(traversableResolverClass); - target.traversableResolverClass(clazz); - log.log(Level.INFO, String.format("Using %s as traversable resolver.", traversableResolverClass)); - } + if (target.getTraversableResolver() == target.getDefaultTraversableResolver() + && traversableResolverClass != null) { + Class<TraversableResolver> clazz = (Class<TraversableResolver>) loadClass(traversableResolverClass); + target.traversableResolverClass(clazz); + log.log(Level.INFO, String.format("Using %s as traversable resolver.", traversableResolverClass)); + } } @SuppressWarnings("unchecked") - private void applyConstraintFactory(ValidationConfigType xmlConfig, - ConfigurationImpl target) { + private void applyConstraintFactory(ValidationConfigType xmlConfig, ConfigurationImpl target) { String constraintFactoryClass = xmlConfig.getConstraintValidatorFactory(); - if (target.getConstraintValidatorFactory() == target.getDefaultConstraintValidatorFactory() && constraintFactoryClass != null) { - Class<ConstraintValidatorFactory> clazz = (Class<ConstraintValidatorFactory>) - loadClass(constraintFactoryClass); - target.constraintValidatorFactoryClass(clazz); - log.log(Level.INFO, String.format("Using %s as constraint factory.", constraintFactoryClass)); - } - } - - private static void applyMappingStreams(ValidationConfigType xmlConfig, - ConfigurationImpl target) { - for (String rawMappingFileName : xmlConfig.getConstraintMapping()) { - String mappingFileName = rawMappingFileName; - if (mappingFileName.startsWith("/")) { - // Classloader needs a path without a starting / - mappingFileName = mappingFileName.substring(1); - } - log.log(Level.FINEST, String.format("Trying to open input stream for %s", mappingFileName)); - InputStream in; - try { - in = getInputStream(mappingFileName); - if (in == null) { - throw new ValidationException( - "Unable to open input stream for mapping file " + - mappingFileName); - } - } catch (IOException e) { - throw new ValidationException("Unable to open input stream for mapping file " + - mappingFileName, e); - } - target.addMapping(in); + if (target.getConstraintValidatorFactory() == target.getDefaultConstraintValidatorFactory() + && constraintFactoryClass != null) { + Class<ConstraintValidatorFactory> clazz = + (Class<ConstraintValidatorFactory>) loadClass(constraintFactoryClass); + target.constraintValidatorFactoryClass(clazz); + log.log(Level.INFO, String.format("Using %s as constraint factory.", constraintFactoryClass)); } } http://git-wip-us.apache.org/repos/asf/bval/blob/a43c0b0c/bval-jsr/src/main/java/org/apache/bval/jsr/xml/XmlUtils.java ---------------------------------------------------------------------- diff --git a/bval-jsr/src/main/java/org/apache/bval/jsr/xml/XmlUtils.java b/bval-jsr/src/main/java/org/apache/bval/jsr/xml/XmlUtils.java new file mode 100644 index 0000000..2826174 --- /dev/null +++ b/bval-jsr/src/main/java/org/apache/bval/jsr/xml/XmlUtils.java @@ -0,0 +1,67 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.bval.jsr.xml; + +import java.io.IOException; +import java.io.InputStream; +import java.net.URL; +import java.util.logging.Level; +import java.util.logging.Logger; + +import javax.xml.XMLConstants; +import javax.xml.bind.JAXBContext; +import javax.xml.bind.JAXBElement; +import javax.xml.bind.JAXBException; +import javax.xml.bind.Unmarshaller; +import javax.xml.bind.helpers.DefaultValidationEventHandler; +import javax.xml.parsers.DocumentBuilderFactory; +import javax.xml.parsers.ParserConfigurationException; +import javax.xml.validation.Schema; +import javax.xml.validation.SchemaFactory; + +import org.apache.bval.util.reflection.Reflection; +import org.w3c.dom.Document; +import org.xml.sax.SAXException; + +class XmlUtils { + private static final DocumentBuilderFactory DOCUMENT_BUILDER_FACTORY = DocumentBuilderFactory.newInstance(); + private static final Logger log = Logger.getLogger(XmlUtils.class.getName()); + private static final SchemaFactory SCHEMA_FACTORY = SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI); + + static Schema loadSchema(String resource) { + final URL schemaUrl = Reflection.getClassLoader(XmlUtils.class).getResource(resource); + try { + return SCHEMA_FACTORY.newSchema(schemaUrl); + } catch (SAXException e) { + log.log(Level.WARNING, String.format("Unable to parse schema: %s", resource), e); + return null; + } + } + + static Document parse(InputStream in) throws SAXException, IOException, ParserConfigurationException { + return DOCUMENT_BUILDER_FACTORY.newDocumentBuilder().parse(in); + } + + static <T> T unmarshal(Document document, Schema schema, Class<T> type) throws JAXBException { + final JAXBContext jc = JAXBContext.newInstance(type); + final Unmarshaller unmarshaller = jc.createUnmarshaller(); + unmarshaller.setSchema(schema); + unmarshaller.setEventHandler(new DefaultValidationEventHandler()); + final JAXBElement<T> root = unmarshaller.unmarshal(document, type); + return root.getValue(); + } +} \ No newline at end of file http://git-wip-us.apache.org/repos/asf/bval/blob/a43c0b0c/bval-jsr/src/main/resources/org/apache/bval/jsr/DefaultConstraints.properties ---------------------------------------------------------------------- diff --git a/bval-jsr/src/main/resources/org/apache/bval/jsr/DefaultConstraints.properties b/bval-jsr/src/main/resources/org/apache/bval/jsr/DefaultConstraints.properties index 7bc433b..3543eb9 100644 --- a/bval-jsr/src/main/resources/org/apache/bval/jsr/DefaultConstraints.properties +++ b/bval-jsr/src/main/resources/org/apache/bval/jsr/DefaultConstraints.properties @@ -22,22 +22,86 @@ javax.validation.constraints.AssertFalse=org.apache.bval.constraints.AssertFalseValidator javax.validation.constraints.AssertTrue=org.apache.bval.constraints.AssertTrueValidator + javax.validation.constraints.DecimalMax=org.apache.bval.constraints.DecimalMaxValidatorForNumber,\ org.apache.bval.constraints.DecimalMaxValidatorForString + javax.validation.constraints.DecimalMin=org.apache.bval.constraints.DecimalMinValidatorForNumber,\ org.apache.bval.constraints.DecimalMinValidatorForString + javax.validation.constraints.Digits=org.apache.bval.constraints.DigitsValidatorForNumber,\ org.apache.bval.constraints.DigitsValidatorForString -javax.validation.constraints.Future=org.apache.bval.constraints.FutureValidatorForDate,\ - org.apache.bval.constraints.FutureValidatorForCalendar + +javax.validation.constraints.Future=\ + org.apache.bval.constraints.FutureValidator$ForCalendar,\ + org.apache.bval.constraints.FutureValidator$ForDate,\ + org.apache.bval.constraints.FutureValidator$ForChronoLocalDate,\ + org.apache.bval.constraints.FutureValidator$ForChronoLocalDateTime,\ + org.apache.bval.constraints.FutureValidator$ForChronoZonedDateTime,\ + org.apache.bval.constraints.FutureValidator$ForInstant,\ + org.apache.bval.constraints.FutureValidator$ForLocalTime,\ + org.apache.bval.constraints.FutureValidator$ForMonthDay,\ + org.apache.bval.constraints.FutureValidator$ForOffsetDateTime,\ + org.apache.bval.constraints.FutureValidator$ForOffsetTime,\ + org.apache.bval.constraints.FutureValidator$ForYear,\ + org.apache.bval.constraints.FutureValidator$ForYearMonth + +javax.validation.constraints.FutureOrPresent=\ + org.apache.bval.constraints.FutureOrPresentValidator$ForCalendar,\ + org.apache.bval.constraints.FutureOrPresentValidator$ForDate,\ + org.apache.bval.constraints.FutureOrPresentValidator$ForChronoLocalDate,\ + org.apache.bval.constraints.FutureOrPresentValidator$ForChronoLocalDateTime,\ + org.apache.bval.constraints.FutureOrPresentValidator$ForChronoZonedDateTime,\ + org.apache.bval.constraints.FutureOrPresentValidator$ForInstant,\ + org.apache.bval.constraints.FutureOrPresentValidator$ForLocalTime,\ + org.apache.bval.constraints.FutureOrPresentValidator$ForMonthDay,\ + org.apache.bval.constraints.FutureOrPresentValidator$ForOffsetDateTime,\ + org.apache.bval.constraints.FutureOrPresentValidator$ForOffsetTime,\ + org.apache.bval.constraints.FutureOrPresentValidator$ForYear,\ + org.apache.bval.constraints.FutureOrPresentValidator$ForYearMonth + javax.validation.constraints.Max=org.apache.bval.constraints.MaxValidatorForNumber,\ org.apache.bval.constraints.MaxValidatorForString + javax.validation.constraints.Min=org.apache.bval.constraints.MinValidatorForNumber,\ org.apache.bval.constraints.MinValidatorForString + +javax.validation.constraints.Negative=org.apache.bval.constraints.NumberSignValidator$ForNegative +javax.validation.constraints.NegativeOrZero=org.apache.bval.constraints.NumberSignValidator$ForNegative$OrZero javax.validation.constraints.NotNull=org.apache.bval.constraints.NotNullValidator javax.validation.constraints.Null=org.apache.bval.constraints.NullValidator -javax.validation.constraints.Past=org.apache.bval.constraints.PastValidatorForDate,\ - org.apache.bval.constraints.PastValidatorForCalendar + +javax.validation.constraints.Past=\ + org.apache.bval.constraints.PastValidator$ForCalendar,\ + org.apache.bval.constraints.PastValidator$ForDate,\ + org.apache.bval.constraints.PastValidator$ForChronoLocalDate,\ + org.apache.bval.constraints.PastValidator$ForChronoLocalDateTime,\ + org.apache.bval.constraints.PastValidator$ForChronoZonedDateTime,\ + org.apache.bval.constraints.PastValidator$ForInstant,\ + org.apache.bval.constraints.PastValidator$ForLocalTime,\ + org.apache.bval.constraints.PastValidator$ForMonthDay,\ + org.apache.bval.constraints.PastValidator$ForOffsetDateTime,\ + org.apache.bval.constraints.PastValidator$ForOffsetTime,\ + org.apache.bval.constraints.PastValidator$ForYear,\ + org.apache.bval.constraints.PastValidator$ForYearMonth + +javax.validation.constraints.PastOrPresent=\ + org.apache.bval.constraints.PastOrPresentValidator$ForCalendar,\ + org.apache.bval.constraints.PastOrPresentValidator$ForDate,\ + org.apache.bval.constraints.PastOrPresentValidator$ForChronoLocalDate,\ + org.apache.bval.constraints.PastOrPresentValidator$ForChronoLocalDateTime,\ + org.apache.bval.constraints.PastOrPresentValidator$ForChronoZonedDateTime,\ + org.apache.bval.constraints.PastOrPresentValidator$ForInstant,\ + org.apache.bval.constraints.PastOrPresentValidator$ForLocalTime,\ + org.apache.bval.constraints.PastOrPresentValidator$ForMonthDay,\ + org.apache.bval.constraints.PastOrPresentValidator$ForOffsetDateTime,\ + org.apache.bval.constraints.PastOrPresentValidator$ForOffsetTime,\ + org.apache.bval.constraints.PastOrPresentValidator$ForYear,\ + org.apache.bval.constraints.PastOrPresentValidator$ForYearMonth + +javax.validation.constraints.Positive=org.apache.bval.constraints.NumberSignValidator$ForPositive +javax.validation.constraints.PositiveOrZero=org.apache.bval.constraints.NumberSignValidator$ForPositive$OrZero + javax.validation.constraints.Size=org.apache.bval.constraints.SizeValidatorForCharSequence,\ org.apache.bval.constraints.SizeValidatorForMap,\ org.apache.bval.constraints.SizeValidatorForCollection,\ @@ -50,4 +114,5 @@ javax.validation.constraints.Size=org.apache.bval.constraints.SizeValidatorForCh org.apache.bval.constraints.SizeValidatorForArrayOfLong,\ org.apache.bval.constraints.SizeValidatorForArrayOfObject,\ org.apache.bval.constraints.SizeValidatorForArrayOfShort + javax.validation.constraints.Pattern=org.apache.bval.constraints.PatternValidator http://git-wip-us.apache.org/repos/asf/bval/blob/a43c0b0c/bval-jsr/src/main/resources/org/apache/bval/jsr/valueextraction/DefaultExtractors.properties ---------------------------------------------------------------------- diff --git a/bval-jsr/src/main/resources/org/apache/bval/jsr/valueextraction/DefaultExtractors.properties b/bval-jsr/src/main/resources/org/apache/bval/jsr/valueextraction/DefaultExtractors.properties new file mode 100644 index 0000000..9d9b768 --- /dev/null +++ b/bval-jsr/src/main/resources/org/apache/bval/jsr/valueextraction/DefaultExtractors.properties @@ -0,0 +1,25 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. + +javax.validation.valueextraction.ValueExtractor=\ + org.apache.bval.jsr.valueextraction.IterableElementExtractor,\ + org.apache.bval.jsr.valueextraction.ListElementExtractor + +javax.validation.valueextraction.ValueExtractor.container=\ + org.apache.bval.jsr.valueextraction.MapExtractor,\ + org.apache.bval.jsr.valueextraction.OptionalExtractor,\ + org.apache.bval.jsr.valueextraction.FxExtractor http://git-wip-us.apache.org/repos/asf/bval/blob/a43c0b0c/bval-jsr/src/main/xjb/binding-customization.xjb ---------------------------------------------------------------------- diff --git a/bval-jsr/src/main/xjb/binding-customization.xjb b/bval-jsr/src/main/xjb/binding-customization.xjb index 598bd3c..762a058 100644 --- a/bval-jsr/src/main/xjb/binding-customization.xjb +++ b/bval-jsr/src/main/xjb/binding-customization.xjb @@ -19,7 +19,7 @@ --> <jxb:bindings version="1.0" xmlns:jxb="http://java.sun.com/xml/ns/jaxb" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:xjc="http://java.sun.com/xml/ns/jaxb/xjc"> - <jxb:bindings schemaLocation="../xsd/validation-mapping-1.1.xsd" node="/xs:schema"> + <jxb:bindings schemaLocation="../xsd/validation-mapping-2.0.xsd" node="/xs:schema"> <jxb:globalBindings> <xjc:javaType name="java.lang.String" xmlType="xs:string" adapter="javax.xml.bind.annotation.adapters.CollapsedStringAdapter"/> @@ -29,7 +29,7 @@ <jxb:property name="classType"/> </jxb:bindings> </jxb:bindings> - <jxb:bindings schemaLocation="../xsd/validation-configuration-1.1.xsd" node="/xs:schema"> + <jxb:bindings schemaLocation="../xsd/validation-configuration-2.0.xsd" node="/xs:schema"> <jxb:bindings node="//xs:element[@name='executable-type']"> <jxb:javaType name="javax.validation.executable.ExecutableType" parseMethod="javax.validation.executable.ExecutableType.valueOf"/> http://git-wip-us.apache.org/repos/asf/bval/blob/a43c0b0c/bval-jsr/src/main/xsd/validation-configuration-2.0.xsd ---------------------------------------------------------------------- diff --git a/bval-jsr/src/main/xsd/validation-configuration-2.0.xsd b/bval-jsr/src/main/xsd/validation-configuration-2.0.xsd new file mode 100644 index 0000000..6fd7874 --- /dev/null +++ b/bval-jsr/src/main/xsd/validation-configuration-2.0.xsd @@ -0,0 +1,75 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + Licensed to the Apache Software Foundation (ASF) under one + or more contributor license agreements. See the NOTICE file + distributed with this work for additional information + regarding copyright ownership. The ASF licenses this file + to you under the Apache License, Version 2.0 (the + "License"); you may not use this file except in compliance + with the License. You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, + software distributed under the License is distributed on an + "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + KIND, either express or implied. See the License for the + specific language governing permissions and limitations + under the License. +--> +<xs:schema attributeFormDefault="unqualified" + elementFormDefault="qualified" + targetNamespace="http://xmlns.jcp.org/xml/ns/javax/validation/configuration" + xmlns:xs="http://www.w3.org/2001/XMLSchema" + xmlns:config="http://xmlns.jcp.org/xml/ns/javax/validation/configuration" + version="2.0"> + <xs:element name="validation-config" type="config:validation-configType" /> + <xs:complexType name="validation-configType"> + <xs:sequence> + <xs:element type="xs:string" name="default-provider" minOccurs="0" /> + <xs:element type="xs:string" name="message-interpolator" minOccurs="0" /> + <xs:element type="xs:string" name="traversable-resolver" minOccurs="0" /> + <xs:element type="xs:string" name="constraint-validator-factory" minOccurs="0" /> + <xs:element type="xs:string" name="parameter-name-provider" minOccurs="0" /> + <xs:element type="xs:string" name="clock-provider" minOccurs="0" /> + <xs:element type="xs:string" name="value-extractor" maxOccurs="unbounded" minOccurs="0" /> + <xs:element type="config:executable-validationType" name="executable-validation" minOccurs="0" /> + <xs:element type="xs:string" name="constraint-mapping" maxOccurs="unbounded" minOccurs="0" /> + <xs:element type="config:propertyType" name="property" maxOccurs="unbounded" minOccurs="0" /> + </xs:sequence> + <xs:attribute name="version" type="config:versionType" fixed="2.0" /> <!-- use="required" --> + </xs:complexType> + <xs:complexType name="executable-validationType"> + <xs:sequence> + <xs:element type="config:default-validated-executable-typesType" name="default-validated-executable-types" minOccurs="0" /> + </xs:sequence> + <xs:attribute name="enabled" use="optional" type="xs:boolean" default="true" /> + </xs:complexType> + <xs:complexType name="default-validated-executable-typesType"> + <xs:sequence> + <xs:element name="executable-type" maxOccurs="unbounded" minOccurs="1"> + <xs:simpleType> + <xs:restriction base="xs:string"> + <xs:enumeration value="NONE" /> + <xs:enumeration value="CONSTRUCTORS" /> + <xs:enumeration value="NON_GETTER_METHODS" /> + <xs:enumeration value="GETTER_METHODS" /> + <xs:enumeration value="ALL" /> + </xs:restriction> + </xs:simpleType> + </xs:element> + </xs:sequence> + </xs:complexType> + <xs:complexType name="propertyType"> + <xs:simpleContent> + <xs:extension base="xs:string"> + <xs:attribute name="name" use="required" type="xs:string" /> + </xs:extension> + </xs:simpleContent> + </xs:complexType> + <xs:simpleType name="versionType"> + <xs:restriction base="xs:token"> + <xs:pattern value="[0-9]+(\.[0-9]+)*" /> + </xs:restriction> + </xs:simpleType> +</xs:schema>
