Java tutorial
/* * 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.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 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.EnumerationConverter; import org.apache.bval.jsr.util.IOs; import org.apache.bval.util.FieldAccess; import org.apache.bval.util.MethodAccess; import org.apache.bval.util.reflection.Reflection; import org.apache.commons.beanutils.ConvertUtils; import org.apache.commons.beanutils.Converter; import org.apache.commons.lang3.ArrayUtils; import org.apache.commons.lang3.StringUtils; import org.apache.commons.weaver.privilizer.Privileged; import org.apache.commons.weaver.privilizer.Privilizing; import org.apache.commons.weaver.privilizer.Privilizing.CallTo; /** * Uses JAXB to parse constraints.xml based on validation-mapping-1.0.xsd.<br> */ @Privilizing(@CallTo(Reflection.class)) public class ValidationMappingParser { // private static final Log log = LogFactory.getLog(ValidationMappingParser.class); private static final String VALIDATION_MAPPING_XSD = "META-INF/validation-mapping-1.1.xsd"; 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<?>>(); } /** * Parse files with constraint mappings and collect information in the factory. * * @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); } } } /** @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) { 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(Class.class)) { value = toQualifiedClassName(value, defaultPackage); } 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); } /* Converter lookup */ Converter converter = ConvertUtils.lookup(returnType); if (converter == null && returnType.isEnum()) { converter = EnumerationConverter.getInstance(); } if (converter == null) { return converter; } return converter.convert(returnType, value); } 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 ArrayUtils.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(); Class<?> clazz = loadClass(annotationClassName, defaultPackage); if (!clazz.isAnnotation()) { throw new ValidationException(annotationClassName + " is not an annotation"); } Class<? extends Annotation> annotationClass = (Class<? extends Annotation>) clazz; 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); if (!ConstraintValidator.class.isAssignableFrom(validatorClass)) { throw new ValidationException(validatorClass + " is not a constraint validator class"); } /* Annotation based ConstraintValidator come before XML based ConstraintValidator in the array. The new list is returned by ConstraintDescriptor.getConstraintValidatorClasses(). */ 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()])); } } } private List<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 = 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; } private Class<?> loadClass(String className, String defaultPackage) { return loadClass(toQualifiedClassName(className, defaultPackage)); } private String toQualifiedClassName(String className, String defaultPackage) { if (!isQualifiedClass(className)) { if (className.startsWith("[L") && className.endsWith(";")) { className = "[L" + defaultPackage + "." + className.substring(2); } else { 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; } } private Class<?> loadClass(final String className) { ClassLoader loader = Reflection.getClassLoader(ValidationMappingParser.class); if (loader == null) loader = getClass().getClassLoader(); try { return Class.forName(className, true, loader); } catch (ClassNotFoundException ex) { throw new ValidationException("Unable to load class: " + className, ex); } } }