/**
* 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 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(new Long(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 JJTPATTERNVALUE:
return eval(firstChild(node));
case JJTNAMEDINPUTPARAMETER:
return getParameter(node.text, false);
case JJTPOSITIONALINPUTPARAMETER:
return getParameter(node.text, 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')
Expression inExp = null;
Iterator inIterator = node.iterator();
// the first child is the path
val1 = getValue((JPQLNode) inIterator.next());
while (inIterator.hasNext()) {
val2 = getValue((JPQLNode) inIterator.next());
// special case for <value> IN (<subquery>) or
// <value> IN (<single value>)
if (!(val2 instanceof Literal) && node.getChildCount() == 2)
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 (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
return and(evalNot(not, inExp),
factory.notEqual(val1, factory.getNull()));
case JJTISNULL: // x.field IS [NOT] NULL
if (not)
return factory.notEqual
(getValue(onlyChild(node)), factory.getNull());
else
return factory.equal
(getValue(onlyChild(node)), factory.getNull());
case JJTPATH:
return getPathOrConstant(node);
case JJTIDENTIFIER:
case JJTIDENTIFICATIONVARIABLE:
return getIdentifier(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 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:
val1 = getValue(child(node, 0, 3));
val2 = getValue(child(node, 1, 3));
val3 = getValue(child(node, 2, 3));
setImplicitType(val1, TYPE_STRING);
setImplicitType(val2, Integer.TYPE);
setImplicitType(val3, Integer.TYPE);
// the semantics of the JPQL substring() function
// are that arg2 is the 1-based start index, and arg3 is
// the length of the string to be return; this is different
// than the semantics of the ExpressionFactory's substring,
// which matches the Java language (0-based start index,
// arg2 is the end index): we perform the translation by
// adding one to the first argument, and then adding the
// first argument to the second argument to get the endIndex
//
// ### we could get rid of some messy expressions by checking for
// the common case where the arguments are specified as
// a literal, in which case we could just do the calculations
// in memory; otherwise we wind up with ugly looking SQL like:
// SELECT ... FROM ... t1
// (SUBSTRING(t1.ASTR, (? - ?) + 1, (? + (? - ?)) - ((? - ?))) = ?)
// [params=(long) 2, (int) 1, (long) 2, (long) 2, (int) 1,
// (long) 2, (int) 1, (String) oo
return factory.substring(val1, factory.newArgumentList
(factory.subtract(val2, factory.newLiteral
(Numbers.valueOf(1), Literal.TYPE_NUMBER)),
(factory.add(val3,
(factory.subtract(val2, factory.newLiteral
(Numbers.valueOf(1), Literal.TYPE_NUMBER)))))));
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
locateFromIndex = getValue(thirdChild(node));
setImplicitType(locatePath, TYPE_STRING);
setImplicitType(locateSearch, TYPE_STRING);