diff --git a/org.adempiere.base/src/org/idempiere/expression/logic/EvaluationVisitor.java b/org.adempiere.base/src/org/idempiere/expression/logic/EvaluationVisitor.java index 13cc177c78..a428788c04 100644 --- a/org.adempiere.base/src/org/idempiere/expression/logic/EvaluationVisitor.java +++ b/org.adempiere.base/src/org/idempiere/expression/logic/EvaluationVisitor.java @@ -25,6 +25,8 @@ package org.idempiere.expression.logic; import java.math.BigDecimal; +import java.sql.Timestamp; +import java.util.regex.Matcher; import java.util.regex.Pattern; import org.compiere.util.Evaluatee; @@ -88,18 +90,35 @@ public class EvaluationVisitor extends SimpleBooleanBaseVisitor { return super.visit(ctx.expression()); } + @SuppressWarnings({"unchecked", "rawtypes"}) @Override public Object visitComparatorExpression(SimpleBooleanParser.ComparatorExpressionContext ctx) { if (ctx.op.EQ() != null) { return isEqual(ctx); } else if (ctx.op.LE() != null) { - return asBigDecimal(ctx.left).compareTo(asBigDecimal(ctx.right)) <= 0; + Comparable leftValue = asComparable(ctx.left); + Comparable rightValue = asComparable(ctx.right); + if (leftValue == null || rightValue == null) + return Boolean.FALSE; + return leftValue.compareTo(rightValue) <= 0; } else if (ctx.op.GE() != null) { - return asBigDecimal(ctx.left).compareTo(asBigDecimal(ctx.right)) >= 0; + Comparable leftValue = asComparable(ctx.left); + Comparable rightValue = asComparable(ctx.right); + if (leftValue == null || rightValue == null) + return Boolean.FALSE; + return leftValue.compareTo(rightValue) >= 0; } else if (ctx.op.LT() != null) { - return asBigDecimal(ctx.left).compareTo(asBigDecimal(ctx.right)) < 0; + Comparable leftValue = asComparable(ctx.left); + Comparable rightValue = asComparable(ctx.right); + if (leftValue == null || rightValue == null) + return Boolean.FALSE; + return leftValue.compareTo(rightValue) < 0; } else if (ctx.op.GT() != null) { - return asBigDecimal(ctx.left).compareTo(asBigDecimal(ctx.right)) > 0; + Comparable leftValue = asComparable(ctx.left); + Comparable rightValue = asComparable(ctx.right); + if (leftValue == null || rightValue == null) + return Boolean.FALSE; + return leftValue.compareTo(rightValue) > 0; } else if (ctx.op.NE() != null) { return !(isEqual(ctx)); } else if (ctx.op.RE() != null) { @@ -215,13 +234,35 @@ public class EvaluationVisitor extends SimpleBooleanBaseVisitor { return (boolean) visit(ctx); } - private BigDecimal asBigDecimal(SimpleBooleanParser.ExpressionContext ctx) { + private static final Pattern jdbcTimestampPattern = Pattern.compile(".*[-].*[-].*[:].*[:].*"); + + @SuppressWarnings("rawtypes") + private Comparable asComparable(SimpleBooleanParser.ExpressionContext ctx) { Object value = visit(ctx); - if (value instanceof String) - return new BigDecimal((String)value); - else if (value instanceof BigDecimal) + if (value instanceof String) { + String s = (String) value; + if (Util.isEmpty(s, true)) + return null; + + Matcher matcher = jdbcTimestampPattern.matcher(s); + if (matcher.matches()) { + try { + return Timestamp.valueOf(s); + } catch (Exception e) {} + } + try { + return new BigDecimal(s); + } catch (Exception e) {} + } else if (value instanceof BigDecimal) { return (BigDecimal)value; + } else if (value instanceof Timestamp) { + return (Timestamp)value; + } + + //fall back to comparable and string + if (value instanceof Comparable) + return (Comparable)value; else - return new BigDecimal(value.toString()); + return value.toString(); } } \ No newline at end of file diff --git a/org.idempiere.test/src/org/idempiere/test/base/LogicExpressionTest.java b/org.idempiere.test/src/org/idempiere/test/base/LogicExpressionTest.java index 67b79379bd..106c12c859 100644 --- a/org.idempiere.test/src/org/idempiere/test/base/LogicExpressionTest.java +++ b/org.idempiere.test/src/org/idempiere/test/base/LogicExpressionTest.java @@ -33,6 +33,7 @@ import static org.junit.jupiter.api.Assertions.fail; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; +import java.sql.Timestamp; import java.util.ArrayList; import java.util.List; @@ -44,6 +45,7 @@ import org.compiere.util.DB; import org.compiere.util.Env; import org.compiere.util.Evaluatee; import org.compiere.util.LegacyLogicEvaluator; +import org.compiere.util.TimeUtil; import org.idempiere.expression.logic.LogicEvaluator; import org.idempiere.test.AbstractTestCase; import org.junit.jupiter.api.Test; @@ -492,6 +494,32 @@ public class LogicExpressionTest extends AbstractTestCase { assertTrue(exceptions.isEmpty(), "Found " + exceptions.size() + " logic expression with invalid syntax in AD"); } + @Test + public void testDateExpression() { + String expr = "@DateAcct@<@DateOrdered@"; + Timestamp today = TimeUtil.getDay(System.currentTimeMillis()); + + Env.setContext(Env.getCtx(), "DateAcct", (Timestamp)null); + Env.setContext(Env.getCtx(), "DateOrdered", (Timestamp)null); + assertFalse(LegacyLogicEvaluator.evaluateLogic(evaluatee, expr)); + Env.setContext(Env.getCtx(), "DateAcct", today); + assertFalse(LegacyLogicEvaluator.evaluateLogic(evaluatee, expr)); + Env.setContext(Env.getCtx(), "DateOrdered", today); + assertFalse(LegacyLogicEvaluator.evaluateLogic(evaluatee, expr)); + Env.setContext(Env.getCtx(), "DateAcct", TimeUtil.addDays(today, -1)); + assertTrue(LegacyLogicEvaluator.evaluateLogic(evaluatee, expr)); + + Env.setContext(Env.getCtx(), "DateAcct", (Timestamp)null); + Env.setContext(Env.getCtx(), "DateOrdered", (Timestamp)null); + assertFalse(LogicEvaluator.evaluateLogic(evaluatee, expr)); + Env.setContext(Env.getCtx(), "DateAcct", today); + assertFalse(LogicEvaluator.evaluateLogic(evaluatee, expr)); + Env.setContext(Env.getCtx(), "DateOrdered", today); + assertFalse(LogicEvaluator.evaluateLogic(evaluatee, expr)); + Env.setContext(Env.getCtx(), "DateAcct", TimeUtil.addDays(today, -1)); + assertTrue(LogicEvaluator.evaluateLogic(evaluatee, expr)); + } + private static class ContextEvaluatee implements Evaluatee { @Override