mirror of
https://github.com/ebean-orm/ebean.git
synced 2024-04-21 10:51:47 +00:00
Initial refactor to extract EqlWhereListener
This commit is contained in:
@@ -1,25 +1,15 @@
|
||||
package io.ebeaninternal.server.grammer;
|
||||
|
||||
import io.ebean.Expression;
|
||||
import io.ebean.ExpressionFactory;
|
||||
import io.ebean.ExpressionList;
|
||||
import io.ebean.FetchConfig;
|
||||
import io.ebean.LikeType;
|
||||
import io.ebean.OrderBy;
|
||||
import io.ebeaninternal.api.SpiQuery;
|
||||
import io.ebeaninternal.server.grammer.antlr.EQLBaseListener;
|
||||
import io.ebeaninternal.server.grammer.antlr.EQLLexer;
|
||||
import io.ebeaninternal.server.grammer.antlr.EQLParser;
|
||||
import io.ebeaninternal.server.util.ArrayStack;
|
||||
import org.antlr.v4.runtime.ParserRuleContext;
|
||||
import org.antlr.v4.runtime.tree.ParseTree;
|
||||
import org.antlr.v4.runtime.tree.TerminalNode;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
class EqlAdapter<T> extends EQLBaseListener {
|
||||
|
||||
private static final OperatorMapping operatorMapping = new OperatorMapping();
|
||||
class EqlAdapter<T> extends EqlWhereListener<T> {
|
||||
|
||||
private static final String DISTINCT = "distinct";
|
||||
|
||||
@@ -29,27 +19,28 @@ class EqlAdapter<T> extends EQLBaseListener {
|
||||
|
||||
private final SpiQuery<T> query;
|
||||
|
||||
private final EqlAdapterHelper helper;
|
||||
private final ExpressionFactory expressionFactory;
|
||||
|
||||
private ArrayStack<ExpressionList<T>> textStack;
|
||||
|
||||
private ArrayStack<ExpressionList<T>> whereStack;
|
||||
|
||||
private boolean textMode;
|
||||
|
||||
private List<Object> inValues;
|
||||
|
||||
private String inPropertyName;
|
||||
|
||||
public EqlAdapter(SpiQuery<T> query) {
|
||||
EqlAdapter(SpiQuery<T> query) {
|
||||
this.query = query;
|
||||
this.helper = new EqlAdapterHelper(this);
|
||||
this.expressionFactory = query.getExpressionFactory();
|
||||
}
|
||||
|
||||
@Override
|
||||
ExpressionFactory expressionFactory() {
|
||||
return expressionFactory;
|
||||
}
|
||||
|
||||
@Override
|
||||
Object namedParam(String parameterName) {
|
||||
return query.createNamedParameter(parameterName);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the current expression list that expressions should be added to.
|
||||
*/
|
||||
protected ExpressionList<T> peekExprList() {
|
||||
@Override
|
||||
ExpressionList<T> peekExprList() {
|
||||
|
||||
if (textMode) {
|
||||
// return the current text expression list
|
||||
@@ -74,28 +65,6 @@ class EqlAdapter<T> extends EQLBaseListener {
|
||||
return textStack.peek();
|
||||
}
|
||||
|
||||
/**
|
||||
* Push the expression list onto the appropriate stack.
|
||||
*/
|
||||
private void pushExprList(ExpressionList<T> list) {
|
||||
if (textMode) {
|
||||
textStack.push(list);
|
||||
} else {
|
||||
whereStack.push(list);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* End a list of expressions added by 'OR'.
|
||||
*/
|
||||
private void popJunction() {
|
||||
if (textMode) {
|
||||
textStack.pop();
|
||||
} else {
|
||||
whereStack.pop();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void enterSelect_clause(EQLParser.Select_clauseContext ctx) {
|
||||
|
||||
@@ -199,233 +168,4 @@ class EqlAdapter<T> extends EQLBaseListener {
|
||||
}
|
||||
}
|
||||
|
||||
private String getLeftHandSidePath(ParserRuleContext ctx) {
|
||||
TerminalNode pathToken = ctx.getToken(EQLLexer.PATH_VARIABLE, 0);
|
||||
return pathToken.getText();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void enterInrange_expression(EQLParser.Inrange_expressionContext ctx) {
|
||||
checkChildren(ctx, 5);
|
||||
String path = getLeftHandSidePath(ctx);
|
||||
EqlOperator op = getOperator(ctx);
|
||||
if (op != EqlOperator.INRANGE) {
|
||||
throw new IllegalStateException("Expecting INRANGE operator but got " + op);
|
||||
}
|
||||
helper.addInRange(path, child(ctx, 2), child(ctx, 4));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void enterBetween_expression(EQLParser.Between_expressionContext ctx) {
|
||||
|
||||
checkChildren(ctx, 5);
|
||||
String path = getLeftHandSidePath(ctx);
|
||||
EqlOperator op = getOperator(ctx);
|
||||
if (op != EqlOperator.BETWEEN) {
|
||||
throw new IllegalStateException("Expecting BETWEEN operator but got " + op);
|
||||
}
|
||||
helper.addBetween(path, child(ctx, 2), child(ctx, 4));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void enterPropertyBetween_expression(EQLParser.PropertyBetween_expressionContext ctx) {
|
||||
checkChildren(ctx, 5);
|
||||
String rawValue = child(ctx, 0);
|
||||
EqlOperator op = getOperator(ctx);
|
||||
if (op != EqlOperator.BETWEEN) {
|
||||
throw new IllegalStateException("Expecting BETWEEN operator but got " + op);
|
||||
}
|
||||
helper.addBetweenProperty(rawValue, child(ctx, 2), child(ctx, 4));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void enterIn_expression(EQLParser.In_expressionContext ctx) {
|
||||
this.inValues = new ArrayList<>();
|
||||
this.inPropertyName = getLeftHandSidePath(ctx);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void enterIn_value(EQLParser.In_valueContext ctx) {
|
||||
int childCount = ctx.getChildCount();
|
||||
for (int i = 0; i < childCount; i++) {
|
||||
String text = child(ctx, i);
|
||||
if (isValue(text)) {
|
||||
inValues.add(helper.bind(text));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private String child(ParserRuleContext ctx, int position) {
|
||||
ParseTree child = ctx.getChild(position);
|
||||
return child.getText();
|
||||
}
|
||||
|
||||
private boolean isValue(String text) {
|
||||
return text.length() != 1 || (!text.equals("(") && !text.equals(")") && !text.equals(","));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void exitIn_expression(EQLParser.In_expressionContext ctx) {
|
||||
helper.addIn(inPropertyName, inValues);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void enterIsNull_expression(EQLParser.IsNull_expressionContext ctx) {
|
||||
String path = getLeftHandSidePath(ctx);
|
||||
peekExprList().isNull(path);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void enterIsNotNull_expression(EQLParser.IsNotNull_expressionContext ctx) {
|
||||
String path = getLeftHandSidePath(ctx);
|
||||
peekExprList().isNotNull(path);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void enterIsEmpty_expression(EQLParser.IsEmpty_expressionContext ctx) {
|
||||
String path = getLeftHandSidePath(ctx);
|
||||
peekExprList().isEmpty(path);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void enterIsNotEmpty_expression(EQLParser.IsNotEmpty_expressionContext ctx) {
|
||||
String path = getLeftHandSidePath(ctx);
|
||||
peekExprList().isNotEmpty(path);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void enterLike_expression(EQLParser.Like_expressionContext ctx) {
|
||||
addExpression(ctx);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void enterComparison_expression(EQLParser.Comparison_expressionContext ctx) {
|
||||
addExpression(ctx);
|
||||
}
|
||||
|
||||
private void addExpression(ParserRuleContext ctx) {
|
||||
int childCount = ctx.getChildCount();
|
||||
if (childCount < 3) {
|
||||
throw new IllegalStateException("expecting 3 children for comparison? " + ctx);
|
||||
}
|
||||
String operator = child(ctx, 1);
|
||||
EqlOperator op = operatorMapping.get(operator);
|
||||
if (op == null) {
|
||||
throw new IllegalStateException("No operator found for " + operator);
|
||||
}
|
||||
String path = getLeftHandSidePath(ctx);
|
||||
String rhs = child(ctx, 2);
|
||||
if (path.equals(rhs)) {
|
||||
// the 'value operator path' form
|
||||
// invert the operator and use LHS as RHS
|
||||
op = invert(op);
|
||||
rhs = child(ctx, 0);
|
||||
}
|
||||
|
||||
// RHS is Path, Literal or Named input parameter
|
||||
helper.addExpression(path, op, rhs);
|
||||
}
|
||||
|
||||
private EqlOperator invert(EqlOperator op) {
|
||||
switch (op) {
|
||||
// no change
|
||||
case EQ:
|
||||
return EqlOperator.EQ;
|
||||
case IEQ:
|
||||
return EqlOperator.IEQ;
|
||||
case INE:
|
||||
return EqlOperator.INE;
|
||||
case NE:
|
||||
return EqlOperator.NE;
|
||||
// invert
|
||||
case LT:
|
||||
return EqlOperator.GT;
|
||||
case LTE:
|
||||
return EqlOperator.GTE;
|
||||
case GT:
|
||||
return EqlOperator.LT;
|
||||
case GTE:
|
||||
return EqlOperator.LTE;
|
||||
default:
|
||||
throw new IllegalStateException("Can not invert operator " + op);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void enterConditional_term(EQLParser.Conditional_termContext ctx) {
|
||||
int childCount = ctx.getChildCount();
|
||||
if (childCount > 1) {
|
||||
pushExprList(peekExprList().and());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void exitConditional_term(EQLParser.Conditional_termContext ctx) {
|
||||
if (ctx.getChildCount() > 1) {
|
||||
popJunction();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void enterConditional_expression(EQLParser.Conditional_expressionContext ctx) {
|
||||
if (ctx.getChildCount() > 1) {
|
||||
pushExprList(peekExprList().or());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void exitConditional_expression(EQLParser.Conditional_expressionContext ctx) {
|
||||
if (ctx.getChildCount() > 1) {
|
||||
popJunction();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void enterConditional_factor(EQLParser.Conditional_factorContext ctx) {
|
||||
if (ctx.getChildCount() > 1) {
|
||||
pushExprList(peekExprList().not());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void exitConditional_factor(EQLParser.Conditional_factorContext ctx) {
|
||||
if (ctx.getChildCount() > 1) {
|
||||
popJunction();
|
||||
}
|
||||
}
|
||||
|
||||
private EqlOperator getOperator(ParserRuleContext ctx) {
|
||||
String operator = child(ctx, 1);
|
||||
EqlOperator op = operatorMapping.get(operator);
|
||||
if (op == null) {
|
||||
throw new IllegalStateException("No operator found for " + operator);
|
||||
}
|
||||
return op;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check for the minimum number of children.
|
||||
*/
|
||||
private void checkChildren(ParserRuleContext ctx, int min) {
|
||||
if (ctx.getChildCount() < min) {
|
||||
throw new IllegalStateException("expecting " + min + " children for comparison but got " + ctx.getChildCount());
|
||||
}
|
||||
}
|
||||
|
||||
public Object namedParam(String parameterName) {
|
||||
return query.createNamedParameter(parameterName);
|
||||
}
|
||||
|
||||
public Expression like(boolean caseInsensitive, LikeType likeType, String property, Object bindValue) {
|
||||
return query.getExpressionFactory().like(property, bindValue, caseInsensitive, likeType);
|
||||
}
|
||||
|
||||
public Expression ieq(String property, Object bindValue) {
|
||||
return query.getExpressionFactory().ieqObject(property, bindValue);
|
||||
}
|
||||
|
||||
public Expression ine(String property, Object bindValue) {
|
||||
return query.getExpressionFactory().ineObject(property, bindValue);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,148 +0,0 @@
|
||||
package io.ebeaninternal.server.grammer;
|
||||
|
||||
import io.ebean.ExpressionList;
|
||||
import io.ebean.LikeType;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.util.List;
|
||||
|
||||
class EqlAdapterHelper {
|
||||
|
||||
private final EqlAdapter<?> owner;
|
||||
|
||||
public EqlAdapterHelper(EqlAdapter<?> owner) {
|
||||
this.owner = owner;
|
||||
}
|
||||
|
||||
enum ValueType {
|
||||
NAMED_PARAM,
|
||||
STRING,
|
||||
BOOL,
|
||||
NUMBER
|
||||
}
|
||||
|
||||
private ValueType getValueType(String valueAsText) {
|
||||
|
||||
char firstChar = Character.toLowerCase(valueAsText.charAt(0));
|
||||
switch (firstChar) {
|
||||
case ':':
|
||||
return ValueType.NAMED_PARAM;
|
||||
case 't':
|
||||
return ValueType.BOOL;
|
||||
case 'f':
|
||||
return ValueType.BOOL;
|
||||
case '\'':
|
||||
return ValueType.STRING;
|
||||
default:
|
||||
if (Character.isDigit(firstChar)) {
|
||||
return ValueType.NUMBER;
|
||||
}
|
||||
throw new IllegalArgumentException("Unexpected first character in value [" + valueAsText + "]");
|
||||
}
|
||||
}
|
||||
|
||||
protected void addBetweenProperty(String rawValue, String lowProperty, String highProperty) {
|
||||
peekExprList().betweenProperties(lowProperty, highProperty, bind(rawValue));
|
||||
}
|
||||
|
||||
protected void addBetween(String path, String value1, String value2) {
|
||||
peekExprList().between(path, bind(value1), bind(value2));
|
||||
}
|
||||
|
||||
protected void addInRange(String path, String value1, String value2) {
|
||||
peekExprList().inRange(path, bind(value1), bind(value2));
|
||||
}
|
||||
|
||||
protected void addIn(String path, List<Object> inValues) {
|
||||
peekExprList().in(path, inValues);
|
||||
}
|
||||
|
||||
protected void addExpression(String path, EqlOperator op, String value) {
|
||||
|
||||
switch (op) {
|
||||
case EQ:
|
||||
peekExprList().eq(path, bind(value));
|
||||
break;
|
||||
case IEQ:
|
||||
peekExprList().add(owner.ieq(path, bind(value)));
|
||||
break;
|
||||
case NE:
|
||||
peekExprList().ne(path, bind(value));
|
||||
break;
|
||||
case INE:
|
||||
peekExprList().add(owner.ine(path, bind(value)));
|
||||
break;
|
||||
case GT:
|
||||
peekExprList().gt(path, bind(value));
|
||||
break;
|
||||
case LT:
|
||||
peekExprList().lt(path, bind(value));
|
||||
break;
|
||||
case GTE:
|
||||
peekExprList().ge(path, bind(value));
|
||||
break;
|
||||
case LTE:
|
||||
peekExprList().le(path, bind(value));
|
||||
break;
|
||||
case LIKE:
|
||||
addLike(false, LikeType.RAW, path, bind(value));
|
||||
break;
|
||||
case CONTAINS:
|
||||
addLike(false, LikeType.CONTAINS, path, bind(value));
|
||||
break;
|
||||
case STARTS_WITH:
|
||||
addLike(false, LikeType.STARTS_WITH, path, bind(value));
|
||||
break;
|
||||
case ENDS_WITH:
|
||||
addLike(false, LikeType.ENDS_WITH, path, bind(value));
|
||||
break;
|
||||
case ILIKE:
|
||||
addLike(true, LikeType.RAW, path, bind(value));
|
||||
break;
|
||||
case ICONTAINS:
|
||||
addLike(true, LikeType.CONTAINS, path, bind(value));
|
||||
break;
|
||||
case ISTARTS_WITH:
|
||||
addLike(true, LikeType.STARTS_WITH, path, bind(value));
|
||||
break;
|
||||
case IENDS_WITH:
|
||||
addLike(true, LikeType.ENDS_WITH, path, bind(value));
|
||||
break;
|
||||
default:
|
||||
throw new IllegalStateException("Unhandled operator " + op);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private void addLike(boolean caseInsensitive, LikeType likeType, String path, Object bindValue) {
|
||||
peekExprList().add(owner.like(caseInsensitive, likeType, path, bindValue));
|
||||
}
|
||||
|
||||
protected Object bind(String value) {
|
||||
ValueType valueType = getValueType(value);
|
||||
return getBindValue(valueType, value);
|
||||
}
|
||||
|
||||
private ExpressionList<?> peekExprList() {
|
||||
return owner.peekExprList();
|
||||
}
|
||||
|
||||
private Object getBindValue(ValueType valueType, String value) {
|
||||
switch (valueType) {
|
||||
case BOOL:
|
||||
return Boolean.parseBoolean(value);
|
||||
case NUMBER:
|
||||
return new BigDecimal(value);
|
||||
case STRING:
|
||||
return unquote(value);
|
||||
case NAMED_PARAM:
|
||||
return owner.namedParam(value.substring(1));
|
||||
default:
|
||||
throw new IllegalArgumentException("Unhandled valueType " + valueType);
|
||||
}
|
||||
}
|
||||
|
||||
private String unquote(String value) {
|
||||
return value.substring(1, value.length() - 1);
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,7 @@
|
||||
package io.ebeaninternal.server.grammer;
|
||||
|
||||
import io.ebean.ExpressionFactory;
|
||||
import io.ebean.ExpressionList;
|
||||
import io.ebeaninternal.api.SpiQuery;
|
||||
import io.ebeaninternal.server.grammer.antlr.EQLLexer;
|
||||
import io.ebeaninternal.server.grammer.antlr.EQLParser;
|
||||
@@ -22,20 +24,21 @@ public class EqlParser {
|
||||
*/
|
||||
public static <T> void parse(String raw, SpiQuery<T> query) {
|
||||
|
||||
EQLLexer lexer = new EQLLexer(CharStreams.fromString(raw));
|
||||
CommonTokenStream tokens = new CommonTokenStream(lexer);
|
||||
EQLParser parser = new EQLParser(tokens);
|
||||
EQLParser parser = new EQLParser(new CommonTokenStream(new EQLLexer(CharStreams.fromString(raw))));
|
||||
parser.addErrorListener(errorListener);
|
||||
EQLParser.Select_statementContext context = parser.select_statement();
|
||||
|
||||
EqlAdapter<T> adapter = new EqlAdapter<>(query);
|
||||
|
||||
ParseTreeWalker walker = new ParseTreeWalker();
|
||||
walker.walk(adapter, context);
|
||||
|
||||
new ParseTreeWalker().walk(new EqlAdapter<>(query), parser.select_statement());
|
||||
query.simplifyExpressions();
|
||||
}
|
||||
|
||||
public static <T> void parseWhere(String raw, ExpressionList<T> where, ExpressionFactory expr, Object[] params) {
|
||||
|
||||
EQLParser parser = new EQLParser(new CommonTokenStream(new EQLLexer(CharStreams.fromString(raw))));
|
||||
parser.addErrorListener(errorListener);
|
||||
|
||||
new ParseTreeWalker().walk(new EqlWhereAdapter<>(where, expr), parser.conditional_expression());
|
||||
}
|
||||
|
||||
static class ErrorListener extends BaseErrorListener {
|
||||
|
||||
@Override
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
package io.ebeaninternal.server.grammer;
|
||||
|
||||
enum EqlValueType {
|
||||
NAMED_PARAM,
|
||||
STRING,
|
||||
BOOL,
|
||||
NUMBER
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
package io.ebeaninternal.server.grammer;
|
||||
|
||||
import io.ebean.ExpressionFactory;
|
||||
import io.ebean.ExpressionList;
|
||||
|
||||
class EqlWhereAdapter<T> extends EqlWhereListener<T> {
|
||||
|
||||
private final ExpressionList<T> where;
|
||||
private final ExpressionFactory expr;
|
||||
|
||||
EqlWhereAdapter(ExpressionList<T> where, ExpressionFactory expr) {
|
||||
this.where = where;
|
||||
this.expr = expr;
|
||||
}
|
||||
|
||||
@Override
|
||||
ExpressionList<T> peekExprList() {
|
||||
return where;
|
||||
}
|
||||
|
||||
@Override
|
||||
ExpressionFactory expressionFactory() {
|
||||
return expr;
|
||||
}
|
||||
|
||||
@Override
|
||||
Object namedParam(String substring) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,407 @@
|
||||
package io.ebeaninternal.server.grammer;
|
||||
|
||||
import io.ebean.Expression;
|
||||
import io.ebean.ExpressionFactory;
|
||||
import io.ebean.ExpressionList;
|
||||
import io.ebean.LikeType;
|
||||
import io.ebeaninternal.server.grammer.antlr.EQLBaseListener;
|
||||
import io.ebeaninternal.server.grammer.antlr.EQLLexer;
|
||||
import io.ebeaninternal.server.grammer.antlr.EQLParser;
|
||||
import io.ebeaninternal.server.util.ArrayStack;
|
||||
import org.antlr.v4.runtime.ParserRuleContext;
|
||||
import org.antlr.v4.runtime.tree.ParseTree;
|
||||
import org.antlr.v4.runtime.tree.TerminalNode;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
abstract class EqlWhereListener<T> extends EQLBaseListener {
|
||||
|
||||
private static final OperatorMapping operatorMapping = new OperatorMapping();
|
||||
|
||||
ArrayStack<ExpressionList<T>> textStack;
|
||||
|
||||
ArrayStack<ExpressionList<T>> whereStack;
|
||||
|
||||
boolean textMode;
|
||||
|
||||
private List<Object> inValues;
|
||||
|
||||
private String inPropertyName;
|
||||
|
||||
/**
|
||||
* Return the current expression list that expressions should be added to.
|
||||
*/
|
||||
abstract ExpressionList<T> peekExprList();
|
||||
|
||||
abstract ExpressionFactory expressionFactory();
|
||||
|
||||
abstract Object namedParam(String substring);
|
||||
|
||||
/**
|
||||
* Push the expression list onto the appropriate stack.
|
||||
*/
|
||||
private void pushExprList(ExpressionList<T> list) {
|
||||
if (textMode) {
|
||||
textStack.push(list);
|
||||
} else {
|
||||
whereStack.push(list);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* End a list of expressions added by 'OR'.
|
||||
*/
|
||||
private void popJunction() {
|
||||
if (textMode) {
|
||||
textStack.pop();
|
||||
} else {
|
||||
whereStack.pop();
|
||||
}
|
||||
}
|
||||
|
||||
private String getLeftHandSidePath(ParserRuleContext ctx) {
|
||||
TerminalNode pathToken = ctx.getToken(EQLLexer.PATH_VARIABLE, 0);
|
||||
return pathToken.getText();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void enterInrange_expression(EQLParser.Inrange_expressionContext ctx) {
|
||||
checkChildren(ctx, 5);
|
||||
String path = getLeftHandSidePath(ctx);
|
||||
EqlOperator op = getOperator(ctx);
|
||||
if (op != EqlOperator.INRANGE) {
|
||||
throw new IllegalStateException("Expecting INRANGE operator but got " + op);
|
||||
}
|
||||
addInRange(path, child(ctx, 2), child(ctx, 4));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void enterBetween_expression(EQLParser.Between_expressionContext ctx) {
|
||||
|
||||
checkChildren(ctx, 5);
|
||||
String path = getLeftHandSidePath(ctx);
|
||||
EqlOperator op = getOperator(ctx);
|
||||
if (op != EqlOperator.BETWEEN) {
|
||||
throw new IllegalStateException("Expecting BETWEEN operator but got " + op);
|
||||
}
|
||||
addBetween(path, child(ctx, 2), child(ctx, 4));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void enterPropertyBetween_expression(EQLParser.PropertyBetween_expressionContext ctx) {
|
||||
checkChildren(ctx, 5);
|
||||
String rawValue = child(ctx, 0);
|
||||
EqlOperator op = getOperator(ctx);
|
||||
if (op != EqlOperator.BETWEEN) {
|
||||
throw new IllegalStateException("Expecting BETWEEN operator but got " + op);
|
||||
}
|
||||
addBetweenProperty(rawValue, child(ctx, 2), child(ctx, 4));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void enterIn_expression(EQLParser.In_expressionContext ctx) {
|
||||
this.inValues = new ArrayList<>();
|
||||
this.inPropertyName = getLeftHandSidePath(ctx);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void enterIn_value(EQLParser.In_valueContext ctx) {
|
||||
int childCount = ctx.getChildCount();
|
||||
for (int i = 0; i < childCount; i++) {
|
||||
String text = child(ctx, i);
|
||||
if (isValue(text)) {
|
||||
inValues.add(bind(text));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
String child(ParserRuleContext ctx, int position) {
|
||||
ParseTree child = ctx.getChild(position);
|
||||
return child.getText();
|
||||
}
|
||||
|
||||
private boolean isValue(String text) {
|
||||
return text.length() != 1 || (!text.equals("(") && !text.equals(")") && !text.equals(","));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void exitIn_expression(EQLParser.In_expressionContext ctx) {
|
||||
addIn(inPropertyName, inValues);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void enterIsNull_expression(EQLParser.IsNull_expressionContext ctx) {
|
||||
String path = getLeftHandSidePath(ctx);
|
||||
peekExprList().isNull(path);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void enterIsNotNull_expression(EQLParser.IsNotNull_expressionContext ctx) {
|
||||
String path = getLeftHandSidePath(ctx);
|
||||
peekExprList().isNotNull(path);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void enterIsEmpty_expression(EQLParser.IsEmpty_expressionContext ctx) {
|
||||
String path = getLeftHandSidePath(ctx);
|
||||
peekExprList().isEmpty(path);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void enterIsNotEmpty_expression(EQLParser.IsNotEmpty_expressionContext ctx) {
|
||||
String path = getLeftHandSidePath(ctx);
|
||||
peekExprList().isNotEmpty(path);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void enterLike_expression(EQLParser.Like_expressionContext ctx) {
|
||||
addExpression(ctx);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void enterComparison_expression(EQLParser.Comparison_expressionContext ctx) {
|
||||
addExpression(ctx);
|
||||
}
|
||||
|
||||
private void addExpression(ParserRuleContext ctx) {
|
||||
int childCount = ctx.getChildCount();
|
||||
if (childCount < 3) {
|
||||
throw new IllegalStateException("expecting 3 children for comparison? " + ctx);
|
||||
}
|
||||
String operator = child(ctx, 1);
|
||||
EqlOperator op = operatorMapping.get(operator);
|
||||
if (op == null) {
|
||||
throw new IllegalStateException("No operator found for " + operator);
|
||||
}
|
||||
String path = getLeftHandSidePath(ctx);
|
||||
String rhs = child(ctx, 2);
|
||||
if (path.equals(rhs)) {
|
||||
// the 'value operator path' form
|
||||
// invert the operator and use LHS as RHS
|
||||
op = invert(op);
|
||||
rhs = child(ctx, 0);
|
||||
}
|
||||
|
||||
// RHS is Path, Literal or Named input parameter
|
||||
addExpression(path, op, rhs);
|
||||
}
|
||||
|
||||
private EqlOperator invert(EqlOperator op) {
|
||||
switch (op) {
|
||||
// no change
|
||||
case EQ:
|
||||
return EqlOperator.EQ;
|
||||
case IEQ:
|
||||
return EqlOperator.IEQ;
|
||||
case INE:
|
||||
return EqlOperator.INE;
|
||||
case NE:
|
||||
return EqlOperator.NE;
|
||||
// invert
|
||||
case LT:
|
||||
return EqlOperator.GT;
|
||||
case LTE:
|
||||
return EqlOperator.GTE;
|
||||
case GT:
|
||||
return EqlOperator.LT;
|
||||
case GTE:
|
||||
return EqlOperator.LTE;
|
||||
default:
|
||||
throw new IllegalStateException("Can not invert operator " + op);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void enterConditional_term(EQLParser.Conditional_termContext ctx) {
|
||||
int childCount = ctx.getChildCount();
|
||||
if (childCount > 1) {
|
||||
pushExprList(peekExprList().and());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void exitConditional_term(EQLParser.Conditional_termContext ctx) {
|
||||
if (ctx.getChildCount() > 1) {
|
||||
popJunction();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void enterConditional_expression(EQLParser.Conditional_expressionContext ctx) {
|
||||
if (ctx.getChildCount() > 1) {
|
||||
pushExprList(peekExprList().or());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void exitConditional_expression(EQLParser.Conditional_expressionContext ctx) {
|
||||
if (ctx.getChildCount() > 1) {
|
||||
popJunction();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void enterConditional_factor(EQLParser.Conditional_factorContext ctx) {
|
||||
if (ctx.getChildCount() > 1) {
|
||||
pushExprList(peekExprList().not());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void exitConditional_factor(EQLParser.Conditional_factorContext ctx) {
|
||||
if (ctx.getChildCount() > 1) {
|
||||
popJunction();
|
||||
}
|
||||
}
|
||||
|
||||
private EqlOperator getOperator(ParserRuleContext ctx) {
|
||||
String operator = child(ctx, 1);
|
||||
EqlOperator op = operatorMapping.get(operator);
|
||||
if (op == null) {
|
||||
throw new IllegalStateException("No operator found for " + operator);
|
||||
}
|
||||
return op;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check for the minimum number of children.
|
||||
*/
|
||||
void checkChildren(ParserRuleContext ctx, int min) {
|
||||
if (ctx.getChildCount() < min) {
|
||||
throw new IllegalStateException("expecting " + min + " children for comparison but got " + ctx.getChildCount());
|
||||
}
|
||||
}
|
||||
|
||||
private Expression like(boolean caseInsensitive, LikeType likeType, String property, Object bindValue) {
|
||||
return expressionFactory().like(property, bindValue, caseInsensitive, likeType);
|
||||
}
|
||||
|
||||
private Expression ieq(String property, Object bindValue) {
|
||||
return expressionFactory().ieqObject(property, bindValue);
|
||||
}
|
||||
|
||||
private Expression ine(String property, Object bindValue) {
|
||||
return expressionFactory().ineObject(property, bindValue);
|
||||
}
|
||||
|
||||
private EqlValueType getValueType(String valueAsText) {
|
||||
|
||||
char firstChar = Character.toLowerCase(valueAsText.charAt(0));
|
||||
switch (firstChar) {
|
||||
case ':':
|
||||
return EqlValueType.NAMED_PARAM;
|
||||
case 't':
|
||||
return EqlValueType.BOOL;
|
||||
case 'f':
|
||||
return EqlValueType.BOOL;
|
||||
case '\'':
|
||||
return EqlValueType.STRING;
|
||||
default:
|
||||
if (Character.isDigit(firstChar)) {
|
||||
return EqlValueType.NUMBER;
|
||||
}
|
||||
throw new IllegalArgumentException("Unexpected first character in value [" + valueAsText + "]");
|
||||
}
|
||||
}
|
||||
|
||||
private void addBetweenProperty(String rawValue, String lowProperty, String highProperty) {
|
||||
peekExprList().betweenProperties(lowProperty, highProperty, bind(rawValue));
|
||||
}
|
||||
|
||||
private void addBetween(String path, String value1, String value2) {
|
||||
peekExprList().between(path, bind(value1), bind(value2));
|
||||
}
|
||||
|
||||
private void addInRange(String path, String value1, String value2) {
|
||||
peekExprList().inRange(path, bind(value1), bind(value2));
|
||||
}
|
||||
|
||||
private void addIn(String path, List<Object> inValues) {
|
||||
peekExprList().in(path, inValues);
|
||||
}
|
||||
|
||||
private void addExpression(String path, EqlOperator op, String value) {
|
||||
|
||||
switch (op) {
|
||||
case EQ:
|
||||
peekExprList().eq(path, bind(value));
|
||||
break;
|
||||
case IEQ:
|
||||
peekExprList().add(ieq(path, bind(value)));
|
||||
break;
|
||||
case NE:
|
||||
peekExprList().ne(path, bind(value));
|
||||
break;
|
||||
case INE:
|
||||
peekExprList().add(ine(path, bind(value)));
|
||||
break;
|
||||
case GT:
|
||||
peekExprList().gt(path, bind(value));
|
||||
break;
|
||||
case LT:
|
||||
peekExprList().lt(path, bind(value));
|
||||
break;
|
||||
case GTE:
|
||||
peekExprList().ge(path, bind(value));
|
||||
break;
|
||||
case LTE:
|
||||
peekExprList().le(path, bind(value));
|
||||
break;
|
||||
case LIKE:
|
||||
addLike(false, LikeType.RAW, path, bind(value));
|
||||
break;
|
||||
case CONTAINS:
|
||||
addLike(false, LikeType.CONTAINS, path, bind(value));
|
||||
break;
|
||||
case STARTS_WITH:
|
||||
addLike(false, LikeType.STARTS_WITH, path, bind(value));
|
||||
break;
|
||||
case ENDS_WITH:
|
||||
addLike(false, LikeType.ENDS_WITH, path, bind(value));
|
||||
break;
|
||||
case ILIKE:
|
||||
addLike(true, LikeType.RAW, path, bind(value));
|
||||
break;
|
||||
case ICONTAINS:
|
||||
addLike(true, LikeType.CONTAINS, path, bind(value));
|
||||
break;
|
||||
case ISTARTS_WITH:
|
||||
addLike(true, LikeType.STARTS_WITH, path, bind(value));
|
||||
break;
|
||||
case IENDS_WITH:
|
||||
addLike(true, LikeType.ENDS_WITH, path, bind(value));
|
||||
break;
|
||||
default:
|
||||
throw new IllegalStateException("Unhandled operator " + op);
|
||||
}
|
||||
}
|
||||
|
||||
private void addLike(boolean caseInsensitive, LikeType likeType, String path, Object bindValue) {
|
||||
peekExprList().add(like(caseInsensitive, likeType, path, bindValue));
|
||||
}
|
||||
|
||||
private Object bind(String value) {
|
||||
return getBindValue(getValueType(value), value);
|
||||
}
|
||||
|
||||
private Object getBindValue(EqlValueType valueType, String value) {
|
||||
switch (valueType) {
|
||||
case BOOL:
|
||||
return Boolean.parseBoolean(value);
|
||||
case NUMBER:
|
||||
return new BigDecimal(value);
|
||||
case STRING:
|
||||
return unquote(value);
|
||||
case NAMED_PARAM:
|
||||
return namedParam(value.substring(1));
|
||||
default:
|
||||
throw new IllegalArgumentException("Unhandled valueType " + valueType);
|
||||
}
|
||||
}
|
||||
|
||||
private String unquote(String value) {
|
||||
return value.substring(1, value.length() - 1);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user