Использование GroupSequence и порядка одной и той же пользовательской аннотации JRS-303 для класса и поля - PullRequest
0 голосов
/ 16 ноября 2018

Я использую спецификацию JSR-380 в проекте весенней загрузки для проверки запроса API остальных.

У меня есть пользовательская аннотация, которую я использую как для класса, так и для поля. Поскольку в классе может быть один и тот же валидатор, а также поле для одного и того же поля с разными сообщениями, я хочу определить groupSequence для собственного валидатора. Я хочу проверить валидацию поля, только если валидация класса прошла успешно, и наоборот, если последовательность обратная. Вы можете увидеть, как я пытаюсь использовать groupSequence, предположим, что все интерфейсы Default и другие доступны.

Я получаю следующую ошибку в другом сценарии:

1. Ошибка:

"message": "class javax.validation.GroupDefinitionException
    HV000053: 'Default.class' cannot appear in default group sequence
    list."

когда я использую

@GroupSequence({Default.class, ClassFirst.class, FieldSecond.class})

2. Ошибка:

"message": "class javax.validation.GroupDefinitionException
    HV000054: package.Request must be part of the
    redefined default group sequence."

когда я использую

@GroupSequence({ClassFirst.class, FieldSecond.class})

Мой код выглядит так:

Аннотация:

@Documented
@Constraint(validatedBy = { SpecificValueValidator.class })
@Target({ ElementType.FIELD, ElementType.TYPE_USE, ElementType.TYPE_PARAMETER })
@Retention(RetentionPolicy.RUNTIME)
@ReportAsSingleViolation
public @interface SpecificValue {
    String message() default "{xyz.util.SpecificValue}";

    BusinessErrors enumMessage();

    Class<?>[] groups() default {};

    Class<? extends Payload>[] payload() default {};

    String fieldName() default StringUtils.EMPTY;

    String value() default StringUtils.EMPTY;

    String pattern() default StringUtils.EMPTY;

    boolean isNullable() default false;

    @Target({ ElementType.FIELD, ElementType.TYPE_USE, ElementType.TYPE_PARAMETER })
    @Retention(RetentionPolicy.RUNTIME)
    @Documented
    @interface List {
        SpecificValue[] value();
    }
}

Валидатор:

public class SpecificValueValidator implements ConstraintValidator<SpecificValue, Object> {
    private String fieldName;
    private String value;
    private String pattern;
    private boolean isNullable;

    @Override
    public void initialize(SpecificValue annotation) {
        this.fieldName = annotation.fieldName();
        this.value = annotation.value();
        this.pattern = annotation.pattern();
        this.isNullable = annotation.isNullable();
    }

    @Override
    public boolean isValid(Object object, ConstraintValidatorContext ctx) {
        boolean isValid = true;
        Object fieldValue;
        try {
            if (StringUtils.isNotBlank(fieldName)) {
                fieldValue = BeanUtils.getProperty(object, fieldName);
            } else {
                fieldValue = object;
            }
        } catch(NestedNullException | NullPointerException ex) {
            fieldValue = null;
        }catch (NoSuchMethodException | InvocationTargetException | IllegalAccessException ex) {
            throw new RuntimeException(ex);
        }

        if (fieldValue == null && BooleanUtils.isTrue(isNullable)) {
            isValid = true;
        } else if (fieldValue instanceof String && StringUtils.isBlank((String) fieldValue) && BooleanUtils.isTrue(isNullable)) {
            isValid = true;
        } else {
            //fieldValue should not be null or blank else return isValid
            isValid = fieldValue instanceof String ? StringUtils.isNotBlank((String) fieldValue) : fieldValue != null;
            //fieldValue should match with specific value else return isValid
            isValid = fieldValue instanceof String && StringUtils.isNotBlank(value) ? StringUtils.equalsIgnoreCase(value, (String) fieldValue): isValid;
            //feildValue should match with specific pattern else return isValid
            isValid = fieldValue instanceof String && StringUtils.isNotBlank(pattern) ? ((String) fieldValue).matches(pattern) : isValid;
            //if specific value contains multiple value separated by comma
            if(StringUtils.isNotBlank(value)) {
                List<String> values = Arrays.asList(value.toLowerCase().split("\\s*,\\s*"));
                isValid = fieldValue instanceof String ? values.contains(((String) fieldValue).toLowerCase()) : isValid;
            }
        }
        //if isValid is false mark corresponding field as dirty
        if (BooleanUtils.isFalse(isValid) && StringUtils.isNotBlank(fieldName)) {
            ctx.disableDefaultConstraintViolation();
            ctx.buildConstraintViolationWithTemplate(ctx.getDefaultConstraintMessageTemplate())
                    .addPropertyNode(fieldName)
                    .addBeanNode()
                    .addConstraintViolation();
        }
        return isValid;
    }
}

Запрос:

@GroupSequence({Default.class, ClassFirst.class, FieldSecond.class})
@SpecificValue.List({
        @SpecificValue(enumMessage = BusinessErrors.XYZ1_NAME_TYPE_SHOULD_BE_I, fieldName = "xyz1.personName.nameTypeInd", value = "I")
    })

public class Request implements Serializable {
    @Valid
    @NotNullable(enumMessage = RequiredFieldErrors.XYZ1_INFO)
    private PersonType xyz1;
    @Valid
    @NotNullable(enumMessage = RequiredFieldErrors.XYZ2_INFO)
    private PersonType xyz2;

}
public class PersonType implements Serializable {
    @Valid
    @NotNullable(enumMessage = RequiredFieldErrors.PERSON_NAME_R)
    private PersonName personName;
    @Valid
    @NotNullable(enumMessage = RequiredFieldErrors.PERSON_NAME_R)
    private PersonAddress personAddress;
}

public class PersonName implements Serializable {
    @SpecificValue(enumMessage = BusinessErrors.NAME_TYPE_INIDICATOR, value = ConstantValue.NAME_TYPE_VALUES, groups=FieldSecond.class) // Here nameType can be B, I for xyz2 and others, however, only I for xyz1
    private String nameTypeInd;
}
...