package org.openstack.atlas.api.validation;
import org.openstack.atlas.api.validation.expectation.*;
import org.openstack.atlas.api.validation.util.CallStateRegistry;
import java.lang.reflect.Method;
import java.util.LinkedList;
import java.util.List;
import org.openstack.atlas.api.validation.exceptions.UnfinishedExpectationChainException;
import net.sf.cglib.proxy.Enhancer;
public class ValidatorBuilder<ObjectTypeToValidate> {
private final List<ExpectationTarget<ObjectTypeToValidate>> validationTargetList;
private final CallStateRegistry sharedCallState;
private final ObjectTypeToValidate validationTarget;
private volatile int currentExpectationId;
public ValidatorBuilder(Class<ObjectTypeToValidate> type) {
validationTargetList = new LinkedList<ExpectationTarget<ObjectTypeToValidate>>();
sharedCallState = new CallStateRegistry();
currentExpectationId = 0;
final Enhancer eh = new Enhancer();
eh.setSuperclass(type);
eh.setCallback(new ValidationInterceptor(sharedCallState));
validationTarget = (ObjectTypeToValidate) eh.create();
}
protected ObjectTypeToValidate validationTarget() {
return validationTarget;
}
protected OngoingExpectation must() {
final Expectation freshExpectation = new Expectation(++currentExpectationId);
synchronized (validationTargetList) {
//Null - No target means root
addExpectationToExpectationTarget(null, freshExpectation);
}
return freshExpectation.must();
}
protected OngoingExpectation if_() {
final Expectation freshExpectation = new Expectation(++currentExpectationId);
synchronized (validationTargetList) {
//Null - No target means root
addExpectationToExpectationTarget(null, freshExpectation);
}
return freshExpectation.if_();
}
protected <R> ConditionalExpectation result(R methodCallResult) {
final Method callResultBelongsTo = sharedCallState.getLastKnownCall();
if (callResultBelongsTo == null) {
throw new IllegalArgumentException("Method call handed to expectation builder is not trackable. Please use the object being validated.");
} else if (callResultBelongsTo.getParameterTypes().length > 0) {
throw new IllegalArgumentException("Methods with parameters are not eligible for validation");
}
final Expectation freshExpectation = new Expectation(++currentExpectationId);
addExpectationToExpectationTarget(callResultBelongsTo, freshExpectation);
return freshExpectation;
}
private synchronized void addExpectationToExpectationTarget(final Method callResultBelongsTo, final Expectation freshExpectation) {
boolean foundExistingTarget = false;
for (ExpectationTarget<ObjectTypeToValidate> existingTarget : validationTargetList) {
if (existingTarget.targets(callResultBelongsTo)) {
existingTarget.addExpectation(freshExpectation);
foundExistingTarget = true;
break;
}
}
if (!foundExistingTarget) {
final ExpectationTarget<ObjectTypeToValidate> newTarget = new ExpectationTarget<ObjectTypeToValidate>(callResultBelongsTo);
newTarget.addExpectation(freshExpectation);
validationTargetList.add(newTarget);
}
}
private synchronized void validateSelf() {
final StringBuilder strBuff = new StringBuilder("Broken expectations:");
boolean validationChainOk = true;
for (ExpectationTarget<ObjectTypeToValidate> targetToCheck : validationTargetList) {
final SelfValidationResult selfValidationResult = targetToCheck.isSatisfied();
validationChainOk = selfValidationResult.isValid();
strBuff.append(selfValidationResult.getErrorBuffer());
}
if (!validationChainOk) {
throw new UnfinishedExpectationChainException(strBuff.toString());
}
}
public static <R, S> Validator<R> build(ValidatorBuilder<R> builder) {
return builder.toValidator();
}
public Validator<ObjectTypeToValidate> toValidator() {
validateSelf();
return new ValidatorImpl<ObjectTypeToValidate>(validationTargetList);
}
}