package com.blazebit.validation.constraint.validator;
import com.blazebit.lang.StringUtils;
import com.blazebit.reflection.ExpressionUtils;
import com.blazebit.validation.constraint.CheckCompare;
import com.blazebit.validation.constraint.ComparisonMode;
import java.util.Comparator;
import javax.validation.ConstraintValidator;
import javax.validation.ConstraintValidatorContext;
import javax.validation.ConstraintValidatorContext.ConstraintViolationBuilder;
import javax.validation.ConstraintValidatorContext.ConstraintViolationBuilder.NodeBuilderDefinedContext;
public class CheckCompareValidator implements
ConstraintValidator<CheckCompare, Object> {
private String[] propertyPaths;
private ComparisonMode comparisonMode;
private Comparator<Object> comparator;
@SuppressWarnings("unchecked")
@Override
public void initialize(CheckCompare constraintAnnotation) {
this.propertyPaths = constraintAnnotation.value();
this.comparisonMode = constraintAnnotation.mode();
if (this.propertyPaths.length < 2) {
throw new IllegalArgumentException(
"At least two property paths have to be given for the check constraint to work properly!");
}
try {
this.comparator = (Comparator<Object>) constraintAnnotation
.comparator().newInstance();
} catch (Exception e) {
throw new IllegalArgumentException(
"Could not instantiate comparator", e);
}
}
@Override
public boolean isValid(Object target, ConstraintValidatorContext context) {
boolean isValid = true;
try {
Object lastValue = ExpressionUtils.getNullSafeValue(target,
propertyPaths[0]);
for (int i = 1; i < propertyPaths.length; i++) {
Object current = ExpressionUtils.getNullSafeValue(target,
propertyPaths[i]);
if ((comparisonMode == ComparisonMode.EQUAL && comparator
.compare(lastValue, current) != 0)
|| (comparisonMode == ComparisonMode.NOT_EQUAL && comparator
.compare(lastValue, current) == 0)) {
isValid = false;
break;
}
lastValue = current;
}
if (!isValid) {
/*
* if custom message was provided, don't touch it, otherwise
* build the default message
*/
String message = context.getDefaultConstraintMessageTemplate();
message = (message.isEmpty()) ? resolveMessage() : message;
context.disableDefaultConstraintViolation();
ConstraintViolationBuilder violationBuilder = context
.buildConstraintViolationWithTemplate(message);
for (String propertyName : propertyPaths) {
NodeBuilderDefinedContext nbdc = violationBuilder
.addNode(propertyName);
nbdc.addConstraintViolation();
}
}
} catch (Exception ex) {
throw new IllegalArgumentException(ex);
}
return isValid;
}
private String resolveMessage() {
final StringBuilder sb = new StringBuilder(propertyPaths.length * 10);
sb.append('[');
StringUtils.join(sb, ", ", propertyPaths);
sb.append(']');
sb.append(" must");
switch (comparisonMode) {
case EQUAL:
sb.append(" be equal");
break;
case NOT_EQUAL:
sb.append(" not be equal");
break;
}
sb.append('.');
return sb.toString();
}
}