/**
* Recursive helper method to evaluate the given node.
*/
private Object eval(JPQLNode node) {
Value val1 = null;
Value val2 = null;
Value val3 = null;
boolean not = node.not;
switch (node.id) {
case JJTSCALAREXPRESSION:
return eval(onlyChild(node));
case JJTTYPE:
return getType(onlyChild(node));
case JJTTYPELITERAL:
return getTypeLiteral(node);
case JJTCLASSNAME:
return getPathOrConstant(node);
case JJTCASE:
return eval(onlyChild(node));
case JJTSIMPLECASE:
return getSimpleCaseExpression(node);
case JJTGENERALCASE:
return getGeneralCaseExpression(node);
case JJTWHEN:
return getWhenCondition(node);
case JJTWHENSCALAR:
return getWhenScalar(node);
case JJTCOALESCE:
return getCoalesceExpression(node);
case JJTNULLIF:
return getNullIfExpression(node);
case JJTWHERE: // top-level WHERE clause
return getExpression(onlyChild(node));
case JJTBOOLEANLITERAL:
return factory.newLiteral("true".equalsIgnoreCase
(node.text) ? Boolean.TRUE : Boolean.FALSE,
Literal.TYPE_BOOLEAN);
case JJTINTEGERLITERAL:
// use BigDecimal because it can handle parsing exponents
BigDecimal intlit = new BigDecimal
(node.text.endsWith("l") || node.text.endsWith("L")
? node.text.substring(0, node.text.length() - 1)
: node.text).
multiply(new BigDecimal(negative(node)));
return factory.newLiteral(Long.valueOf(intlit.longValue()),
Literal.TYPE_NUMBER);
case JJTDECIMALLITERAL:
BigDecimal declit = new BigDecimal
(node.text.endsWith("d") || node.text.endsWith("D") ||
node.text.endsWith("f") || node.text.endsWith("F")
? node.text.substring(0, node.text.length() - 1)
: node.text).
multiply(new BigDecimal(negative(node)));
return factory.newLiteral(declit, Literal.TYPE_NUMBER);
case JJTSTRINGLITERAL:
case JJTTRIMCHARACTER:
case JJTESCAPECHARACTER:
return factory.newLiteral(trimQuotes(node.text),
Literal.TYPE_SQ_STRING);
case JJTSTRINGLITERAL2:
return factory.newLiteral(trimDoubleQuotes(node.text),
Literal.TYPE_SQ_STRING);
case JJTPATTERNVALUE:
return eval(firstChild(node));
case JJTNAMEDINPUTPARAMETER:
return getParameter(onlyChild(node).text, false, false);
case JJTPOSITIONALINPUTPARAMETER:
return getParameter(node.text, true, false);
case JJTCOLLECTIONPARAMETER:
JPQLNode child = onlyChild(node);
boolean positional = child.id == JJTPOSITIONALINPUTPARAMETER;
if (!positional)
child = onlyChild(child);
return getParameter(child.text,
positional, true);
case JJTOR: // x OR y
return factory.or(getExpression(left(node)),
getExpression(right(node)));
case JJTAND: // x AND y
return and(getExpression(left(node)),
getExpression(right(node)));
case JJTEQUALS: // x = y
val1 = getValue(left(node));
val2 = getValue(right(node));
setImplicitTypes(val1, val2, null);
return factory.equal(val1, val2);
case JJTNOTEQUALS: // x <> y
val1 = getValue(left(node));
val2 = getValue(right(node));
setImplicitTypes(val1, val2, null);
return factory.notEqual(val1, val2);
case JJTLESSTHAN: // x < y
val1 = getValue(left(node));
val2 = getValue(right(node));
setImplicitTypes(val1, val2, null);
return factory.lessThan(val1, val2);
case JJTLESSOREQUAL: // x <= y
val1 = getValue(left(node));
val2 = getValue(right(node));
setImplicitTypes(val1, val2, null);
return factory.lessThanEqual(val1, val2);
case JJTGREATERTHAN: // x > y
val1 = getValue(left(node));
val2 = getValue(right(node));
setImplicitTypes(val1, val2, null);
return factory.greaterThan(val1, val2);
case JJTGREATEROREQUAL: // x >= y
val1 = getValue(left(node));
val2 = getValue(right(node));
setImplicitTypes(val1, val2, null);
return factory.greaterThanEqual(val1, val2);
case JJTADD: // x + y
val1 = getValue(left(node));
val2 = getValue(right(node));
setImplicitTypes(val1, val2, TYPE_NUMBER);
return factory.add(val1, val2);
case JJTSUBTRACT: // x - y
val1 = getValue(left(node));
val2 = getValue(right(node));
setImplicitTypes(val1, val2, TYPE_NUMBER);
return factory.subtract(val1, val2);
case JJTMULTIPLY: // x * y
val1 = getValue(left(node));
val2 = getValue(right(node));
setImplicitTypes(val1, val2, TYPE_NUMBER);
return factory.multiply(val1, val2);
case JJTDIVIDE: // x / y
val1 = getValue(left(node));
val2 = getValue(right(node));
setImplicitTypes(val1, val2, TYPE_NUMBER);
return factory.divide(val1, val2);
case JJTBETWEEN: // x.field [NOT] BETWEEN 5 AND 10
val1 = getValue(child(node, 0, 3));
val2 = getValue(child(node, 1, 3));
val3 = getValue(child(node, 2, 3));
setImplicitTypes(val1, val2, null);
setImplicitTypes(val1, val3, null);
return evalNot(not, and(factory.greaterThanEqual(val1, val2),
factory.lessThanEqual(val1, val3)));
case JJTIN: // x.field [NOT] IN ('a', 'b', 'c')
// TYPE(x...) [NOT] IN (entityTypeLiteral1,...)
Expression inExp = null;
Iterator<JPQLNode> inIterator = node.iterator();
// the first child is the path
JPQLNode first = inIterator.next();
val1 = getValue(first);
while (inIterator.hasNext()) {
JPQLNode next = inIterator.next();
if (first.id == JJTTYPE && next.id == JJTTYPELITERAL)
val2 = getTypeLiteral(next);
else
val2 = getValue(next);
if (val2 instanceof Parameter) {
hasParameterizedInExpression = true;
}
// special case for <value> IN (<subquery>) or
// <value> IN (<single value>)
if (useContains(not, val1, val2, node))
return evalNot(not, factory.contains(val2, val1));
// this is currently a sequence of OR expressions, since we
// do not have support for IN expressions
setImplicitTypes(val1, val2, null);
if (isVerticalTypeInExpr(val1, node) && not) {
if (inExp == null)
inExp = factory.notEqual(val1, val2);
else
inExp = factory.and(inExp, factory.notEqual(val1, val2));
} else {
if (inExp == null)
inExp = factory.equal(val1, val2);
else
inExp = factory.or(inExp, factory.equal(val1, val2));
}
}
// we additionally need to add in a "NOT NULL" clause, since
// the IN behavior that is expected by the CTS also expects
// to filter our NULLs
if (isVerticalTypeInExpr(val1, node))
return inExp;
else
return and(evalNot(not, inExp),
factory.notEqual(val1, factory.getNull()));
case JJTISNULL: // x.field IS [NOT] NULL
val1 = getValue(onlyChild(node));
checkEmbeddable(val1);
if (not)
return factory.notEqual
(val1, factory.getNull());
else
return factory.equal
(val1, factory.getNull());
case JJTPATH:
return getPathOrConstant(node);
case JJTIDENTIFIER:
case JJTIDENTIFICATIONVARIABLE:
return getIdentifier(node);
case JJTQUALIFIEDPATH:
return getQualifiedPath(node);
case JJTQUALIFIEDIDENTIFIER:
// KEY(e), VALUE(e), ENTRY(e)
return getQualifiedIdentifier(node);
case JJTGENERALIDENTIFIER:
// KEY(e), VALUE(e)
if (node.parent.parent.id == JJTWHERE || node.parent.id == JJTGROUPBY)
return getGeneralIdentifier(onlyChild(node), true);
return getQualifiedIdentifier(onlyChild(node));
case JJTNOT:
return factory.not(getExpression(onlyChild(node)));
case JJTLIKE: // field LIKE '%someval%'
val1 = getValue(left(node));
val2 = getValue(right(node));
setImplicitType(val1, TYPE_STRING);
setImplicitType(val2, TYPE_STRING);
// look for an escape character beneath the node
String escape = null;
JPQLNode escapeNode = right(node).
findChildByID(JJTESCAPECHARACTER, true);
if (escapeNode != null)
escape = trimQuotes(onlyChild(escapeNode).text);
if (not)
return factory.notMatches(val1, val2, "_", "%", escape);
else
return factory.matches(val1, val2, "_", "%", escape);
case JJTISEMPTY:
return evalNot(not,
factory.isEmpty(getValue(onlyChild(node))));
case JJTSIZE:
return factory.size(getValue(onlyChild(node)));
case JJTINDEX:
return factory.index(getValue(onlyChild(node)));
case JJTUPPER:
val1 = getValue(onlyChild(node));
setImplicitType(val1, TYPE_STRING);
return factory.toUpperCase(val1);
case JJTLOWER:
return factory.toLowerCase(getStringValue(onlyChild(node)));
case JJTLENGTH:
return factory.stringLength(getStringValue(onlyChild(node)));
case JJTABS:
return factory.abs(getNumberValue(onlyChild(node)));
case JJTSQRT:
return factory.sqrt(getNumberValue(onlyChild(node)));
case JJTMOD:
val1 = getValue(left(node));
val2 = getValue(right(node));
setImplicitTypes(val1, val2, TYPE_NUMBER);
return factory.mod(val1, val2);
case JJTTRIM: // TRIM([[where] [char] FROM] field)
val1 = getValue(lastChild(node));
setImplicitType(val1, TYPE_STRING);
Boolean trimWhere = null;
JPQLNode firstTrimChild = firstChild(node);
if (node.getChildCount() > 1) {
trimWhere =
firstTrimChild.id == JJTTRIMLEADING ? Boolean.TRUE
:
firstTrimChild.id == JJTTRIMTRAILING ? Boolean.FALSE
: null;
}
Value trimChar;
// if there are 3 children, then we know the trim
// char is the second node
if (node.getChildCount() == 3)
trimChar = getValue(secondChild(node));
// if there are two children, then we need to check to see
// if the first child is a leading/trailing/both node,
// or the trim character node
else if (node.getChildCount() == 2
&& firstTrimChild.id != JJTTRIMLEADING
&& firstTrimChild.id != JJTTRIMTRAILING
&& firstTrimChild.id != JJTTRIMBOTH)
trimChar = getValue(firstChild(node));
// othwerwise, we default to trimming the space character
else
trimChar = factory.newLiteral(" ", Literal.TYPE_STRING);
return factory.trim(val1, trimChar, trimWhere);
case JJTCONCAT:
val1 = getValue(left(node));
val2 = getValue(right(node));
setImplicitType(val1, TYPE_STRING);
setImplicitType(val2, TYPE_STRING);
return factory.concat(val1, val2);
case JJTSUBSTRING:
if (node.children.length == 3) {
val1 = getValue(child(node, 0, 3));
val2 = getValue(child(node, 1, 3));
JPQLNode child3 = child(node, 2, 3);
if (child3.id == JJTINTEGERLITERAL)
val3 = getIntegerValue(child3);
else
val3 = getValue(child3);
} else if (node.children.length == 2) {
val1 = getValue(child(node, 0, 2));
val2 = getValue(child(node, 1, 2));
}
setImplicitType(val1, TYPE_STRING);
setImplicitType(val2, Integer.TYPE);
if (node.children.length == 3)
setImplicitType(val3, Integer.TYPE);
return convertSubstringArguments(factory, val1, val2, val3);
case JJTLOCATE:
// as with SUBSTRING (above), the semantics for LOCATE differ
// from ExpressionFactory.indexOf in that LOCATE uses a
// 0-based index, and indexOf uses a 1-based index
Value locatePath = getValue(firstChild(node));
Value locateSearch = getValue(secondChild(node));
Value locateFromIndex = null;
if (node.getChildCount() > 2) { // optional start index arg
JPQLNode child3 = thirdChild(node);
if (child3.id == JJTINTEGERLITERAL) {
locateFromIndex = getIntegerValue(child3);
} else