dtrunk
dtrunk

Reputation: 4805

Create custom hibernate Validator based on existing Hibernate Validator

I would like to create a custom constraint @LengthIfNotBlank(min=,max=) based on Hibernates @Length constraint:

@Target( { METHOD, FIELD, ANNOTATION_TYPE })
@Retention(RUNTIME)
@Constraint(validatedBy = LengthIfNotBlankValidator.class)
@Documented
public @interface LengthIfNotBlank {
    String message() default "{com.mycompany.LengthIfNotBlank}";
    Class<? extends Payload>[] payload() default {};
    int max() default Integer.MAX_VALUE;
    Class<?>[] groups() default {};
    int min() default 0;
}

public class LengthIfNotBlankValidator extends LengthValidator {
    @Override
    public boolean isValid(String value, ConstraintValidatorContext constraintValidatorContext) {
        return StringUtils.isBlank(value) || super.isValid(value, constraintValidatorContext);
    }
}

Exception:

[java] javax.validation.ValidationException: Unable to initialize com.example.constraints.LengthIfNotBlankValidator
[java] Caused by: java.lang.ClassCastException: $Proxy32 cannot be cast to org.hibernate.validator.constraints.Length

UPDATE

Still getting this exception :-(

public class LengthIfNotBlankValidator extends LengthValidator {
    public void initialize(LengthIfNotBlank parameters) {
        Map<String,Object> elements = new HashMap<String,Object>();
        elements.put("message", parameters.message());
        elements.put("payload", parameters.payload());
        elements.put("groups", parameters.groups());
        elements.put("min", parameters.min());
        elements.put("max", parameters.max());

        AnnotationDescriptor<Length> descriptor = AnnotationDescriptor.getInstance(Length.class, elements);
        Length length = AnnotationFactory.create(descriptor);
        super.initialize(length);
    }

    @Override
    public boolean isValid(String value, ConstraintValidatorContext constraintValidatorContext) {
        return StringUtils.isBlank(value) || super.isValid(value, constraintValidatorContext);
    }
}

Upvotes: 5

Views: 6835

Answers (2)

Didier L
Didier L

Reputation: 20579

I think you should not bother with that: Hibernate's LengthValidator is really simple and whatever you do, you will end up with more code that if you had just reimplemented the code yourself like this:

public class LengthIfNotBlankValidator implements ConstraintValidator<LengthIfNotBlank, String> {
    private int min;
    private int max;

    public void initialize(LengthIfNotBlank parameters) {
        min = parameters.min();
        max = parameters.max();
        validateParameters();
    }

    public boolean isValid(String value, ConstraintValidatorContext constraintValidatorContext) {
        if ( StringUtils.isBlank(value) ) {
            return true;
        }
        int length = value.length();
        return length >= min && length <= max;
    }

    private void validateParameters() {
        if ( min < 0 ) {
            throw new IllegalArgumentException( "The min parameter cannot be negative." );
        }
        if ( max < 0 ) {
            throw new IllegalArgumentException( "The max parameter cannot be negative." );
        }
        if ( max < min ) {
            throw new IllegalArgumentException( "The max cannot be less than the min." );
        }
    }
}

(This is just Hibernate's code adapted for your particular case).

Also, I don't think it would be possible to extend LengthValidator while implementing ConstraintValidator<LengthIfNotBlank, String>, which is mandatory for your validator.

Notice however that if you want to re-use unparameterized validators (or validators with constant parameters), you could simply annotate your validator with the re-used one (and maybe add @ReportAsSingleViolation)

Upvotes: 1

axtavt
axtavt

Reputation: 242686

Since annotation types cannot be inherited, you should override initialize() and pass an actual instance of type Length to super.initialize().

Note that you cannot create an instance of annotation type directly, therefore you have to use something like AnnotationFactory.

Also I'm not sure that compiler would allow you to do it due to type safety constraints. If it doesn't, use composition instead of inheritance (i.e. create a field of type LengthValidator).

Upvotes: 2

Related Questions