package edu.parser.QL.evaluator;
import edu.exceptions.EvaluationException;
import edu.exceptions.TypeCheckException;
import edu.parser.QL.nodes.AbstractNode;
import edu.parser.QL.nodes.expression.*;
import edu.parser.QL.nodes.question.Question;
import edu.parser.QL.nodes.type.Number;
import java.util.List;
import java.util.Optional;
/**
* Created by Steven Kok on 12/03/2015.
*/
public class ComputedQuestionsRetriever implements ExpressionVisitor<AbstractNode> {
public static final String NOT_SUPPORTED_OPERATION_FOR_COMPUTED_QUESTIONS = "Not supported operation for computed questions: ";
private final List<Question> evaluatedQuestions;
public ComputedQuestionsRetriever(List<Question> evaluatedQuestions) {
this.evaluatedQuestions = evaluatedQuestions;
}
@Override
public AbstractNode visit(Addition addition) {
Number left = getNumber(addition.getLeft());
Number right = getNumber(addition.getRight());
return new Number(left.getNumber() + right.getNumber());
}
private Number getNumber(Expression expression) {
AbstractNode abstractNode = expression.accept(this);
if (abstractNode instanceof Number) {
return (Number) abstractNode;
} else {
throw new TypeCheckException("Invalid expression: " + expression);
}
}
@Override
public AbstractNode visit(And and) {
throw new TypeCheckException(NOT_SUPPORTED_OPERATION_FOR_COMPUTED_QUESTIONS + and);
}
@Override
public AbstractNode visit(Equal equal) {
throw new TypeCheckException(NOT_SUPPORTED_OPERATION_FOR_COMPUTED_QUESTIONS + equal);
}
@Override
public AbstractNode visit(GreaterOrEqual greaterOrEqual) {
throw new TypeCheckException(NOT_SUPPORTED_OPERATION_FOR_COMPUTED_QUESTIONS + greaterOrEqual);
}
@Override
public AbstractNode visit(GreaterThan greaterThan) {
throw new TypeCheckException(NOT_SUPPORTED_OPERATION_FOR_COMPUTED_QUESTIONS + greaterThan);
}
@Override
public AbstractNode visit(QLIdentifier qlIdentifier) {
Question question = getQuestion(qlIdentifier);
return question.getValue();
}
private Question getQuestion(QLIdentifier qlIdentifier) {
Optional<Question> question = evaluatedQuestions.stream()
.filter(evaluatedQuestion -> evaluatedQuestion.getQLIdentifier().equals(qlIdentifier))
.findFirst();
if (question.isPresent()) {
return question.get();
} else {
throw new EvaluationException("There is no question with identifier: " + qlIdentifier);
}
}
@Override
public AbstractNode visit(LessOrEqual lessOrEqual) {
throw new TypeCheckException(NOT_SUPPORTED_OPERATION_FOR_COMPUTED_QUESTIONS + lessOrEqual);
}
@Override
public AbstractNode visit(LessThan lessThan) {
throw new TypeCheckException(NOT_SUPPORTED_OPERATION_FOR_COMPUTED_QUESTIONS + lessThan);
}
@Override
public AbstractNode visit(Multiplication multiplication) {
Number left = getNumber(multiplication.getLeft());
Number right = getNumber(multiplication.getRight());
return new Number(left.getNumber() * right.getNumber());
}
@Override
public AbstractNode visit(Not not) {
throw new TypeCheckException(NOT_SUPPORTED_OPERATION_FOR_COMPUTED_QUESTIONS + not);
}
@Override
public AbstractNode visit(NotEqual notEqual) {
throw new TypeCheckException(NOT_SUPPORTED_OPERATION_FOR_COMPUTED_QUESTIONS + notEqual);
}
@Override
public AbstractNode visit(Or or) {
throw new TypeCheckException(NOT_SUPPORTED_OPERATION_FOR_COMPUTED_QUESTIONS + or);
}
@Override
public AbstractNode visit(Division division) {
Number left = getNumber(division.getLeft());
Number right = getNumber(division.getRight());
if (bothZero(left, right)) {
return new Number(0);
} else if (onlyLeftZero(left, right)) {
return new Number(right.getNumber());
} else if (onlyRightZero(left, right)) {
return new Number(left.getNumber());
} else {
return new Number(left.getNumber() / right.getNumber());
}
}
private boolean onlyRightZero(Number left, Number right) {
return isOnlyFirstParameterZero(right, left);
}
private boolean onlyLeftZero(Number left, Number right) {
return isOnlyFirstParameterZero(left, right);
}
private boolean isOnlyFirstParameterZero(Number zeroValue, Number nonZeroValue) {
return zeroValue.getNumber() == 0 && nonZeroValue.getNumber() > 0;
}
private boolean bothZero(Number left, Number right) {
return left.getNumber() == 0 && right.getNumber() == 0;
}
@Override
public AbstractNode visit(edu.parser.QL.nodes.type.Boolean aBoolean) {
throw new TypeCheckException(NOT_SUPPORTED_OPERATION_FOR_COMPUTED_QUESTIONS + aBoolean);
}
@Override
public AbstractNode visit(Number number) {
return number;
}
@Override
public AbstractNode visit(Expression expression) {
return expression.accept(this);
}
}