/** * @return A list of the class level annotations that the annotator will * use. */ private List<MetaAnnotation> buildClassAnnotations() { List<MetaAnnotation> anns = Lists.newArrayList(); HashMap<String, Object> annotParams; // AlwaysValid JDefinedClass alwaysValid = buildTemplateConstraint("AlwaysValid"); JDefinedClass alwaysValidValidator = buildTemplateConstraintValidator("AlwaysValidValidator", alwaysValid, Object.class); JMethod isValid = getIsValidMethod(alwaysValidValidator); isValid.body()._return(JExpr.TRUE); alwaysValid.annotate(Constraint.class).param("validatedBy", alwaysValidValidator); annotParams = Maps.newHashMap(); anns.add(new MetaAnnotation(alwaysValid, AnnotationType.JSR_303, annotParams)); return anns; }
/** * @return true if this is a multi-value constraint class as per JSR 303 requirements (contains a value() method * which returns an array of Constraints), false otherwise. */ private static boolean isMultiValueConstraintClass(Class<? extends Annotation> annotationClass) { // It must have a value() method. Method valueMethod; try { valueMethod = annotationClass.getDeclaredMethod("value"); } catch (NoSuchMethodException e) { return false; } // That value field must return a type of "array of Constraint" //noinspection RedundantIfStatement if (valueMethod.getReturnType().isArray() && valueMethod.getReturnType().getComponentType().getAnnotation(Constraint.class) != null) { return true; } return false; }
private static boolean hasConstraintParameter(Method method) { Annotation[][] parameterAnnotations = method.getParameterAnnotations(); if (parameterAnnotations != null && parameterAnnotations.length > 0) { for (Annotation[] annotations : parameterAnnotations) { for (Annotation annotation : annotations) { if (annotation.annotationType().isAnnotationPresent(Constraint.class)) { return true; } } } } return false; }
private boolean isRequired(Field field) { if (field.isAnnotationPresent(NotNull.class)) { return true; } Annotation[] annotations = field.getAnnotations(); for (Annotation annotation : annotations) { Class<? extends Annotation> annotationType = annotation.annotationType(); if (annotationType.isAnnotationPresent(Constraint.class) && annotationType .isAnnotationPresent(NotNull.class)) { return true; } } return false; }
private <U, C extends Annotation> Optional<U> determineConstraints(Class<C> clazz, Field field, Function<C, U> callback) { C constraint = field.getAnnotation(clazz); if (constraint != null) { return Optional.of(callback.apply(constraint)); } for (Annotation annotation : field.getAnnotations()) { Class<? extends Annotation> annotationType = annotation.annotationType(); if (annotationType.isAnnotationPresent(Constraint.class) && annotationType.isAnnotationPresent(clazz)) { return Optional.of(callback.apply(annotationType.getAnnotation(clazz))); } } return Optional.empty(); }
private boolean hasMatchingAnnotation(final Annotation expectedAnnotation, final Annotation[] annotations) throws UnableToCompleteException { // See spec section 2.2. Applying multiple constraints of the same type for (final Annotation annotation : annotations) { // annotations not annotated by @Constraint if (annotation.annotationType().getAnnotation(Constraint.class) == null) { try { // value element has a return type of an array of constraint // annotations final Method valueMethod = annotation.annotationType().getMethod("value"); final Class<?> valueType = valueMethod.getReturnType(); if (valueType.isArray() && Annotation.class.isAssignableFrom(valueType.getComponentType())) { if (Modifier.isAbstract(valueMethod.getModifiers())) { // handle edge case where interface is marked "abstract" valueMethod.setAccessible(true); } final Annotation[] valueAnnotions = (Annotation[]) valueMethod.invoke(annotation); for (final Annotation annotation2 : valueAnnotions) { if (expectedAnnotation.equals(annotation2)) { return true; } } } } catch (final NoSuchMethodException ignore) { // NOPMD // Expected Case. } catch (final Exception e) { throw error(this.logger, e); } } } return false; }
public static boolean isBeanConstraint(String annotationName) { boolean result = false; try { Class<?> annotationClass = Class.forName(annotationName); result = annotationClass.getAnnotation(Constraint.class) != null; } catch (ClassNotFoundException e) { ; // TODO At least log this, probably some class loading issue that we have to solve } return result; }
private static List<Class<? extends ConstraintValidator<?, ?>>> getBeanConstraintValidator(Class annotationClass) { Constraint annotation = (Constraint) annotationClass.getAnnotation(Constraint.class); List<Class<? extends ConstraintValidator<?, ?>>> result = new ArrayList<>(); if (annotation != null) { result = Arrays.asList(annotation.validatedBy()); } return result; }
@Override public boolean isValid(List<?> entries, ConstraintValidatorContext context) { boolean valid = true; if(entries == null){ return valid; } if(ArrayUtils.getLength(constraints) != ArrayUtils.getLength(messages)){ throw new ConstraintDeclarationException("Number of messages must be the same as number of constraints"); } ValidatorFactory validatorFactory = Validation.buildDefaultValidatorFactory(); ConstraintValidatorFactory constraintValidatorFactory = validatorFactory.getConstraintValidatorFactory(); for(Object element : entries) { for(Class<?> constraint : constraints) { Constraint constraintAnnotation = constraint.getAnnotation(Constraint.class); Class<? extends ConstraintValidator<?, ?>>[] constraintValidators = constraintAnnotation.validatedBy(); for (int i = 0; i < constraintValidators.length; i++) { ConstraintValidator constraintValidator = constraintValidatorFactory.getInstance(constraintValidators[i]); if(!constraintValidator.isValid(element, context)){ context.buildConstraintViolationWithTemplate(messages[i]).addConstraintViolation().disableDefaultConstraintViolation(); valid = false; } } } } return valid; }
private boolean isValidationAnnotations(final Annotation... annotations) { boolean matches = false; for (Annotation ann : annotations) { final Class<? extends Annotation> annotationType = ann.annotationType(); if (Valid.class.equals(annotationType) || annotationType.isAnnotationPresent(Constraint.class)) { matches = true; break; } } return matches; }
@Test public void testAnnotation() throws Exception { DayOfWeek validationAnnotation = Dummy.class.getField("date").getAnnotation(DayOfWeek.class); // instantiate validator from annotation info Constraint validatorAnnotation = DayOfWeek.class.getAnnotation(Constraint.class); Class<?>[] validatorClass = validatorAnnotation.validatedBy(); DayOfWeekValidator validator = (DayOfWeekValidator) BeanUtil.newInstance(validatorClass[0]); validator.initialize(validationAnnotation); // test check(validator, true, false, true, false, false, false, false); }
@SuppressWarnings("unchecked") public static <T extends Annotation> MinijaxConstraintDescriptor<T> build(final AnnotatedType annotatedType, final T annotation) { final Constraint constraint = annotation.annotationType().getAnnotation(Constraint.class); if (constraint == null) { return null; } final Class<?> valueClass = ReflectionUtils.getRawType(annotatedType); final Class<?> annotationClass = annotation.annotationType(); if (constraint.validatedBy().length > 0) { return buildDeclaredValidator(annotation, constraint.validatedBy()[0]); } else if (annotationClass == AssertFalse.class) { return (MinijaxConstraintDescriptor<T>) buildAssertFalseValidator((AssertFalse) annotation, valueClass); } else if (annotationClass == AssertTrue.class) { return (MinijaxConstraintDescriptor<T>) buildAssertTrueValidator((AssertTrue) annotation, valueClass); } else if (annotationClass == Max.class) { return (MinijaxConstraintDescriptor<T>) buildMaxValidator((Max) annotation, valueClass); } else if (annotationClass == Min.class) { return (MinijaxConstraintDescriptor<T>) buildMinValidator((Min) annotation, valueClass); } else if (annotationClass == NotBlank.class) { return (MinijaxConstraintDescriptor<T>) buildNotBlankValidator((NotBlank) annotation, valueClass); } else if (annotationClass == NotEmpty.class) { return (MinijaxConstraintDescriptor<T>) buildNotEmptyValidator((NotEmpty) annotation, valueClass); } else if (annotationClass == NotNull.class) { return (MinijaxConstraintDescriptor<T>) buildNotNullValidator((NotNull) annotation); } else if (annotationClass == Pattern.class) { return (MinijaxConstraintDescriptor<T>) buildPatternValidator((Pattern) annotation, valueClass); } else if (annotationClass == Size.class) { return (MinijaxConstraintDescriptor<T>) buildSizeValidator((Size) annotation, valueClass); } else { throw new ValidationException("Unrecognized constraint annotation: " + annotation); } }
/** * Initializes the instance based on what is returned by {@link #ignoreAllAnnotationsAssociatedWithTheseProjectClasses()} * and {@link #specificAnnotationDeclarationExclusionsForProject()}. This is time consuming and should only be done * once per project if possible - see the usage info in the {@link ReflectionBasedJsr303AnnotationTrollerBase} * class-level javadocs. * * <p>The given set of extra packages for constraint annotation searching will be passed into {@link * #getFinalPackagesToSearchForConstraintAnnotations(Set)} to generate the final set of packages that are searched. * If you don't want the {@link #DEFAULT_CONSTRAINT_SEARCH_PACKAGES} default packages to be searched you can * override {@link #getDefaultPackagesToSearchForConstraintAnnotations()}. */ public ReflectionBasedJsr303AnnotationTrollerBase(Set<String> extraPackagesForConstraintAnnotationSearch) { /* * Set up the {@link #ignoreAllAnnotationsAssociatedWithTheseClasses} and * {@link #specificAnnotationDeclarationsExcludedFromStrictMessageRequirement} fields so we know which * annotations are project-relevant vs. unit-test-only. */ ignoreAllAnnotationsAssociatedWithTheseClasses = new ArrayList<>(setupIgnoreAllAnnotationsAssociatedWithTheseClasses()); specificAnnotationDeclarationsExcludedFromStrictMessageRequirement = new ArrayList<>(setupSpecificAnnotationDeclarationExclusions()); /* * Set up the {@link #reflections}, {@link #constraintAnnotationClasses}, and * {@link #allConstraintAnnotationsMasterList} fields. This is where the crazy reflection magic happens to troll * the project for the JSR 303 annotation declarations. */ // Create the ConfigurationBuilder to search the relevant set of packages. ConfigurationBuilder configurationBuilder = new ConfigurationBuilder(); for (String packageToAdd : getFinalPackagesToSearchForConstraintAnnotations( extraPackagesForConstraintAnnotationSearch)) { configurationBuilder.addUrls(ClasspathHelper.forPackage(packageToAdd)); } // Create the Reflections object so it scans for all validation annotations we care about and all project // classes that might have annotations on them. reflections = new Reflections(configurationBuilder.setScanners( new SubTypesScanner(), new MethodParameterScanner(), new TypeAnnotationsScanner(), new MethodAnnotationsScanner(), new FieldAnnotationsScanner() )); // Gather the list of all JSR 303 validation annotations in the project. Per the JSR 303 spec this is any // annotation class type that is marked with @Constraint. constraintAnnotationClasses = new ArrayList<>(); for (Class<?> constraintAnnotatedType : reflections.getTypesAnnotatedWith(Constraint.class, true)) { if (constraintAnnotatedType.isAnnotation()) { //noinspection unchecked constraintAnnotationClasses.add((Class<? extends Annotation>) constraintAnnotatedType); } } // We're not done gathering validation annotations though, unfortunately. JSR 303 also says that *any* // annotation (whether it is a Constraint or not) that has a value field that returns an array of actual // Constraints is treated as a "multi-value constraint", and the validation processor will run each // of the Constraints in the array as if they were declared separately on the annotated element. Therefore, // we have to dig through all the annotations in the project, find any that fall into this "multi-value // constraint" category, and include them in our calculations. for (Class<? extends Annotation> annotationClass : reflections.getSubTypesOf(Annotation.class)) { if (isMultiValueConstraintClass(annotationClass)) constraintAnnotationClasses.add(annotationClass); } // Setup the master constraint list allConstraintAnnotationsMasterList = new ArrayList<>(setupAllConstraintAnnotationsMasterList(reflections, constraintAnnotationClasses)); /* * Finally use the info we've gathered/constructed previously to populate the * {@link #projectRelevantConstraintAnnotationsExcludingUnitTestsList} field, which is the main chunk of data * that extensions of this class will care about. */ projectRelevantConstraintAnnotationsExcludingUnitTestsList = Collections.unmodifiableList( getSubAnnotationListUsingExclusionFilters(allConstraintAnnotationsMasterList, ignoreAllAnnotationsAssociatedWithTheseClasses, specificAnnotationDeclarationsExcludedFromStrictMessageRequirement)); }
/** * Determines whether the given annotation is a 'constraint' or not. * It just checks if the annotation has the {@link Constraint} annotation on it or if the annotation is the {@link * Valid} annotation. * * @param annotation the annotation to check * @return {@code true} if the given annotation is a constraint */ private static boolean isConstraint(Annotation annotation) { return annotation.annotationType().isAnnotationPresent(Constraint.class) || annotation.annotationType().equals(Valid.class); }