Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Custom Constraint(Validator) for different Types

On the frontend side there's a form containing a selectbox filled with some options. The model contains a field which will be validated by my custom constraint:

private @Options(values=OptionKeys.SALUTATIONS) String salutation;

Which works great. Now I would like to have checkboxes or a multi value selectbox on the frontend side which uses the following snippet:

private @Options(values=OptionKeys.INTERESTS, optional=true) String[] interests;

Here's the constraint interface and the validator code:

@Target( { METHOD, FIELD, ANNOTATION_TYPE })
@Retention(RUNTIME)
@Constraint(validatedBy = OptionsValidator.class)
@Documented
public @interface Options {
    String message() default "{com.example.web.form.constraints.Options.message}";
    Class<? extends Payload>[] payload() default {};
    Class<?>[] groups() default {};
    boolean optional() default false;
    OptionKeys values();
}

public class OptionsValidator implements ConstraintValidator<Options, String> {

    private List<String> values;
    private boolean optional;

    @Override
    public void initialize(Options params) {
        values = Arrays.asList(params.values().data);
        optional = params.optional();
    }

    @Override
    public boolean isValid(String value, ConstraintValidatorContext constraintValidatorContext) {
        if (optional && StringUtils.isBlank(value)) {
            return true;
        }

        return value != null && values.contains(value);
    }

}

public static enum OptionKeys {
    SALUTATIONS(new String[] {
            "m",
            "f"
    }),

    INTERESTS(new String[] {
            "dummy"
    });

    public final String[] data;
    OptionKeys(String[] data) {
        this.data = data;
    }
}

Is it possible to extend the validator somehow to also check String[] values or do I have to write another one (e.g. @MultiOptions)?

like image 358
dtrunk Avatar asked Oct 27 '25 09:10

dtrunk


2 Answers

You can reuse the same annotation to validate different classes by providing more than one ConstraintValidators implementing the constraint. In the @Constraint annotation definition fill the validatedBy all applicable classes:

@Target( { METHOD, FIELD, ANNOTATION_TYPE })
@Retention(RUNTIME)
@Constraint(validatedBy = {OptionsValidator.class, OptionsStringArrayValidator.class})
@Documented
public @interface Options {
   ...
}

and create corresponding implementations:

public class OptionsValidator implements ConstraintValidator<Options, String> {...}
public class OptionsStringArrayValidator implements ConstraintValidator<Options, String[]> {...}
like image 148
Maciej Lach Avatar answered Oct 28 '25 22:10

Maciej Lach


One could use Object and check its type with instanceof:

public class OptionsValidator implements ConstraintValidator<Options, Object> {
    private List<String> values;
    private boolean optional;

    @Override
    public void initialize(Options params) {
        values = Arrays.asList(params.values().data);
        optional = params.optional();
    }

    @Override
    public boolean isValid(Object value, ConstraintValidatorContext constraintValidatorContext) {
        if (optional && value == null) {
            return true;
        }

        if (value instanceof String[]) {
            String[] valArray = (String[]) value;
            if (!optional && valArray.length == 0) {
                return false;
            }

            for (String val : valArray) {
                if (!values.contains(val)) {
                    return false;
                }
            }

            return true;
        } else if (value instanceof String) {
            String val = (String) value;
            if (optional && val.trim() == "") {
                return true;
            }

            return val != null && values.contains(val);
        }

        return false;
    }
}
like image 42
dtrunk Avatar answered Oct 29 '25 00:10

dtrunk



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!