diff --git a/src/main/java/jef/asm/FilterMethodVisitor.java b/src/main/java/jef/asm/FilterMethodVisitor.java index 1f77ab8..6c0fe31 100644 --- a/src/main/java/jef/asm/FilterMethodVisitor.java +++ b/src/main/java/jef/asm/FilterMethodVisitor.java @@ -4,6 +4,7 @@ import jef.expressions.BinaryExpression; import jef.expressions.ConstantExpression; import jef.expressions.Expression; import jef.expressions.FieldExpression; +import jef.expressions.NullExpression; import jef.expressions.ParameterExpression; import jef.expressions.SelectExpression; import jef.expressions.TableExpression; @@ -198,6 +199,8 @@ class FilterMethodVisitor extends MethodVisitor { case Opcodes.IF_ICMPLE: case Opcodes.IF_ICMPGT: case Opcodes.IF_ICMPLT: + case Opcodes.IFNULL: + case Opcodes.IFNONNULL: handleLextLabel(); condStack.add(new Cond(opcode, label, varStack)); varStack = new Stack<>(); @@ -311,19 +314,15 @@ class FilterMethodVisitor extends MethodVisitor { switch (cond.opcode) { case Opcodes.IFEQ: expr = right; -// expr = new Expression.TernaryExpression(right, cond.e1, cond.e2); break; case Opcodes.IFNE: - expr = right; - -// expr = new Expression.TernaryExpression(right, cond.e1, cond.e2); -// if (expr instanceof Expression.TernaryExpression texpr -// && texpr.getWhenFalse() == Expression.ConstantExpression.I0) { -// expr = new Expression.BinaryExpression(texpr.getCond(), texpr.getWhenTrue(), "AND"); -// } -// wrapInTernary = false; - - expr = new UnaryExpression(expr, UnaryExpression.Operator.NOT); + expr = new UnaryExpression(right, UnaryExpression.Operator.NOT); + break; + case Opcodes.IFNULL: + expr = new BinaryExpression(right, new UnaryExpression(NullExpression.INSTANCE, UnaryExpression.Operator.NOT), BinaryExpression.Operator.IS); + break; + case Opcodes.IFNONNULL: + expr = new BinaryExpression(right, NullExpression.INSTANCE, BinaryExpression.Operator.IS); break; default: { @@ -430,7 +429,9 @@ class FilterMethodVisitor extends MethodVisitor { 183, "INVOKESPECIAL", 184, "INVOKESTATIC", 185, "INVOKEINTERFACE", - 186, "INVOKEDYNAMIC" + 186, "INVOKEDYNAMIC", + 198, "IFNULL", + 199, "IFNONNULL" ); private static Map createOpsMap(Object... o) { diff --git a/src/main/java/jef/expressions/BinaryExpression.java b/src/main/java/jef/expressions/BinaryExpression.java index eb6bf6a..5491669 100644 --- a/src/main/java/jef/expressions/BinaryExpression.java +++ b/src/main/java/jef/expressions/BinaryExpression.java @@ -33,6 +33,7 @@ public class BinaryExpression implements Expression { // OR("OR"), // AND("AND"), IN("IN"), + IS("IS"), ; private final String string; diff --git a/src/main/java/jef/expressions/Expression.java b/src/main/java/jef/expressions/Expression.java index cc9063c..cb6079d 100644 --- a/src/main/java/jef/expressions/Expression.java +++ b/src/main/java/jef/expressions/Expression.java @@ -8,6 +8,7 @@ public interface Expression { BINARY, CONSTANT, FIELD, + NULL, OR, PARAMETER, SELECT, diff --git a/src/main/java/jef/expressions/NullExpression.java b/src/main/java/jef/expressions/NullExpression.java new file mode 100644 index 0000000..946fb81 --- /dev/null +++ b/src/main/java/jef/expressions/NullExpression.java @@ -0,0 +1,19 @@ +package jef.expressions; + +public class NullExpression extends ConstantExpression { + public static final NullExpression INSTANCE = new NullExpression(); + + private NullExpression() { + super(null); + } + + @Override + public Type getType() { + return Type.NULL; + } + + @Override + public String toString() { + return "NULL"; + } +} diff --git a/src/main/java/jef/expressions/ParameterExpression.java b/src/main/java/jef/expressions/ParameterExpression.java index fcc12ae..7720ade 100644 --- a/src/main/java/jef/expressions/ParameterExpression.java +++ b/src/main/java/jef/expressions/ParameterExpression.java @@ -3,6 +3,7 @@ package jef.expressions; import lombok.Getter; import java.util.Collection; +import java.util.Objects; import java.util.stream.Collectors; @Getter @@ -28,7 +29,7 @@ public class ParameterExpression extends ConstantExpression implements Expressio } else if (this.value == null) { return "null"; } else if (this.value instanceof Collection) { - return "(" + ((Collection) this.value).stream().map(Object::toString).collect(Collectors.joining(",")) + ")"; + return "(" + ((Collection) this.value).stream().map(Objects::toString).collect(Collectors.joining(",")) + ")"; } return value.toString(); } diff --git a/src/main/java/jef/expressions/UnaryExpression.java b/src/main/java/jef/expressions/UnaryExpression.java index d96543c..8c6039c 100644 --- a/src/main/java/jef/expressions/UnaryExpression.java +++ b/src/main/java/jef/expressions/UnaryExpression.java @@ -16,7 +16,11 @@ public class UnaryExpression implements Expression { @Override public String toString() { - return operator + " (" + expr + ")"; + if (expr instanceof ConstantExpression) { + return operator + " " + expr; + } else { + return operator + " (" + expr + ")"; + } } @AllArgsConstructor diff --git a/src/main/java/jef/expressions/modifier/ExpressionModifier.java b/src/main/java/jef/expressions/modifier/ExpressionModifier.java index 49a991f..2df0067 100644 --- a/src/main/java/jef/expressions/modifier/ExpressionModifier.java +++ b/src/main/java/jef/expressions/modifier/ExpressionModifier.java @@ -5,6 +5,7 @@ import jef.expressions.BinaryExpression; import jef.expressions.ConstantExpression; import jef.expressions.Expression; import jef.expressions.FieldExpression; +import jef.expressions.NullExpression; import jef.expressions.OrExpression; import jef.expressions.ParameterExpression; import jef.expressions.SelectExpression; @@ -24,6 +25,7 @@ public abstract class ExpressionModifier { case BINARY -> modifyBinary((BinaryExpression) expr); case CONSTANT -> modifyConstant((ConstantExpression) expr); case FIELD -> modifyField((FieldExpression) expr); + case NULL -> modifyNull((NullExpression) expr); case OR -> modifyOr((OrExpression) expr); case PARAMETER -> modifyParameter((ParameterExpression) expr); case SELECT -> modifySelect((SelectExpression) expr); @@ -55,6 +57,10 @@ public abstract class ExpressionModifier { return expr; } + public Expression modifyNull(NullExpression expr) { + return expr; + } + public Expression modifyOr(OrExpression expr) { var exprs = new ArrayList(expr.getExprs().size()); for (Expression e : expr.getExprs()) { diff --git a/src/main/java/jef/expressions/visitors/DebugExpressionVisitor.java b/src/main/java/jef/expressions/visitors/DebugExpressionVisitor.java index 561f412..5113c5c 100644 --- a/src/main/java/jef/expressions/visitors/DebugExpressionVisitor.java +++ b/src/main/java/jef/expressions/visitors/DebugExpressionVisitor.java @@ -4,6 +4,7 @@ import jef.expressions.AndExpression; import jef.expressions.BinaryExpression; import jef.expressions.ConstantExpression; import jef.expressions.FieldExpression; +import jef.expressions.NullExpression; import jef.expressions.OrExpression; import jef.expressions.ParameterExpression; import jef.expressions.SelectExpression; @@ -50,6 +51,11 @@ public class DebugExpressionVisitor extends ExpressionVisitor { System.out.println(i() + expr.getName()); } + @Override + public void visitNull(NullExpression expr) { + System.out.println(i() + expr.toString()); + } + @Override public void visitOr(OrExpression expr) { for (int i = 0; i < expr.getExprs().size(); i++) { diff --git a/src/main/java/jef/expressions/visitors/ExpressionVisitor.java b/src/main/java/jef/expressions/visitors/ExpressionVisitor.java index 7376640..7ab184d 100644 --- a/src/main/java/jef/expressions/visitors/ExpressionVisitor.java +++ b/src/main/java/jef/expressions/visitors/ExpressionVisitor.java @@ -5,6 +5,7 @@ import jef.expressions.BinaryExpression; import jef.expressions.ConstantExpression; import jef.expressions.Expression; import jef.expressions.FieldExpression; +import jef.expressions.NullExpression; import jef.expressions.OrExpression; import jef.expressions.ParameterExpression; import jef.expressions.SelectExpression; @@ -20,6 +21,7 @@ public abstract class ExpressionVisitor { case BINARY -> visitBinary((BinaryExpression) expr); case CONSTANT -> visitConstant((ConstantExpression) expr); case FIELD -> visitField((FieldExpression) expr); + case NULL -> visitNull((NullExpression) expr); case OR -> visitOr((OrExpression) expr); case PARAMETER -> visitParameter((ParameterExpression) expr); case SELECT -> visitSelect((SelectExpression) expr); @@ -48,6 +50,9 @@ public abstract class ExpressionVisitor { public void visitField(FieldExpression expr) { } + public void visitNull(NullExpression expr) { + } + public void visitOr(OrExpression expr) { for (Expression e : expr.getExprs()) { visit(e); diff --git a/src/test/java/jef/operations/FilterOpTest.java b/src/test/java/jef/operations/FilterOpTest.java index 0080443..dbbe8e8 100644 --- a/src/test/java/jef/operations/FilterOpTest.java +++ b/src/test/java/jef/operations/FilterOpTest.java @@ -5,6 +5,7 @@ import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; import java.io.Serializable; +import java.util.Arrays; import java.util.List; public class FilterOpTest { @@ -111,6 +112,7 @@ public class FilterOpTest { .toString(); Assertions.assertEquals("SELECT * FROM (SELECT * FROM `table1`) a WHERE a.b = 1337 OR a.b = 420 OR a.b IN (1,3)", act); + act = new DBSet("table1") .filter(e -> e.b == 1337 || s.contains(e.b) || e.b == 420) .toString(); @@ -133,6 +135,28 @@ public class FilterOpTest { Assertions.assertEquals("SELECT * FROM (SELECT * FROM `table1`) a WHERE (a.b = 1337 OR a.b = 420) AND a.b IN (1,3)", act); } + @Test + public void testObject() { + String act; + var s = Arrays.asList(null, new Object()); + act = new DBSet("table1") + .filter(e -> e.o == null) + .toString(); + Assertions.assertEquals("SELECT * FROM (SELECT * FROM `table1`) a WHERE a.o IS NULL", act); + + + act = new DBSet("table1") + .filter(e -> e.o != null) + .toString(); + Assertions.assertEquals("SELECT * FROM (SELECT * FROM `table1`) a WHERE a.o IS NOT NULL", act); + + + act = new DBSet("table1") + .filter(e -> e.o != null || s.contains(e.o)) + .toString(); + Assertions.assertEquals("SELECT * FROM (SELECT * FROM `table1`) a WHERE a.o IS NOT NULL OR a.o IN (NULL)", act); + } + @Test public void test() { @@ -140,5 +164,6 @@ public class FilterOpTest { public static class TestClass implements Serializable { public int b = 1; + public Object o = new Object(); } }