package nl.uva.softwcons.ql.eval; import nl.uva.softwcons.ql.ast.expression.identifier.Identifier; import nl.uva.softwcons.ql.ast.form.Form; import nl.uva.softwcons.ql.ast.form.FormVisitor; import nl.uva.softwcons.ql.ast.statement.Computable; import nl.uva.softwcons.ql.ast.statement.ComputedQuestion; import nl.uva.softwcons.ql.ast.statement.Conditional; import nl.uva.softwcons.ql.ast.statement.Question; import nl.uva.softwcons.ql.ast.statement.StatementVisitor; import nl.uva.softwcons.ql.eval.value.Value; import com.google.common.collect.ArrayListMultimap; import com.google.common.collect.Multimap; public class Evaluator implements FormVisitor<Void>, StatementVisitor<Void> { private final FormAnswers answers; private final ReferenceResolver referencesResolver; private final Multimap<Computable, ValueChangeListener<Value>> changeListeners = ArrayListMultimap.create(); public Evaluator(final Form form) { this.answers = new FormAnswers(); this.referencesResolver = new ReferenceResolver(form); form.accept(this); } public Value getValue(final Identifier variable) { return this.answers.getValue(variable); } public Value getValue(final Conditional conditional) { return ExpressionEvaluator.evaluate(conditional.getExpression(), answers); } public void updateValue(final Identifier variable, final Value value) { this.answers.setValue(variable, value); reevaluateIdentifierReferences(variable); } public void addListener(final Computable computable, final ValueChangeListener<Value> listener) { this.changeListeners.put(computable, listener); } @Override public Void visit(final Form form) { form.getStatements().forEach(st -> st.accept(this)); return null; } @Override public Void visit(final Question question) { return null; } @Override public Void visit(final ComputedQuestion question) { final Value resultValue = ExpressionEvaluator.evaluate(question.getExpression(), answers); this.answers.setValue(question.getId(), resultValue); notifyListeners(question, resultValue); return null; } @Override public Void visit(final Conditional conditional) { notifyListeners(conditional, getValue(conditional)); return null; } private void reevaluateIdentifierReferences(final Identifier variableName) { this.referencesResolver.getReferencedQuestions(variableName).forEach(q -> { q.accept(this); this.reevaluateIdentifierReferences(q.getId()); }); this.referencesResolver.getReferencedConditionals(variableName).forEach(c -> c.accept(this)); } private void notifyListeners(final Computable computable, final Value newValue) { this.changeListeners.get(computable).forEach(l -> l.processValueChange(newValue)); } }