package org.raidenjpa.query.executor;
import java.util.List;
import java.util.Map;
import java.util.Stack;
import org.raidenjpa.query.parser.Condition;
import org.raidenjpa.query.parser.LogicExpression;
import org.raidenjpa.query.parser.LogicExpressionElement;
import org.raidenjpa.query.parser.LogicOperator;
import org.raidenjpa.util.BadSmell;
@BadSmell("Should we put it in LogicExpression")
public class LogicExpressionExecutor {
@BadSmell("It is a field, but match is that one which set it")
private Stack<Element> stack;
private LogicExpression logicExpression;
private Map<String, Object> parameters;
public LogicExpressionExecutor(LogicExpression logicExpression, Map<String, Object> parameters) {
this.parameters = parameters;
this.logicExpression = logicExpression;
}
public boolean match(QueryResultRow row, boolean executingWhere) {
initStack();
for (LogicExpressionElement element : logicExpression.getElements()) {
WhereStackAction action = push(element);
if (action == WhereStackAction.RESOLVE) {
resolve(row, executingWhere);
} else if (action == WhereStackAction.REDUCE) {
reduce(row, executingWhere);
}
}
return getResult();
}
void initStack() {
stack = new Stack<Element>();
}
WhereStackAction push(LogicExpressionElement element) {
stack.push(new Element(element));
if (element.isLogicOperator()) {
return pushLogicOperator((LogicOperator) element);
} else if (element.isExpression()) {
return pushExpression((Condition) element);
} else {
throw new RuntimeException("Element must be a logicOperator or an expression");
}
}
private WhereStackAction pushExpression(Condition element) {
if (isThereOperatorBefore()) {
return WhereStackAction.REDUCE;
} else {
return WhereStackAction.RESOLVE;
}
}
private WhereStackAction pushLogicOperator(LogicOperator element) {
if (stack.size() == 1) {
throw new RuntimeException("First element must be a expression");
}
return WhereStackAction.NOTHING;
}
private boolean isThereOperatorBefore() {
Element previousElement = getPreviousElement();
if (previousElement == null) {
return false;
} else {
return previousElement.isLogicOperator();
}
}
private Element getPreviousElement() {
if (stack.size() == 1) {
return null;
}
return stack.get(stack.size() - 2);
}
void resolve(QueryResultRow row, boolean executingWhere) {
Condition condition = (Condition) stack.pop().getRaw();
boolean match = condition.match(row, parameters, executingWhere);
stack.push(new Element(match));
}
void reduce(QueryResultRow row, boolean executingWhere) {
resolve(row, executingWhere);
Boolean firstResult = (Boolean) stack.pop().getRaw();
LogicOperator logicOperator = (LogicOperator) stack.pop().getRaw();
Boolean secondResult = (Boolean) stack.pop().getRaw();
Boolean result = logicOperator.evaluate(firstResult, secondResult);
stack.push(new Element(result));
}
boolean getResult() {
if (stack.size() != 1) {
throw new RuntimeException("The stack has more than one element");
}
if (!(stack.get(0).getRaw() instanceof Boolean)) {
throw new RuntimeException("The result element is not a Boolean");
}
return (Boolean) stack.get(0).getRaw();
}
public int size() {
return stack.size();
}
@BadSmell("It is not so beautiful, but at least is isolated")
private class Element {
// Boolean (when resolved) || WhereLogicOperator || WhereExpression
private Object raw;
Element(Object element) {
if (element instanceof LogicExpressionElement || element instanceof List || element instanceof Boolean) {
this.raw = element;
} else {
throw new RuntimeException("Only WhereElement or List is acceptable");
}
}
boolean isLogicOperator() {
return raw instanceof LogicOperator;
}
Object getRaw() {
return raw;
}
}
}