initial commit
This commit is contained in:
3
.gitignore
vendored
Normal file
3
.gitignore
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
target/
|
||||
*.iml
|
||||
.idea/
|
||||
137
pom.xml
Normal file
137
pom.xml
Normal file
@@ -0,0 +1,137 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<groupId>jef</groupId>
|
||||
<artifactId>jef</artifactId>
|
||||
<version>1.0</version>
|
||||
|
||||
<properties>
|
||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||
<maven.compiler.source>17</maven.compiler.source>
|
||||
<maven.compiler.target>17</maven.compiler.target>
|
||||
<asm.version>9.3</asm.version>
|
||||
<lombok.version>1.18.16</lombok.version>
|
||||
</properties>
|
||||
|
||||
<profiles>
|
||||
<profile>
|
||||
<id>java9plus</id>
|
||||
<activation>
|
||||
<jdk>[1.9,)</jdk>
|
||||
</activation>
|
||||
<properties>
|
||||
<maven.compiler.release>${maven.compiler.target}</maven.compiler.release>
|
||||
</properties>
|
||||
</profile>
|
||||
<profile>
|
||||
<id>java11plus</id>
|
||||
<activation>
|
||||
<jdk>[1.11,)</jdk>
|
||||
</activation>
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-compiler-plugin</artifactId>
|
||||
<version>3.8.1</version>
|
||||
<configuration>
|
||||
<fork>true</fork>
|
||||
<compilerArgs>
|
||||
<arg>-J--add-opens=jdk.compiler/com.sun.tools.javac.code=ALL-UNNAMED</arg>
|
||||
<arg>-J--add-opens=jdk.compiler/com.sun.tools.javac.comp=ALL-UNNAMED</arg>
|
||||
<arg>-J--add-opens=jdk.compiler/com.sun.tools.javac.file=ALL-UNNAMED</arg>
|
||||
<arg>-J--add-opens=jdk.compiler/com.sun.tools.javac.main=ALL-UNNAMED</arg>
|
||||
<arg>-J--add-opens=jdk.compiler/com.sun.tools.javac.model=ALL-UNNAMED</arg>
|
||||
<arg>-J--add-opens=jdk.compiler/com.sun.tools.javac.parser=ALL-UNNAMED</arg>
|
||||
<arg>-J--add-opens=jdk.compiler/com.sun.tools.javac.processing=ALL-UNNAMED</arg>
|
||||
<arg>-J--add-opens=jdk.compiler/com.sun.tools.javac.tree=ALL-UNNAMED</arg>
|
||||
<arg>-J--add-opens=jdk.compiler/com.sun.tools.javac.util=ALL-UNNAMED</arg>
|
||||
<arg>-J--add-opens=jdk.compiler/com.sun.tools.javac.jvm=ALL-UNNAMED</arg>
|
||||
</compilerArgs>
|
||||
<!--for lombok annotations to resolve-->
|
||||
<annotationProcessorPaths>
|
||||
<path>
|
||||
<groupId>org.projectlombok</groupId>
|
||||
<artifactId>lombok</artifactId>
|
||||
<version>${lombok.version}</version>
|
||||
</path>
|
||||
</annotationProcessorPaths>
|
||||
</configuration>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
</profile>
|
||||
</profiles>
|
||||
<build>
|
||||
<finalName>${project.artifactId}</finalName>
|
||||
<resources>
|
||||
<resource>
|
||||
<directory>src/main/resources</directory>
|
||||
<filtering>true</filtering> <!-- insert version into plugin.yml -->
|
||||
</resource>
|
||||
</resources>
|
||||
<plugins>
|
||||
<plugin> <!-- for debugging profiles -->
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-help-plugin</artifactId>
|
||||
<version>3.1.0</version>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>show-profiles</id>
|
||||
<phase>validate</phase>
|
||||
<goals>
|
||||
<goal>active-profiles</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
<plugin> <!-- for profile java9plus -->
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-compiler-plugin</artifactId>
|
||||
<version>3.8.0</version>
|
||||
</plugin>
|
||||
<plugin> <!-- for inserting values in plugin.yml -->
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-resources-plugin</artifactId>
|
||||
<version>2.7</version>
|
||||
<configuration>
|
||||
<encoding>${project.build.sourceEncoding}</encoding>
|
||||
</configuration>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.ow2.asm</groupId>
|
||||
<artifactId>asm-tree</artifactId>
|
||||
<version>${asm.version}</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.ow2.asm</groupId>
|
||||
<artifactId>asm</artifactId>
|
||||
<version>${asm.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.ow2.asm</groupId>
|
||||
<artifactId>asm-commons</artifactId>
|
||||
<version>${asm.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.junit.jupiter</groupId>
|
||||
<artifactId>junit-jupiter</artifactId>
|
||||
<version>5.8.2</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.projectlombok</groupId>
|
||||
<artifactId>lombok</artifactId>
|
||||
<version>${lombok.version}</version>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</project>
|
||||
229
src/main/java/jef/DBSet.java
Normal file
229
src/main/java/jef/DBSet.java
Normal file
@@ -0,0 +1,229 @@
|
||||
package jef;
|
||||
|
||||
import jef.expressions.Expression;
|
||||
import jef.expressions.SelectExpression;
|
||||
import jef.expressions.TableExpression;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Spliterator;
|
||||
|
||||
public class DBSet<T extends Serializable> implements Queryable<T> {
|
||||
private final String table;
|
||||
|
||||
public DBSet(String table) {
|
||||
this.table = table;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getTableAlias() {
|
||||
return String.valueOf((char) ('a' - 1));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Expression getExpression() {
|
||||
return new SelectExpression(List.of("*"), new TableExpression(table), "");
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "SELECT * FROM `" + table + "`";
|
||||
}
|
||||
|
||||
@Override
|
||||
public Iterator<T> iterator() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Spliterator<T> spliterator() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isParallel() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public DBSet<T> sequential() {
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public DBSet<T> parallel() {
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public DBSet<T> unordered() {
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public DBSet<T> onClose(Runnable runnable) {
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
|
||||
}
|
||||
|
||||
//stream
|
||||
// @Override
|
||||
// public <R extends Serializable> Queryable<R> map(Function<? super T, ? extends R> function) {
|
||||
// return null;
|
||||
// }
|
||||
//
|
||||
// @Override
|
||||
// public IntStream mapToInt(ToIntFunction<? super T> toIntFunction) {
|
||||
// return null;
|
||||
// }
|
||||
//
|
||||
// @Override
|
||||
// public LongStream mapToLong(ToLongFunction<? super T> toLongFunction) {
|
||||
// return null;
|
||||
// }
|
||||
//
|
||||
// @Override
|
||||
// public DoubleStream mapToDouble(ToDoubleFunction<? super T> toDoubleFunction) {
|
||||
// return null;
|
||||
// }
|
||||
//
|
||||
// @Override
|
||||
// public <R extends Serializable> Queryable<R> flatMap(Function<? super T, ? extends Stream<? extends R>> function) {
|
||||
// return null;
|
||||
// }
|
||||
//
|
||||
// @Override
|
||||
// public IntStream flatMapToInt(Function<? super T, ? extends IntStream> function) {
|
||||
// return null;
|
||||
// }
|
||||
//
|
||||
// @Override
|
||||
// public LongStream flatMapToLong(Function<? super T, ? extends LongStream> function) {
|
||||
// return null;
|
||||
// }
|
||||
//
|
||||
// @Override
|
||||
// public DoubleStream flatMapToDouble(Function<? super T, ? extends DoubleStream> function) {
|
||||
// return null;
|
||||
// }
|
||||
//
|
||||
// @Override
|
||||
// public DBSet<T> distinct() {
|
||||
// return null;
|
||||
// }
|
||||
//
|
||||
// @Override
|
||||
// public DBSet<T> sorted() {
|
||||
// return null;
|
||||
// }
|
||||
//
|
||||
// @Override
|
||||
// public DBSet<T> sorted(Comparator<? super T> comparator) {
|
||||
// return null;
|
||||
// }
|
||||
//
|
||||
// @Override
|
||||
// public DBSet<T> peek(Consumer<? super T> consumer) {
|
||||
// return null;
|
||||
// }
|
||||
//
|
||||
// @Override
|
||||
// public DBSet<T> limit(long l) {
|
||||
// return null;
|
||||
// }
|
||||
//
|
||||
// @Override
|
||||
// public DBSet<T> skip(long l) {
|
||||
// return null;
|
||||
// }
|
||||
|
||||
//<editor-fold desc="terminating operations">
|
||||
// @Override
|
||||
// public void forEach(Consumer<? super T> consumer) {
|
||||
// }
|
||||
//
|
||||
// @Override
|
||||
// public void forEachOrdered(Consumer<? super T> consumer) {
|
||||
// }
|
||||
//
|
||||
// @Override
|
||||
// public Object[] toArray() {
|
||||
// return new Object[0];
|
||||
// }
|
||||
//
|
||||
// @Override
|
||||
// public <A> A[] toArray(IntFunction<A[]> intFunction) {
|
||||
// return null;
|
||||
// }
|
||||
//
|
||||
// @Override
|
||||
// public T reduce(T t, BinaryOperator<T> binaryOperator) {
|
||||
// return null;
|
||||
// }
|
||||
//
|
||||
// @Override
|
||||
// public Optional<T> reduce(BinaryOperator<T> binaryOperator) {
|
||||
// return Optional.empty();
|
||||
// }
|
||||
//
|
||||
// @Override
|
||||
// public <U> U reduce(U u, BiFunction<U, ? super T, U> biFunction, BinaryOperator<U> binaryOperator) {
|
||||
// return null;
|
||||
// }
|
||||
//
|
||||
// @Override
|
||||
// public <R extends Serializable> R collect(Supplier<R> supplier, BiConsumer<R, ? super T> biConsumer, BiConsumer<R, R> biConsumer1) {
|
||||
// return null;
|
||||
// }
|
||||
//
|
||||
// @Override
|
||||
// public <R extends Serializable, A> R collect(Collector<? super T, A, R> collector) {
|
||||
// return null;
|
||||
// }
|
||||
//
|
||||
// @Override
|
||||
// public Optional<T> min(Comparator<? super T> comparator) {
|
||||
// return Optional.empty();
|
||||
// }
|
||||
//
|
||||
// @Override
|
||||
// public Optional<T> max(Comparator<? super T> comparator) {
|
||||
// return Optional.empty();
|
||||
// }
|
||||
//
|
||||
// @Override
|
||||
// public long count() {
|
||||
// return 0;
|
||||
// }
|
||||
//
|
||||
// @Override
|
||||
// public boolean anyMatch(SerializablePredicate<? super T> SerializablePredicate) {
|
||||
// return false;
|
||||
// }
|
||||
//
|
||||
// @Override
|
||||
// public boolean allMatch(SerializablePredicate<? super T> SerializablePredicate) {
|
||||
// return false;
|
||||
// }
|
||||
//
|
||||
// @Override
|
||||
// public boolean noneMatch(SerializablePredicate<? super T> SerializablePredicate) {
|
||||
// return false;
|
||||
// }
|
||||
//
|
||||
// @Override
|
||||
// public Optional<T> findFirst() {
|
||||
// return Optional.empty();
|
||||
// }
|
||||
//
|
||||
// @Override
|
||||
// public Optional<T> findAny() {
|
||||
// return Optional.empty();
|
||||
// }
|
||||
//</editor-fold>
|
||||
}
|
||||
181
src/main/java/jef/Queryable.java
Normal file
181
src/main/java/jef/Queryable.java
Normal file
@@ -0,0 +1,181 @@
|
||||
package jef;
|
||||
|
||||
import jef.expressions.Expression;
|
||||
import jef.operations.FilterOp;
|
||||
import jef.serializable.SerializablePredicate;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.Iterator;
|
||||
import java.util.Spliterator;
|
||||
|
||||
public interface Queryable<T extends Serializable> {
|
||||
|
||||
//
|
||||
String getTableAlias();
|
||||
|
||||
Expression getExpression();
|
||||
|
||||
String toString();
|
||||
|
||||
//stream functions
|
||||
default Iterator<T> iterator() {
|
||||
return null;
|
||||
}
|
||||
|
||||
default Spliterator<T> spliterator() {
|
||||
return null;
|
||||
}
|
||||
|
||||
default boolean isParallel() {
|
||||
return false;
|
||||
}
|
||||
|
||||
default Queryable<T> sequential() {
|
||||
return this;
|
||||
}
|
||||
|
||||
default Queryable<T> parallel() {
|
||||
return this;
|
||||
}
|
||||
|
||||
default Queryable<T> unordered() {
|
||||
return this;
|
||||
}
|
||||
|
||||
default Queryable<T> onClose(Runnable runnable) {
|
||||
return this;
|
||||
}
|
||||
|
||||
default void close() {
|
||||
|
||||
}
|
||||
|
||||
//stream
|
||||
default Queryable<T> filter(SerializablePredicate<? super T> predicate) {
|
||||
return new FilterOp<T>(this, predicate);
|
||||
}
|
||||
|
||||
// default <R extends Serializable> Queryable<R> map(Function<? super T, ? extends R> function) {
|
||||
// return null;
|
||||
// }
|
||||
//
|
||||
// default IntStream mapToInt(ToIntFunction<? super T> toIntFunction) {
|
||||
// return null;
|
||||
// }
|
||||
//
|
||||
// default LongStream mapToLong(ToLongFunction<? super T> toLongFunction) {
|
||||
// return null;
|
||||
// }
|
||||
//
|
||||
// default DoubleStream mapToDouble(ToDoubleFunction<? super T> toDoubleFunction) {
|
||||
// return null;
|
||||
// }
|
||||
//
|
||||
// default <R extends Serializable> Queryable<R> flatMap(Function<? super T, ? extends Stream<? extends R>> function) {
|
||||
// return null;
|
||||
// }
|
||||
//
|
||||
// default IntStream flatMapToInt(Function<? super T, ? extends IntStream> function) {
|
||||
// return null;
|
||||
// }
|
||||
//
|
||||
// default LongStream flatMapToLong(Function<? super T, ? extends LongStream> function) {
|
||||
// return null;
|
||||
// }
|
||||
//
|
||||
// default DoubleStream flatMapToDouble(Function<? super T, ? extends DoubleStream> function) {
|
||||
// return null;
|
||||
// }
|
||||
//
|
||||
// default Queryable<T> distinct() {
|
||||
// return null;
|
||||
// }
|
||||
//
|
||||
// default Queryable<T> sorted() {
|
||||
// return null;
|
||||
// }
|
||||
//
|
||||
// default Queryable<T> sorted(Comparator<? super T> comparator) {
|
||||
// return null;
|
||||
// }
|
||||
//
|
||||
// default Queryable<T> peek(Consumer<? super T> consumer) {
|
||||
// return null;
|
||||
// }
|
||||
//
|
||||
// default Queryable<T> limit(long l) {
|
||||
// return null;
|
||||
// }
|
||||
//
|
||||
// default Queryable<T> skip(long l) {
|
||||
// return null;
|
||||
// }
|
||||
|
||||
//<editor-fold desc="terminating operations">
|
||||
// default void forEach(Consumer<? super T> consumer) {
|
||||
// }
|
||||
//
|
||||
// default void forEachOrdered(Consumer<? super T> consumer) {
|
||||
// }
|
||||
//
|
||||
// default Object[] toArray() {
|
||||
// return new Object[0];
|
||||
// }
|
||||
//
|
||||
// default <A> A[] toArray(IntFunction<A[]> intFunction) {
|
||||
// return null;
|
||||
// }
|
||||
//
|
||||
// default T reduce(T t, BinaryOperator<T> binaryOperator) {
|
||||
// return null;
|
||||
// }
|
||||
//
|
||||
// default Optional<T> reduce(BinaryOperator<T> binaryOperator) {
|
||||
// return Optional.empty();
|
||||
// }
|
||||
//
|
||||
// default <U> U reduce(U u, BiFunction<U, ? super T, U> biFunction, BinaryOperator<U> binaryOperator) {
|
||||
// return null;
|
||||
// }
|
||||
//
|
||||
// default <R extends Serializable> R collect(Supplier<R> supplier, BiConsumer<R, ? super T> biConsumer, BiConsumer<R, R> biConsumer1) {
|
||||
// return null;
|
||||
// }
|
||||
//
|
||||
// default <R extends Serializable, A> R collect(Collector<? super T, A, R> collector) {
|
||||
// return null;
|
||||
// }
|
||||
//
|
||||
// default Optional<T> min(Comparator<? super T> comparator) {
|
||||
// return Optional.empty();
|
||||
// }
|
||||
//
|
||||
// default Optional<T> max(Comparator<? super T> comparator) {
|
||||
// return Optional.empty();
|
||||
// }
|
||||
//
|
||||
// default long count() {
|
||||
// return 0;
|
||||
// }
|
||||
//
|
||||
// default boolean anyMatch(SerializablePredicate<? super T> SerializablePredicate) {
|
||||
// return false;
|
||||
// }
|
||||
//
|
||||
// default boolean allMatch(SerializablePredicate<? super T> SerializablePredicate) {
|
||||
// return false;
|
||||
// }
|
||||
//
|
||||
// default boolean noneMatch(SerializablePredicate<? super T> SerializablePredicate) {
|
||||
// return false;
|
||||
// }
|
||||
//
|
||||
// default Optional<T> findFirst() {
|
||||
// return Optional.empty();
|
||||
// }
|
||||
//
|
||||
// default Optional<T> findAny() {
|
||||
// return Optional.empty();
|
||||
// }
|
||||
//</editor-fold>
|
||||
}
|
||||
229
src/main/java/jef/QueryableProxy.java
Normal file
229
src/main/java/jef/QueryableProxy.java
Normal file
@@ -0,0 +1,229 @@
|
||||
package jef;
|
||||
|
||||
import jef.expressions.Expression;
|
||||
import jef.serializable.SerializablePredicate;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.Iterator;
|
||||
import java.util.Spliterator;
|
||||
|
||||
public class QueryableProxy<T extends Serializable> implements Queryable<T> {
|
||||
private final Queryable<T> delegate;
|
||||
|
||||
public QueryableProxy(Queryable<T> delegate) {
|
||||
this.delegate = delegate;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getTableAlias() {
|
||||
return delegate.getTableAlias();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Expression getExpression() {
|
||||
return delegate.getExpression();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Iterator<T> iterator() {
|
||||
return delegate.iterator();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Spliterator<T> spliterator() {
|
||||
return delegate.spliterator();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isParallel() {
|
||||
return delegate.isParallel();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Queryable<T> sequential() {
|
||||
return delegate.sequential();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Queryable<T> parallel() {
|
||||
return delegate.parallel();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Queryable<T> unordered() {
|
||||
return delegate.unordered();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Queryable<T> onClose(Runnable runnable) {
|
||||
return delegate.onClose(runnable);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
delegate.close();
|
||||
}
|
||||
|
||||
//stream
|
||||
@Override
|
||||
public Queryable<T> filter(SerializablePredicate<? super T> predicate) {
|
||||
return delegate.filter(predicate);
|
||||
}
|
||||
|
||||
// @Override
|
||||
// public <R extends Serializable> Queryable<R> map(Function<? super T, ? extends R> function) {
|
||||
// return delegate.map(function);
|
||||
// }
|
||||
//
|
||||
// @Override
|
||||
// public IntStream mapToInt(ToIntFunction<? super T> toIntFunction) {
|
||||
// return delegate.mapToInt(toIntFunction);
|
||||
// }
|
||||
//
|
||||
// @Override
|
||||
// public LongStream mapToLong(ToLongFunction<? super T> toLongFunction) {
|
||||
// return delegate.mapToLong(toLongFunction);
|
||||
// }
|
||||
//
|
||||
// @Override
|
||||
// public DoubleStream mapToDouble(ToDoubleFunction<? super T> toDoubleFunction) {
|
||||
// return delegate.mapToDouble(toDoubleFunction);
|
||||
// }
|
||||
//
|
||||
// @Override
|
||||
// public <R extends Serializable> Queryable<R> flatMap(Function<? super T, ? extends Stream<? extends R>> function) {
|
||||
// return delegate.flatMap(function);
|
||||
// }
|
||||
//
|
||||
// @Override
|
||||
// public IntStream flatMapToInt(Function<? super T, ? extends IntStream> function) {
|
||||
// return delegate.flatMapToInt(function);
|
||||
// }
|
||||
//
|
||||
// @Override
|
||||
// public LongStream flatMapToLong(Function<? super T, ? extends LongStream> function) {
|
||||
// return delegate.flatMapToLong(function);
|
||||
// }
|
||||
//
|
||||
// @Override
|
||||
// public DoubleStream flatMapToDouble(Function<? super T, ? extends DoubleStream> function) {
|
||||
// return delegate.flatMapToDouble(function);
|
||||
// }
|
||||
//
|
||||
// @Override
|
||||
// public Queryable<T> distinct() {
|
||||
// return delegate.distinct();
|
||||
// }
|
||||
//
|
||||
// @Override
|
||||
// public Queryable<T> sorted() {
|
||||
// return delegate.sorted();
|
||||
// }
|
||||
//
|
||||
// @Override
|
||||
// public Queryable<T> sorted(Comparator<? super T> comparator) {
|
||||
// return delegate.sorted(comparator);
|
||||
// }
|
||||
//
|
||||
// @Override
|
||||
// public Queryable<T> peek(Consumer<? super T> consumer) {
|
||||
// return delegate.peek(consumer);
|
||||
// }
|
||||
//
|
||||
// @Override
|
||||
// public Queryable<T> limit(long l) {
|
||||
// return delegate.limit(l);
|
||||
// }
|
||||
//
|
||||
// @Override
|
||||
// public Queryable<T> skip(long l) {
|
||||
// return delegate.skip(l);
|
||||
// }
|
||||
|
||||
//<editor-fold desc="terminating operations">
|
||||
// @Override
|
||||
// public void forEach(Consumer<? super T> consumer) {
|
||||
// delegate.forEach(consumer);
|
||||
// }
|
||||
//
|
||||
// @Override
|
||||
// public void forEachOrdered(Consumer<? super T> consumer) {
|
||||
// delegate.forEachOrdered(consumer);
|
||||
// }
|
||||
//
|
||||
// @Override
|
||||
// public Object[] toArray() {
|
||||
// return delegate.toArray();
|
||||
// }
|
||||
//
|
||||
// @Override
|
||||
// public <A> A[] toArray(IntFunction<A[]> intFunction) {
|
||||
// return delegate.toArray(intFunction);
|
||||
// }
|
||||
//
|
||||
// @Override
|
||||
// public T reduce(T t, BinaryOperator<T> binaryOperator) {
|
||||
// return delegate.reduce(t, binaryOperator);
|
||||
// }
|
||||
//
|
||||
// @Override
|
||||
// public Optional<T> reduce(BinaryOperator<T> binaryOperator) {
|
||||
// return delegate.reduce(binaryOperator);
|
||||
// }
|
||||
//
|
||||
// @Override
|
||||
// public <U> U reduce(U u, BiFunction<U, ? super T, U> biFunction, BinaryOperator<U> binaryOperator) {
|
||||
// return delegate.reduce(u, biFunction, binaryOperator);
|
||||
// }
|
||||
//
|
||||
// @Override
|
||||
// public <R extends Serializable> R collect(Supplier<R> supplier, BiConsumer<R, ? super T> biConsumer, BiConsumer<R, R> biConsumer1) {
|
||||
// return delegate.collect(supplier, biConsumer, biConsumer1);
|
||||
// }
|
||||
//
|
||||
// @Override
|
||||
// public <R extends Serializable, A> R collect(Collector<? super T, A, R> collector) {
|
||||
// return delegate.collect(collector);
|
||||
// }
|
||||
//
|
||||
// @Override
|
||||
// public Optional<T> min(Comparator<? super T> comparator) {
|
||||
// return delegate.min(comparator);
|
||||
// }
|
||||
//
|
||||
// @Override
|
||||
// public Optional<T> max(Comparator<? super T> comparator) {
|
||||
// return delegate.max(comparator);
|
||||
// }
|
||||
//
|
||||
// @Override
|
||||
// public long count() {
|
||||
// return delegate.count();
|
||||
// }
|
||||
//
|
||||
// @Override
|
||||
// public boolean anyMatch(SerializablePredicate<? super T> SerializablePredicate) {
|
||||
// return delegate.anyMatch(SerializablePredicate);
|
||||
// }
|
||||
//
|
||||
// @Override
|
||||
// public boolean allMatch(SerializablePredicate<? super T> SerializablePredicate) {
|
||||
// return delegate.allMatch(SerializablePredicate);
|
||||
// }
|
||||
//
|
||||
// @Override
|
||||
// public boolean noneMatch(SerializablePredicate<? super T> SerializablePredicate) {
|
||||
// return delegate.noneMatch(SerializablePredicate);
|
||||
// }
|
||||
//
|
||||
// @Override
|
||||
// public Optional<T> findFirst() {
|
||||
// return delegate.findFirst();
|
||||
// }
|
||||
//
|
||||
// @Override
|
||||
// public Optional<T> findAny() {
|
||||
// return delegate.findAny();
|
||||
// }
|
||||
//</editor-fold>
|
||||
}
|
||||
22
src/main/java/jef/asm/AsmParseException.java
Normal file
22
src/main/java/jef/asm/AsmParseException.java
Normal file
@@ -0,0 +1,22 @@
|
||||
package jef.asm;
|
||||
|
||||
public class AsmParseException extends Exception {
|
||||
public AsmParseException() {
|
||||
}
|
||||
|
||||
public AsmParseException(String message) {
|
||||
super(message);
|
||||
}
|
||||
|
||||
public AsmParseException(String message, Throwable cause) {
|
||||
super(message, cause);
|
||||
}
|
||||
|
||||
public AsmParseException(Throwable cause) {
|
||||
super(cause);
|
||||
}
|
||||
|
||||
public AsmParseException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) {
|
||||
super(message, cause, enableSuppression, writableStackTrace);
|
||||
}
|
||||
}
|
||||
32
src/main/java/jef/asm/FilterClassVisitor.java
Normal file
32
src/main/java/jef/asm/FilterClassVisitor.java
Normal file
@@ -0,0 +1,32 @@
|
||||
package jef.asm;
|
||||
|
||||
import jef.expressions.Expression;
|
||||
import org.objectweb.asm.ClassVisitor;
|
||||
import org.objectweb.asm.MethodVisitor;
|
||||
|
||||
import java.util.function.Consumer;
|
||||
|
||||
class FilterClassVisitor extends ClassVisitor {
|
||||
private final int api;
|
||||
private final String lambdaname;
|
||||
private final Consumer<Expression> queryConsumer;
|
||||
private final Object[] args;
|
||||
|
||||
protected FilterClassVisitor(int api, String lambdaname, Object[] args, Consumer<Expression> queryConsumer) {
|
||||
super(api);
|
||||
this.api = api;
|
||||
this.lambdaname = lambdaname;
|
||||
this.args = args;
|
||||
this.queryConsumer = queryConsumer;
|
||||
}
|
||||
|
||||
@Override
|
||||
public MethodVisitor visitMethod(int access, String name, String descriptor, String signature, String[] exceptions) {
|
||||
if (!name.equals(lambdaname)) {
|
||||
return super.visitMethod(access, name, descriptor, signature, exceptions);
|
||||
}
|
||||
|
||||
System.out.println("found method name: " + name);
|
||||
return new FilterMethodVisitor(api, descriptor, args, queryConsumer);
|
||||
}
|
||||
}
|
||||
445
src/main/java/jef/asm/FilterMethodVisitor.java
Normal file
445
src/main/java/jef/asm/FilterMethodVisitor.java
Normal file
@@ -0,0 +1,445 @@
|
||||
package jef.asm;
|
||||
|
||||
import jef.expressions.BinaryExpression;
|
||||
import jef.expressions.ConstantExpression;
|
||||
import jef.expressions.Expression;
|
||||
import jef.expressions.FieldExpression;
|
||||
import jef.expressions.ParameterExpression;
|
||||
import jef.expressions.SelectExpression;
|
||||
import jef.expressions.TableExpression;
|
||||
import jef.expressions.TernaryExpression;
|
||||
import jef.expressions.UnaryExpression;
|
||||
import jef.expressions.WhereExpression;
|
||||
import lombok.ToString;
|
||||
import org.objectweb.asm.Attribute;
|
||||
import org.objectweb.asm.Label;
|
||||
import org.objectweb.asm.MethodVisitor;
|
||||
import org.objectweb.asm.Opcodes;
|
||||
import org.objectweb.asm.Type;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.Stack;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.IntStream;
|
||||
|
||||
class FilterMethodVisitor extends MethodVisitor {
|
||||
private Stack<Expression> varStack = new Stack<>();
|
||||
private final String[] parameterClasses;
|
||||
private final Object[] args;
|
||||
private final Consumer<Expression> exprConsumer;
|
||||
private Expression prediacteExpr;
|
||||
|
||||
protected FilterMethodVisitor(int api, String descriptor, Object[] args, Consumer<Expression> exprConsumer) {
|
||||
super(api);
|
||||
this.args = args;
|
||||
this.exprConsumer = exprConsumer;
|
||||
|
||||
//parameters
|
||||
var types = Type.getMethodType(descriptor).getArgumentTypes();
|
||||
parameterClasses = new String[types.length];
|
||||
for (int i = 0; i < types.length; i++) {
|
||||
parameterClasses[i] = types[i].getClassName();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitLocalVariable(String name, String descriptor, String signature, Label start, Label end, int index) {
|
||||
System.out.println("local var: " + name);
|
||||
super.visitLocalVariable(name, descriptor, signature, start, end, index);
|
||||
debugExpr();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitFieldInsn(int opcode, String owner, String name, String descriptor) {
|
||||
System.out.println("field insn: " + ops.getOrDefault(opcode, "" + opcode) + ", " + owner + ", " + name + ", " + descriptor);
|
||||
if (opcode == Opcodes.GETFIELD) {
|
||||
var v = varStack.pop();
|
||||
if (v instanceof ParameterExpression) {
|
||||
// if (((Expression.ParameterExpression) v).isInput()) {
|
||||
// varStack.push(new Expression.ConstantExpression(name));
|
||||
// } else {
|
||||
System.out.println("womp womp " + v);
|
||||
throw new RuntimeException("field insn: unsupported GETFIELD op");
|
||||
// }
|
||||
} else if (v instanceof ConstantExpression) {
|
||||
varStack.push(new FieldExpression(name));
|
||||
} else {
|
||||
throw new RuntimeException("field insn: unsupported GETFIELD op");
|
||||
}
|
||||
} else {
|
||||
throw new RuntimeException("field insn: unsupported opcode " + ops.getOrDefault(opcode, "" + opcode));
|
||||
}
|
||||
super.visitFieldInsn(opcode, owner, name, descriptor);
|
||||
debugExpr();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitMethodInsn(int opcode, String owner, String name, String descriptor, boolean isInterface) {
|
||||
System.out.println("method insn: " + ops.getOrDefault(opcode, "" + opcode) + ", " + owner + ", " + name + ", " + descriptor);
|
||||
if (opcode == Opcodes.INVOKESTATIC) {
|
||||
//ignore boxed primitive types
|
||||
var boxedPrimitiveClasses = Set.of("java/lang/Boolean", "java/lang/Integer", "java/lang/Long", "java/lang/Float", "java/lang/Double");
|
||||
if (boxedPrimitiveClasses.contains(owner)) {
|
||||
//do nothing
|
||||
} else {
|
||||
//do something
|
||||
throw new RuntimeException("method insn: unsupported opcode " + ops.getOrDefault(opcode, "" + opcode));
|
||||
}
|
||||
} else if (opcode == Opcodes.INVOKEINTERFACE) {
|
||||
try {
|
||||
if (name.equals("contains")
|
||||
&& owner.startsWith("java/util/")
|
||||
&& Collection.class.isAssignableFrom(Class.forName(owner.replace("/", ".")))) {
|
||||
var element = varStack.pop();
|
||||
var collection = varStack.pop();
|
||||
// System.out.println("element: " + element);
|
||||
// System.out.println("collection: " + collection);
|
||||
varStack.push(new BinaryExpression(element, collection, BinaryExpression.Operator.IN));
|
||||
}
|
||||
} catch (ClassNotFoundException e) {
|
||||
throw new RuntimeException("method insn: ", e);
|
||||
}
|
||||
} else {
|
||||
throw new RuntimeException("method insn: unsupported opcode " + ops.getOrDefault(opcode, "" + opcode));
|
||||
}
|
||||
super.visitMethodInsn(opcode, owner, name, descriptor, isInterface);
|
||||
debugExpr();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitVarInsn(int opcode, int varIndex) {
|
||||
System.out.println("var insn: " + ops.getOrDefault(opcode, "" + opcode) + ", " + varIndex);
|
||||
if (opcode == Opcodes.ALOAD) {
|
||||
if (varIndex == parameterClasses.length - 1) {
|
||||
varStack.push(new ConstantExpression("predicate param"));
|
||||
} else {
|
||||
varStack.push(new ParameterExpression(varIndex, args[varIndex], varIndex == parameterClasses.length - 1));
|
||||
}
|
||||
} else {
|
||||
throw new RuntimeException("var insn: unsupported opcode " + ops.getOrDefault(opcode, "" + opcode));
|
||||
}
|
||||
super.visitVarInsn(opcode, varIndex);
|
||||
debugExpr();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitInsn(int opcode) {
|
||||
System.out.println("insn: " + ops.getOrDefault(opcode, "" + opcode));
|
||||
if (opcode == Opcodes.ICONST_1) {
|
||||
varStack.push(ConstantExpression.I1);
|
||||
} else if (opcode == Opcodes.ICONST_0) {
|
||||
varStack.push(ConstantExpression.I0);
|
||||
} else if (opcode == Opcodes.IRETURN) {
|
||||
//collapse conditions
|
||||
for (int i = condStack.size() - 1; i >= 0; i--) {
|
||||
condStack.get(i).e1 = varStack.pop();
|
||||
varStack = condStack.get(i).varStack;
|
||||
evalCond(condStack.get(i));
|
||||
}
|
||||
// condStack.clear();
|
||||
prediacteExpr = varStack.pop();
|
||||
} else {
|
||||
throw new RuntimeException("insn: unsupported opcode " + ops.getOrDefault(opcode, "" + opcode));
|
||||
}
|
||||
|
||||
// if (!mpgotoconds.isEmpty()) {
|
||||
// var e1 = varStack.pop();
|
||||
// for (int i = mpgotoconds.size() - 1; i >= 0; i--) {
|
||||
// var cond = mpgotoconds.get(i);
|
||||
// cond.e1 = e1;
|
||||
//// for (int j = 0; j < condStack.size() && condStack.get(j) != cond; j++) {
|
||||
//// condStack.get(j).e1 = e1;
|
||||
//// }
|
||||
// condStack.remove(cond);
|
||||
// varStack = cond.varStack;
|
||||
// evalCond(cond);
|
||||
// }
|
||||
// mpgotoconds = new ArrayList<>();
|
||||
// }
|
||||
|
||||
super.visitInsn(opcode);
|
||||
debugExpr();
|
||||
}
|
||||
|
||||
@ToString
|
||||
class Cond {
|
||||
final int opcode;
|
||||
final Label condTarget;
|
||||
Label gotoTarget;
|
||||
final Stack<Expression> varStack;
|
||||
Expression e1;
|
||||
Expression e2;
|
||||
|
||||
public Cond(int opcode, Label condTarget, Stack<Expression> varStack) {
|
||||
this.opcode = opcode;
|
||||
this.condTarget = condTarget;
|
||||
this.varStack = varStack;
|
||||
}
|
||||
}
|
||||
|
||||
private LinkedList<Cond> condStack = new LinkedList<>();
|
||||
|
||||
@Override
|
||||
public void visitJumpInsn(int opcode, Label label) {
|
||||
System.out.println("jump insn: " + ops.getOrDefault(opcode, "" + opcode) + ", " + label);
|
||||
|
||||
switch (opcode) {
|
||||
case Opcodes.IFEQ:
|
||||
case Opcodes.IFNE:
|
||||
case Opcodes.IF_ICMPNE:
|
||||
case Opcodes.IF_ICMPEQ:
|
||||
case Opcodes.IF_ICMPGE:
|
||||
case Opcodes.IF_ICMPLE:
|
||||
case Opcodes.IF_ICMPGT:
|
||||
case Opcodes.IF_ICMPLT:
|
||||
handleLextLabel();
|
||||
condStack.add(new Cond(opcode, label, varStack));
|
||||
varStack = new Stack<>();
|
||||
System.out.println("jump new stack");
|
||||
break;
|
||||
case Opcodes.GOTO:
|
||||
handleLextLabel();
|
||||
System.out.println("goto new stack");
|
||||
condStack.getLast().gotoTarget = label;
|
||||
condStack.getLast().e1 = varStack.pop();
|
||||
varStack = new Stack<>();
|
||||
break;
|
||||
default: {
|
||||
throw new RuntimeException("jump insn: unsupported opcode " + ops.getOrDefault(opcode, "" + opcode));
|
||||
}
|
||||
}
|
||||
|
||||
// int IFEQ = 153; // visitJumpInsn
|
||||
// int IFNE = 154; // -
|
||||
// int IFLT = 155; // -
|
||||
// int IFGE = 156; // -
|
||||
// int IFGT = 157; // -
|
||||
// int IFLE = 158; // -
|
||||
// int IF_ICMPEQ = 159; // -
|
||||
// int IF_ICMPNE = 160; // -
|
||||
// int IF_ICMPLT = 161; // -
|
||||
// int IF_ICMPGE = 162; // -
|
||||
// int IF_ICMPGT = 163; // -
|
||||
// int IF_ICMPLE = 164; // -
|
||||
// int IF_ACMPEQ = 165; // -
|
||||
// int IF_ACMPNE = 166; // -
|
||||
// int GOTO = 167; // -
|
||||
// int JSR = 168; // -
|
||||
// int RET = 169; // visitVarInsn
|
||||
|
||||
super.visitJumpInsn(opcode, label);
|
||||
debugExpr();
|
||||
}
|
||||
|
||||
List<Cond> nextLabelConds = new ArrayList<>();
|
||||
|
||||
private void handleLextLabel() {
|
||||
if (!nextLabelConds.isEmpty()) {
|
||||
var e1 = varStack.peek();
|
||||
for (int i = 0; i < nextLabelConds.size(); i++) {
|
||||
// for (int i = nextLabelConds.size() - 1; i >= 0; i--) {
|
||||
var cond = nextLabelConds.get(i);
|
||||
cond.e2 = e1;
|
||||
// for (int j = 0; j < condStack.size() && condStack.get(j) != cond; j++) {
|
||||
// condStack.get(j).e1 = e1;
|
||||
// }
|
||||
// condStack.remove(cond);
|
||||
// varStack = cond.varStack;
|
||||
// evalCond(cond);
|
||||
}
|
||||
nextLabelConds = new ArrayList<>();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitLabel(Label label) {
|
||||
System.out.println("label: " + label);
|
||||
handleLextLabel();
|
||||
|
||||
// System.out.println("------>");
|
||||
// System.out.println("condstack: " + condStack);
|
||||
List<Cond> conds = condStack.stream().filter(e -> e.gotoTarget != null && e.gotoTarget.equals(label)).toList();
|
||||
nextLabelConds = condStack.stream().filter(e -> e.gotoTarget == null && e.condTarget.equals(label)).toList();
|
||||
|
||||
// List<Cond> conds = condStack.stream().filter(e -> e.gotoTarget != null
|
||||
// ? e.gotoTarget.equals(label)
|
||||
// : e.condTarget.equals(label)//bad
|
||||
// ).toList();
|
||||
if (!conds.isEmpty()) {
|
||||
Expression e2 = varStack.pop();
|
||||
for (int i = conds.size() - 1; i >= 0; i--) {
|
||||
var cond = conds.get(i);
|
||||
// System.out.println("condstack pop " + cond);
|
||||
cond.e2 = e2;
|
||||
//also set e2 for all outer conds
|
||||
// for (int j = 0; j < condStack.size() && condStack.get(j) != cond; j++) {
|
||||
// condStack.get(j).e2 = e2;
|
||||
// }
|
||||
condStack.remove(cond);
|
||||
varStack = cond.varStack;
|
||||
evalCond(cond);
|
||||
}
|
||||
}
|
||||
// System.out.println("<------");
|
||||
super.visitLabel(label);
|
||||
debugExpr();
|
||||
}
|
||||
|
||||
Label collapseAfterNextInstruction = null;
|
||||
|
||||
private void evalCond(Cond cond) {
|
||||
var right = varStack.pop();
|
||||
|
||||
// System.out.println("left: " + left);
|
||||
// System.out.println("cond.e1: " + cond.e1);
|
||||
// System.out.println("cond.e2: " + cond.e2);
|
||||
boolean wrapInTernary = cond.e1 != ConstantExpression.I1 || cond.e2 != ConstantExpression.I0;
|
||||
// boolean wrapInTernary = true;
|
||||
Expression expr;
|
||||
// 153, "IFEQ",
|
||||
// 154, "IFNE",
|
||||
// 155, "IFLT",
|
||||
// 156, "IFGE",
|
||||
// 157, "IFGT",
|
||||
// 158, "IFLE",
|
||||
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);
|
||||
break;
|
||||
|
||||
default: {
|
||||
var left = varStack.pop();
|
||||
switch (cond.opcode) {
|
||||
case Opcodes.IF_ICMPEQ:
|
||||
expr = new BinaryExpression(left, right, BinaryExpression.Operator.NE);
|
||||
break;
|
||||
case Opcodes.IF_ICMPNE:
|
||||
expr = new BinaryExpression(left, right, BinaryExpression.Operator.EQ);
|
||||
break;
|
||||
case Opcodes.IF_ICMPLT:
|
||||
expr = new BinaryExpression(left, right, BinaryExpression.Operator.GE);
|
||||
break;
|
||||
case Opcodes.IF_ICMPGE:
|
||||
expr = new BinaryExpression(left, right, BinaryExpression.Operator.LT);
|
||||
break;
|
||||
case Opcodes.IF_ICMPGT:
|
||||
expr = new BinaryExpression(left, right, BinaryExpression.Operator.LE);
|
||||
break;
|
||||
case Opcodes.IF_ICMPLE:
|
||||
expr = new BinaryExpression(left, right, BinaryExpression.Operator.GT);
|
||||
break;
|
||||
default:
|
||||
throw new RuntimeException("jump insn: unsupported opcode " + ops.getOrDefault(cond.opcode, "" + cond.opcode));
|
||||
}
|
||||
}
|
||||
}
|
||||
if (wrapInTernary) {
|
||||
expr = new TernaryExpression(expr, cond.e1, cond.e2);
|
||||
}
|
||||
|
||||
varStack.push(expr);
|
||||
debugExpr();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitAttribute(Attribute attribute) {
|
||||
System.out.println("attr: " + attribute);
|
||||
super.visitAttribute(attribute);
|
||||
debugExpr();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitLdcInsn(Object value) {
|
||||
System.out.println("ldc: " + value);
|
||||
super.visitLdcInsn(value);
|
||||
debugExpr();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitEnd() {
|
||||
System.out.println("end");
|
||||
|
||||
super.visitEnd();
|
||||
exprConsumer.accept(prediacteExpr);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitIntInsn(int opcode, int operand) {
|
||||
System.out.println("intinsn: " + ops.getOrDefault(opcode, "" + opcode) + ", " + operand);
|
||||
switch (opcode) {
|
||||
case Opcodes.SIPUSH:
|
||||
varStack.push(new ConstantExpression(operand));
|
||||
break;
|
||||
default:
|
||||
throw new RuntimeException("intinsn: unsupported opcode " + ops.getOrDefault(opcode, "" + opcode));
|
||||
}
|
||||
super.visitIntInsn(opcode, operand);
|
||||
debugExpr();
|
||||
}
|
||||
|
||||
private Map<Integer, String> ops = createOpsMap(
|
||||
3, "ICONST_0",
|
||||
4, "ICONST_1",
|
||||
17, "SIPUSH",
|
||||
25, "ALOAD",
|
||||
|
||||
//jmp
|
||||
153, "IFEQ",
|
||||
154, "IFNE",
|
||||
155, "IFLT",
|
||||
156, "IFGE",
|
||||
157, "IFGT",
|
||||
158, "IFLE",
|
||||
159, "IF_ICMPEQ",
|
||||
160, "IF_ICMPNE",
|
||||
161, "IF_ICMPLT",
|
||||
162, "IF_ICMPGE",
|
||||
163, "IF_ICMPGT",
|
||||
164, "IF_ICMPLE",
|
||||
165, "IF_ACMPEQ",
|
||||
166, "IF_ACMPNE",
|
||||
167, "GOTO",
|
||||
168, "JSR",
|
||||
169, "RET",
|
||||
|
||||
172, "IRETURN",
|
||||
|
||||
//field
|
||||
180, "GETFIELD",
|
||||
181, "PUTFIELD",
|
||||
182, "INVOKEVIRTUAL",
|
||||
183, "INVOKESPECIAL",
|
||||
184, "INVOKESTATIC",
|
||||
185, "INVOKEINTERFACE",
|
||||
186, "INVOKEDYNAMIC"
|
||||
);
|
||||
|
||||
private static Map<Integer, String> createOpsMap(Object... o) {
|
||||
return IntStream.range(0, o.length / 2).boxed().collect(Collectors.toMap(i -> (int) o[i * 2], i -> (String) o[i * 2 + 1]));
|
||||
}
|
||||
|
||||
private void debugExpr() {
|
||||
if (!varStack.isEmpty()) {
|
||||
System.out.println("-------------------> " + new WhereExpression(new SelectExpression(List.of("*"), new TableExpression("dummy"), ""), varStack.peek()));
|
||||
}
|
||||
}
|
||||
}
|
||||
60
src/main/java/jef/asm/PredicateParser.java
Normal file
60
src/main/java/jef/asm/PredicateParser.java
Normal file
@@ -0,0 +1,60 @@
|
||||
package jef.asm;
|
||||
|
||||
import jef.expressions.Expression;
|
||||
import jef.serializable.SerializablePredicate;
|
||||
import lombok.Getter;
|
||||
import org.objectweb.asm.ClassReader;
|
||||
import org.objectweb.asm.Opcodes;
|
||||
|
||||
import java.io.InputStream;
|
||||
import java.lang.invoke.SerializedLambda;
|
||||
import java.util.stream.IntStream;
|
||||
|
||||
@Getter
|
||||
public class PredicateParser {
|
||||
private final SerializablePredicate<?> predicate;
|
||||
|
||||
public PredicateParser(SerializablePredicate<?> predicate) {
|
||||
this.predicate = predicate;
|
||||
}
|
||||
|
||||
public Expression parse() throws AsmParseException {
|
||||
try {
|
||||
return parseExpression();
|
||||
} catch (Exception e) {
|
||||
throw new AsmParseException("PredicateParser: failed to parse expression", e);
|
||||
}
|
||||
}
|
||||
|
||||
private Expression parseExpression() throws Exception {
|
||||
var cls = predicate.getClass();
|
||||
var loader = cls.getClassLoader();
|
||||
InputStream is;
|
||||
// System.out.println(cls);
|
||||
// System.out.println(cls.getName());
|
||||
if (cls.getName().contains("$$Lambda$")) {
|
||||
// System.out.println(cls.getName().split("\\$\\$")[0].replace(".", "/") + ".class");
|
||||
is = loader.getResourceAsStream(cls.getName().split("\\$\\$")[0].replace(".", "/") + ".class");
|
||||
} else {
|
||||
// System.out.println(cls.getName().replace(".", "/") + ".class");
|
||||
is = loader.getResourceAsStream(cls.getName().replace(".", "/") + ".class");
|
||||
}
|
||||
var x = cls.getDeclaredMethod("writeReplace");
|
||||
// System.out.println(x);
|
||||
x.setAccessible(true);
|
||||
var serlambda = (SerializedLambda) x.invoke(predicate);
|
||||
Object[] args = IntStream.range(0, serlambda.getCapturedArgCount()).mapToObj(serlambda::getCapturedArg).toArray();
|
||||
// System.out.println(serlambda);
|
||||
|
||||
var lambdaname = serlambda.getImplMethodName();
|
||||
// System.out.println(lambdaname);
|
||||
|
||||
var expr = new Expression[1];
|
||||
|
||||
var cr = new ClassReader(is);
|
||||
var visiter = new FilterClassVisitor(Opcodes.ASM9, lambdaname, args, e -> expr[0] = e);
|
||||
cr.accept(visiter, 0);
|
||||
|
||||
return expr[0];
|
||||
}
|
||||
}
|
||||
33
src/main/java/jef/expressions/AndExpression.java
Normal file
33
src/main/java/jef/expressions/AndExpression.java
Normal file
@@ -0,0 +1,33 @@
|
||||
package jef.expressions;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
@Getter
|
||||
@AllArgsConstructor
|
||||
public class AndExpression implements Expression {
|
||||
private final List<Expression> exprs;
|
||||
|
||||
public AndExpression(Expression... exprs) {
|
||||
this.exprs = List.of(exprs);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Type getType() {
|
||||
return Type.AND;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return exprs.stream().map(e -> {
|
||||
if (e instanceof OrExpression) {
|
||||
return "(" + e + ")";
|
||||
} else {
|
||||
return e.toString();
|
||||
}
|
||||
}).collect(Collectors.joining(" AND "));
|
||||
}
|
||||
}
|
||||
60
src/main/java/jef/expressions/BinaryExpression.java
Normal file
60
src/main/java/jef/expressions/BinaryExpression.java
Normal file
@@ -0,0 +1,60 @@
|
||||
package jef.expressions;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
@Getter
|
||||
@AllArgsConstructor
|
||||
public class BinaryExpression implements Expression {
|
||||
private final Expression left;
|
||||
private final Expression right;
|
||||
private final Operator operator;
|
||||
|
||||
@Override
|
||||
public Type getType() {
|
||||
return Type.BINARY;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return left + " " + operator + " " + right;
|
||||
}
|
||||
|
||||
@AllArgsConstructor
|
||||
public enum Operator {
|
||||
EQ("="),
|
||||
NE("<>"),
|
||||
LT("<"),
|
||||
LE("<="),
|
||||
GT(">"),
|
||||
GE(">="),
|
||||
// OR("OR"),
|
||||
// AND("AND"),
|
||||
IN("IN"),
|
||||
;
|
||||
private final String string;
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return string;
|
||||
}
|
||||
|
||||
private static final Map<Operator, Operator> INVERSION = Map.of(
|
||||
EQ, NE,
|
||||
NE, EQ,
|
||||
LT, GE,
|
||||
GE, LT,
|
||||
LE, GT,
|
||||
GT, LE);
|
||||
|
||||
public Operator invert() {
|
||||
return INVERSION.get(this);
|
||||
}
|
||||
|
||||
public boolean isInvertible() {
|
||||
return INVERSION.containsKey(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
23
src/main/java/jef/expressions/ConstantExpression.java
Normal file
23
src/main/java/jef/expressions/ConstantExpression.java
Normal file
@@ -0,0 +1,23 @@
|
||||
package jef.expressions;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
|
||||
@Getter
|
||||
@AllArgsConstructor
|
||||
public class ConstantExpression implements Expression {
|
||||
public static final jef.expressions.ConstantExpression I0 = new jef.expressions.ConstantExpression(0);
|
||||
public static final jef.expressions.ConstantExpression I1 = new jef.expressions.ConstantExpression(1);
|
||||
|
||||
protected final Object value;
|
||||
|
||||
@Override
|
||||
public Type getType() {
|
||||
return Type.CONSTANT;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return value.toString();
|
||||
}
|
||||
}
|
||||
19
src/main/java/jef/expressions/Expression.java
Normal file
19
src/main/java/jef/expressions/Expression.java
Normal file
@@ -0,0 +1,19 @@
|
||||
package jef.expressions;
|
||||
|
||||
public interface Expression {
|
||||
Type getType();
|
||||
|
||||
public enum Type {
|
||||
AND,
|
||||
BINARY,
|
||||
CONSTANT,
|
||||
FIELD,
|
||||
OR,
|
||||
PARAMETER,
|
||||
SELECT,
|
||||
TABLE,
|
||||
TERNARY,
|
||||
UNARY,
|
||||
WHERE,
|
||||
}
|
||||
}
|
||||
23
src/main/java/jef/expressions/FieldExpression.java
Normal file
23
src/main/java/jef/expressions/FieldExpression.java
Normal file
@@ -0,0 +1,23 @@
|
||||
package jef.expressions;
|
||||
|
||||
import lombok.Getter;
|
||||
|
||||
@Getter
|
||||
public class FieldExpression extends ConstantExpression implements Expression {
|
||||
private final String name;
|
||||
|
||||
public FieldExpression(String name) {
|
||||
super(name);
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Type getType() {
|
||||
return Type.FIELD;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return name;
|
||||
}
|
||||
}
|
||||
27
src/main/java/jef/expressions/OrExpression.java
Normal file
27
src/main/java/jef/expressions/OrExpression.java
Normal file
@@ -0,0 +1,27 @@
|
||||
package jef.expressions;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
@Getter
|
||||
@AllArgsConstructor
|
||||
public class OrExpression implements Expression {
|
||||
private final List<Expression> exprs;
|
||||
|
||||
public OrExpression(Expression... exprs) {
|
||||
this.exprs = List.of(exprs);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Type getType() {
|
||||
return Type.OR;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return exprs.stream().map(Expression::toString).collect(Collectors.joining(" OR "));
|
||||
}
|
||||
}
|
||||
35
src/main/java/jef/expressions/ParameterExpression.java
Normal file
35
src/main/java/jef/expressions/ParameterExpression.java
Normal file
@@ -0,0 +1,35 @@
|
||||
package jef.expressions;
|
||||
|
||||
import lombok.Getter;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
@Getter
|
||||
public class ParameterExpression extends ConstantExpression implements Expression {
|
||||
private final int index;
|
||||
private final boolean isInput;
|
||||
|
||||
public ParameterExpression(int index, Object value, boolean isInput) {
|
||||
super(value);
|
||||
this.index = index;
|
||||
this.isInput = isInput;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Type getType() {
|
||||
return Type.PARAMETER;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
if (isInput) {
|
||||
return "param #" + index;
|
||||
} 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 value.toString();
|
||||
}
|
||||
}
|
||||
27
src/main/java/jef/expressions/SelectExpression.java
Normal file
27
src/main/java/jef/expressions/SelectExpression.java
Normal file
@@ -0,0 +1,27 @@
|
||||
package jef.expressions;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
@Getter
|
||||
@AllArgsConstructor
|
||||
public class SelectExpression implements Expression {
|
||||
private final List<String> fields;
|
||||
private final Expression from;
|
||||
private final String fromAlias;
|
||||
|
||||
@Override
|
||||
public Type getType() {
|
||||
return Type.SELECT;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "SELECT " + fields.stream().map(e -> e.equals("*") ? e : "`" + e + "`").collect(Collectors.joining(", ")) + " FROM "
|
||||
+ (!(from instanceof TableExpression) ? "(" + from + ")" : from)
|
||||
+ ((fromAlias == null || fromAlias.isBlank()) ? "" : " " + fromAlias);
|
||||
}
|
||||
}
|
||||
28
src/main/java/jef/expressions/TableExpression.java
Normal file
28
src/main/java/jef/expressions/TableExpression.java
Normal file
@@ -0,0 +1,28 @@
|
||||
package jef.expressions;
|
||||
|
||||
import lombok.Getter;
|
||||
|
||||
@Getter
|
||||
public class TableExpression extends ConstantExpression {
|
||||
private final String name;
|
||||
|
||||
public TableExpression(String name) {
|
||||
super(name);
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Type getType() {
|
||||
return Type.TABLE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getValue() {
|
||||
return (String) super.getValue();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "`" + super.toString() + "`";
|
||||
}
|
||||
}
|
||||
24
src/main/java/jef/expressions/TernaryExpression.java
Normal file
24
src/main/java/jef/expressions/TernaryExpression.java
Normal file
@@ -0,0 +1,24 @@
|
||||
package jef.expressions;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
|
||||
@Getter
|
||||
@AllArgsConstructor
|
||||
public class TernaryExpression implements Expression {
|
||||
private final Expression cond;
|
||||
private final Expression whenTrue;
|
||||
private final Expression whenFalse;
|
||||
|
||||
@Override
|
||||
public Type getType() {
|
||||
return Type.TERNARY;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return cond
|
||||
+ " ? " + (!(whenTrue instanceof ConstantExpression) ? "(" + whenTrue + ")" : whenTrue)
|
||||
+ " : " + (!(whenFalse instanceof ConstantExpression) ? "(" + whenFalse + ")" : whenFalse);
|
||||
}
|
||||
}
|
||||
35
src/main/java/jef/expressions/UnaryExpression.java
Normal file
35
src/main/java/jef/expressions/UnaryExpression.java
Normal file
@@ -0,0 +1,35 @@
|
||||
package jef.expressions;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
|
||||
@Getter
|
||||
@AllArgsConstructor
|
||||
public class UnaryExpression implements Expression {
|
||||
private final Expression expr;
|
||||
private final Operator operator;
|
||||
|
||||
@Override
|
||||
public Type getType() {
|
||||
return Type.UNARY;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return operator + " (" + expr + ")";
|
||||
}
|
||||
|
||||
@AllArgsConstructor
|
||||
public enum Operator {
|
||||
NOT("NOT"),
|
||||
// NEG("-"),
|
||||
// POS("+"),
|
||||
;
|
||||
private final String string;
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return string;
|
||||
}
|
||||
}
|
||||
}
|
||||
21
src/main/java/jef/expressions/WhereExpression.java
Normal file
21
src/main/java/jef/expressions/WhereExpression.java
Normal file
@@ -0,0 +1,21 @@
|
||||
package jef.expressions;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
|
||||
@Getter
|
||||
@AllArgsConstructor
|
||||
public class WhereExpression implements Expression {
|
||||
private Expression queryable;
|
||||
private Expression where;
|
||||
|
||||
@Override
|
||||
public Type getType() {
|
||||
return Type.WHERE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return queryable + " WHERE " + where;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,89 @@
|
||||
package jef.expressions.modifier;
|
||||
|
||||
import jef.expressions.AndExpression;
|
||||
import jef.expressions.BinaryExpression;
|
||||
import jef.expressions.ConstantExpression;
|
||||
import jef.expressions.Expression;
|
||||
import jef.expressions.FieldExpression;
|
||||
import jef.expressions.OrExpression;
|
||||
import jef.expressions.ParameterExpression;
|
||||
import jef.expressions.SelectExpression;
|
||||
import jef.expressions.TableExpression;
|
||||
import jef.expressions.TernaryExpression;
|
||||
import jef.expressions.UnaryExpression;
|
||||
import jef.expressions.WhereExpression;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
|
||||
public abstract class ExpressionModifier {
|
||||
|
||||
public Expression modify(Expression expr) {
|
||||
return switch (expr.getType()) {
|
||||
case AND -> modifyAnd((AndExpression) expr);
|
||||
case BINARY -> modifyBinary((BinaryExpression) expr);
|
||||
case CONSTANT -> modifyConstant((ConstantExpression) expr);
|
||||
case FIELD -> modifyField((FieldExpression) expr);
|
||||
case OR -> modifyOr((OrExpression) expr);
|
||||
case PARAMETER -> modifyParameter((ParameterExpression) expr);
|
||||
case SELECT -> modifySelect((SelectExpression) expr);
|
||||
case TABLE -> modifyTable((TableExpression) expr);
|
||||
case TERNARY -> modifyTernary((TernaryExpression) expr);
|
||||
case UNARY -> modifyUnary((UnaryExpression) expr);
|
||||
case WHERE -> modifyWhere((WhereExpression) expr);
|
||||
default -> throw new IllegalStateException();
|
||||
};
|
||||
}
|
||||
|
||||
public Expression modifyAnd(AndExpression expr) {
|
||||
var exprs = new ArrayList<Expression>(expr.getExprs().size());
|
||||
for (Expression e : expr.getExprs()) {
|
||||
exprs.add(modify(e));
|
||||
}
|
||||
return new AndExpression(exprs);
|
||||
}
|
||||
|
||||
public Expression modifyBinary(BinaryExpression expr) {
|
||||
return new BinaryExpression(modify(expr.getLeft()), modify(expr.getRight()), expr.getOperator());
|
||||
}
|
||||
|
||||
public Expression modifyConstant(ConstantExpression expr) {
|
||||
return expr;
|
||||
}
|
||||
|
||||
public Expression modifyField(FieldExpression expr) {
|
||||
return expr;
|
||||
}
|
||||
|
||||
public Expression modifyOr(OrExpression expr) {
|
||||
var exprs = new ArrayList<Expression>(expr.getExprs().size());
|
||||
for (Expression e : expr.getExprs()) {
|
||||
exprs.add(modify(e));
|
||||
}
|
||||
return new OrExpression(exprs);
|
||||
}
|
||||
|
||||
public Expression modifyParameter(ParameterExpression expr) {
|
||||
return expr;
|
||||
}
|
||||
|
||||
public Expression modifySelect(SelectExpression expr) {
|
||||
return new SelectExpression(expr.getFields(), modify(expr.getFrom()), expr.getFromAlias());
|
||||
}
|
||||
|
||||
public Expression modifyTable(TableExpression expr) {
|
||||
return expr;
|
||||
}
|
||||
|
||||
public Expression modifyTernary(TernaryExpression expr) {
|
||||
return new TernaryExpression(modify(expr.getCond()), modify(expr.getWhenTrue()), modify(expr.getWhenFalse()));
|
||||
}
|
||||
|
||||
public Expression modifyUnary(UnaryExpression expr) {
|
||||
return new UnaryExpression(modify(expr.getExpr()), expr.getOperator());
|
||||
}
|
||||
|
||||
public Expression modifyWhere(WhereExpression expr) {
|
||||
return new WhereExpression(modify(expr.getQueryable()), modify(expr.getWhere()));
|
||||
}
|
||||
}
|
||||
102
src/main/java/jef/expressions/modifier/ExpressionOptimizer.java
Normal file
102
src/main/java/jef/expressions/modifier/ExpressionOptimizer.java
Normal file
@@ -0,0 +1,102 @@
|
||||
package jef.expressions.modifier;
|
||||
|
||||
import jef.expressions.AndExpression;
|
||||
import jef.expressions.BinaryExpression;
|
||||
import jef.expressions.ConstantExpression;
|
||||
import jef.expressions.Expression;
|
||||
import jef.expressions.OrExpression;
|
||||
import jef.expressions.TernaryExpression;
|
||||
import jef.expressions.UnaryExpression;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
public class ExpressionOptimizer extends ExpressionModifier {
|
||||
@Override
|
||||
public Expression modifyAnd(AndExpression expr) {
|
||||
var ands = new ArrayList<Expression>(expr.getExprs().size() * 2);
|
||||
|
||||
//squash ands
|
||||
for (Expression e : expr.getExprs()) {
|
||||
if (e.getType() == Expression.Type.AND) {
|
||||
ands.addAll(((AndExpression) e).getExprs());
|
||||
} else {
|
||||
ands.add(e);
|
||||
}
|
||||
}
|
||||
ands.replaceAll(this::modify);
|
||||
|
||||
// x && false -> false
|
||||
for (Expression e : ands) {
|
||||
if (e == ConstantExpression.I0) {
|
||||
return ConstantExpression.I0;
|
||||
}
|
||||
}
|
||||
while (ands.remove(ConstantExpression.I1)) ;
|
||||
|
||||
return new AndExpression(ands);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Expression modifyOr(OrExpression expr) {
|
||||
var ors = new ArrayList<Expression>(expr.getExprs().size() * 2);
|
||||
|
||||
//squash ors
|
||||
for (Expression e : expr.getExprs()) {
|
||||
if (e.getType() == Expression.Type.OR) {
|
||||
ors.addAll(((OrExpression) e).getExprs());
|
||||
} else {
|
||||
ors.add(e);
|
||||
}
|
||||
}
|
||||
ors.replaceAll(this::modify);
|
||||
|
||||
// x || true -> true
|
||||
for (Expression e : ors) {
|
||||
if (e == ConstantExpression.I1) {
|
||||
return ConstantExpression.I1;
|
||||
}
|
||||
}
|
||||
while (ors.remove(ConstantExpression.I0)) ;
|
||||
|
||||
return new OrExpression(ors);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Expression modifyTernary(TernaryExpression expr) {
|
||||
if (expr.getWhenFalse() == ConstantExpression.I1 && expr.getWhenFalse() == ConstantExpression.I0) {
|
||||
//x ? 1 : 0 -> x
|
||||
return modify(expr.getCond());
|
||||
} else if (expr.getWhenFalse() == ConstantExpression.I0 && expr.getWhenFalse() == ConstantExpression.I1) {
|
||||
//x ? 0 : 1 -> !x
|
||||
return modify(new UnaryExpression(expr.getCond(), UnaryExpression.Operator.NOT));
|
||||
} else if (expr.getWhenFalse() == ConstantExpression.I0) {
|
||||
//x ? y : 0 -> x && y
|
||||
return modify(new AndExpression(expr.getCond(), expr.getWhenTrue()));
|
||||
} else if (expr.getWhenFalse() == ConstantExpression.I1) {
|
||||
//x ? y : 1 -> !x or y
|
||||
return modify(new OrExpression(new UnaryExpression(expr.getCond(), UnaryExpression.Operator.NOT), expr.getWhenTrue()));
|
||||
} else if (expr.getWhenTrue() instanceof TernaryExpression t && expr.getWhenFalse() == t.getWhenFalse()) {
|
||||
// x ? (y ? z : u) : u -> (x && y) ? z : u
|
||||
return new TernaryExpression(new AndExpression(expr.getCond(), t.getCond()), t.getWhenTrue(), t.getWhenFalse());
|
||||
} else {
|
||||
return super.modifyTernary(expr);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Expression modifyUnary(UnaryExpression expr) {
|
||||
if (expr.getExpr() instanceof UnaryExpression u
|
||||
&& expr.getOperator() == u.getOperator()
|
||||
&& expr.getOperator() == UnaryExpression.Operator.NOT) {
|
||||
//!!x -> x
|
||||
return modify(u.getExpr());
|
||||
} else if (expr.getExpr() instanceof BinaryExpression b
|
||||
&& expr.getOperator() == UnaryExpression.Operator.NOT
|
||||
&& b.getOperator().isInvertible()) {
|
||||
//!(a < b) -> a >= b
|
||||
return new BinaryExpression(b.getLeft(), b.getRight(), b.getOperator().invert());
|
||||
} else {
|
||||
return super.modifyUnary(expr);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,108 @@
|
||||
package jef.expressions.modifier;
|
||||
|
||||
import jef.expressions.AndExpression;
|
||||
import jef.expressions.BinaryExpression;
|
||||
import jef.expressions.ConstantExpression;
|
||||
import jef.expressions.Expression;
|
||||
import jef.expressions.OrExpression;
|
||||
import jef.expressions.TernaryExpression;
|
||||
import jef.expressions.UnaryExpression;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
public class ExpressionOptimizerBottomUp extends ExpressionModifier {
|
||||
@Override
|
||||
public Expression modifyAnd(AndExpression expr) {
|
||||
var andsOpt = expr.getExprs().stream().map(this::modify).toList();
|
||||
var ands = new ArrayList<Expression>(expr.getExprs().size() * 2);
|
||||
|
||||
//squash ands
|
||||
for (Expression e : andsOpt) {
|
||||
if (e.getType() == Expression.Type.AND) {
|
||||
ands.addAll(((AndExpression) e).getExprs());
|
||||
} else {
|
||||
ands.add(e);
|
||||
}
|
||||
}
|
||||
ands.replaceAll(this::modify);
|
||||
|
||||
// x && false -> false
|
||||
for (Expression e : ands) {
|
||||
if (e == ConstantExpression.I0) {
|
||||
return ConstantExpression.I0;
|
||||
}
|
||||
}
|
||||
while (ands.remove(ConstantExpression.I1)) ;
|
||||
|
||||
return new AndExpression(ands);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Expression modifyOr(OrExpression expr) {
|
||||
var orsOpt = expr.getExprs().stream().map(this::modify).toList();
|
||||
var ors = new ArrayList<Expression>(expr.getExprs().size() * 2);
|
||||
|
||||
//squash ors
|
||||
for (Expression e : orsOpt) {
|
||||
if (e.getType() == Expression.Type.OR) {
|
||||
ors.addAll(((OrExpression) e).getExprs());
|
||||
} else {
|
||||
ors.add(e);
|
||||
}
|
||||
}
|
||||
ors.replaceAll(this::modify);
|
||||
|
||||
// x || true -> true
|
||||
for (Expression e : ors) {
|
||||
if (e == ConstantExpression.I1) {
|
||||
return ConstantExpression.I1;
|
||||
}
|
||||
}
|
||||
while (ors.remove(ConstantExpression.I0)) ;
|
||||
|
||||
return new OrExpression(ors);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Expression modifyTernary(TernaryExpression expr) {
|
||||
var cond = modify(expr.getCond());
|
||||
var whenTrue = modify(expr.getWhenTrue());
|
||||
var whenFalse = modify(expr.getWhenFalse());
|
||||
if (whenTrue == ConstantExpression.I1 && whenFalse == ConstantExpression.I0) {
|
||||
//x ? 1 : 0 -> x
|
||||
return cond;
|
||||
} else if (whenTrue == ConstantExpression.I0 && whenFalse == ConstantExpression.I1) {
|
||||
//x ? 0 : 1 -> !x
|
||||
return modify(new UnaryExpression(cond, UnaryExpression.Operator.NOT));
|
||||
} else if (whenFalse == ConstantExpression.I0) {
|
||||
//x ? y : 0 -> x && y
|
||||
return modify(new AndExpression(cond, whenTrue));
|
||||
} else if (whenFalse == ConstantExpression.I1) {
|
||||
//x ? y : 1 -> !x or y
|
||||
return modify(new OrExpression(new UnaryExpression(cond, UnaryExpression.Operator.NOT), whenTrue));
|
||||
} else if (whenTrue instanceof TernaryExpression t && whenFalse == t.getWhenFalse()) {
|
||||
// x ? (y ? z : u) : u -> (x && y) ? z : u
|
||||
return new TernaryExpression(new AndExpression(cond, t.getCond()), t.getWhenTrue(), t.getWhenFalse());
|
||||
} else {
|
||||
return super.modifyTernary(expr);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Expression modifyUnary(UnaryExpression expr) {
|
||||
var inner = modify(expr.getExpr());
|
||||
if (inner instanceof UnaryExpression u
|
||||
&& expr.getOperator() == u.getOperator()
|
||||
&& expr.getOperator() == UnaryExpression.Operator.NOT) {
|
||||
//!!x -> x
|
||||
return modify(u.getExpr());
|
||||
} else if (inner instanceof BinaryExpression b
|
||||
&& expr.getOperator() == UnaryExpression.Operator.NOT
|
||||
&& b.getOperator().isInvertible()) {
|
||||
//!(a < b) -> a >= b
|
||||
return new BinaryExpression(b.getLeft(), b.getRight(), b.getOperator().invert());
|
||||
} else {
|
||||
return super.modifyUnary(expr);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
package jef.expressions.modifier;
|
||||
|
||||
import jef.expressions.Expression;
|
||||
import jef.expressions.FieldExpression;
|
||||
|
||||
public class TableAliasInjector extends ExpressionModifier {
|
||||
private final String tableAlias;
|
||||
private final String prefix;
|
||||
|
||||
public TableAliasInjector(String tableAlias) {
|
||||
this.tableAlias = tableAlias;
|
||||
this.prefix = (tableAlias == null || tableAlias.isBlank()) ? "" : tableAlias + ".";
|
||||
}
|
||||
|
||||
@Override
|
||||
public Expression modifyField(FieldExpression expr) {
|
||||
return new FieldExpression(prefix + expr.getName());
|
||||
}
|
||||
}
|
||||
20
src/main/java/jef/expressions/modifier/TernaryRewriter.java
Normal file
20
src/main/java/jef/expressions/modifier/TernaryRewriter.java
Normal file
@@ -0,0 +1,20 @@
|
||||
package jef.expressions.modifier;
|
||||
|
||||
import jef.expressions.AndExpression;
|
||||
import jef.expressions.BinaryExpression;
|
||||
import jef.expressions.ConstantExpression;
|
||||
import jef.expressions.Expression;
|
||||
import jef.expressions.OrExpression;
|
||||
import jef.expressions.TernaryExpression;
|
||||
import jef.expressions.UnaryExpression;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
public class TernaryRewriter extends ExpressionModifier {
|
||||
@Override
|
||||
public Expression modifyTernary(TernaryExpression expr) {
|
||||
return new OrExpression(new AndExpression(expr.getCond(), expr.getWhenTrue()),
|
||||
new AndExpression(new UnaryExpression(expr.getCond(), UnaryExpression.Operator.NOT), expr.getWhenFalse()));
|
||||
// return new OrExpression(new AndExpression(expr.getCond(), expr.getWhenTrue()), expr.getWhenFalse());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,128 @@
|
||||
package jef.expressions.visitors;
|
||||
|
||||
import jef.expressions.AndExpression;
|
||||
import jef.expressions.BinaryExpression;
|
||||
import jef.expressions.ConstantExpression;
|
||||
import jef.expressions.FieldExpression;
|
||||
import jef.expressions.OrExpression;
|
||||
import jef.expressions.ParameterExpression;
|
||||
import jef.expressions.SelectExpression;
|
||||
import jef.expressions.TableExpression;
|
||||
import jef.expressions.TernaryExpression;
|
||||
import jef.expressions.UnaryExpression;
|
||||
import jef.expressions.WhereExpression;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
public class DebugExpressionVisitor extends ExpressionVisitor {
|
||||
private int indent = 0;
|
||||
|
||||
@Override
|
||||
public void visitAnd(AndExpression expr) {
|
||||
for (int i = 0; i < expr.getExprs().size(); i++) {
|
||||
indent++;
|
||||
visit(expr.getExprs().get(i));
|
||||
indent--;
|
||||
if (i + 1 < expr.getExprs().size())
|
||||
System.out.println(i() + "AND");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitBinary(BinaryExpression expr) {
|
||||
indent++;
|
||||
visit(expr.getLeft());
|
||||
indent--;
|
||||
System.out.println(i() + expr.getOperator());
|
||||
indent++;
|
||||
visit(expr.getRight());
|
||||
indent--;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitConstant(ConstantExpression expr) {
|
||||
System.out.println(i() + expr.getValue());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitField(FieldExpression expr) {
|
||||
System.out.println(i() + expr.getName());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitOr(OrExpression expr) {
|
||||
for (int i = 0; i < expr.getExprs().size(); i++) {
|
||||
indent++;
|
||||
visit(expr.getExprs().get(i));
|
||||
indent--;
|
||||
if (i + 1 < expr.getExprs().size())
|
||||
System.out.println(i() + "OR");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitParameter(ParameterExpression expr) {
|
||||
if (expr.getValue() instanceof Collection c) {
|
||||
System.out.println(i() + c.stream().map(String::valueOf).collect(Collectors.joining(",")));
|
||||
} else {
|
||||
System.out.println(i() + expr.getValue());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitSelect(SelectExpression expr) {
|
||||
var table = expr.getFrom() instanceof TableExpression;
|
||||
var tableName = table ? ((TableExpression) expr.getFrom()).getName() : null;
|
||||
var tableAlias = (expr.getFromAlias() == null || expr.getFromAlias().isBlank()) ? "" : " " + expr.getFromAlias();
|
||||
System.out.println(i() + "SELECT " + expr.getFields().stream().collect(Collectors.joining(", "))
|
||||
+ " FROM" + (!table ? " (" : tableName + " " + tableAlias));
|
||||
if (!table) {
|
||||
indent++;
|
||||
visit(expr.getFrom());
|
||||
indent--;
|
||||
System.out.println(i() + ")" + tableAlias);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitTable(TableExpression expr) {
|
||||
System.out.println(i() + expr.getValue());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitTernary(TernaryExpression expr) {
|
||||
indent++;
|
||||
visit(expr.getCond());
|
||||
indent--;
|
||||
System.out.println(i() + "?");
|
||||
indent++;
|
||||
visit(expr.getWhenTrue());
|
||||
indent--;
|
||||
System.out.println(i() + ":");
|
||||
indent++;
|
||||
visit(expr.getWhenFalse());
|
||||
indent--;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitUnary(UnaryExpression expr) {
|
||||
System.out.println(i() + expr.getOperator());
|
||||
indent++;
|
||||
visit(expr.getExpr());
|
||||
indent--;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitWhere(WhereExpression expr) {
|
||||
visit(expr.getQueryable());
|
||||
System.out.println(i() + "WHERE");
|
||||
indent++;
|
||||
visit(expr.getWhere());
|
||||
indent--;
|
||||
}
|
||||
|
||||
private String i() {
|
||||
return " ".repeat(indent);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,81 @@
|
||||
package jef.expressions.visitors;
|
||||
|
||||
import jef.expressions.AndExpression;
|
||||
import jef.expressions.BinaryExpression;
|
||||
import jef.expressions.ConstantExpression;
|
||||
import jef.expressions.Expression;
|
||||
import jef.expressions.FieldExpression;
|
||||
import jef.expressions.OrExpression;
|
||||
import jef.expressions.ParameterExpression;
|
||||
import jef.expressions.SelectExpression;
|
||||
import jef.expressions.TableExpression;
|
||||
import jef.expressions.TernaryExpression;
|
||||
import jef.expressions.UnaryExpression;
|
||||
import jef.expressions.WhereExpression;
|
||||
|
||||
public abstract class ExpressionVisitor {
|
||||
public void visit(Expression expr) {
|
||||
switch (expr.getType()) {
|
||||
case AND -> visitAnd((AndExpression) expr);
|
||||
case BINARY -> visitBinary((BinaryExpression) expr);
|
||||
case CONSTANT -> visitConstant((ConstantExpression) expr);
|
||||
case FIELD -> visitField((FieldExpression) expr);
|
||||
case OR -> visitOr((OrExpression) expr);
|
||||
case PARAMETER -> visitParameter((ParameterExpression) expr);
|
||||
case SELECT -> visitSelect((SelectExpression) expr);
|
||||
case TABLE -> visitTable((TableExpression) expr);
|
||||
case TERNARY -> visitTernary((TernaryExpression) expr);
|
||||
case UNARY -> visitUnary((UnaryExpression) expr);
|
||||
case WHERE -> visitWhere((WhereExpression) expr);
|
||||
default -> throw new IllegalStateException();
|
||||
}
|
||||
}
|
||||
|
||||
public void visitAnd(AndExpression expr) {
|
||||
for (Expression e : expr.getExprs()) {
|
||||
visit(e);
|
||||
}
|
||||
}
|
||||
|
||||
public void visitBinary(BinaryExpression expr) {
|
||||
visit(expr.getLeft());
|
||||
visit(expr.getRight());
|
||||
}
|
||||
|
||||
public void visitConstant(ConstantExpression expr) {
|
||||
}
|
||||
|
||||
public void visitField(FieldExpression expr) {
|
||||
}
|
||||
|
||||
public void visitOr(OrExpression expr) {
|
||||
for (Expression e : expr.getExprs()) {
|
||||
visit(e);
|
||||
}
|
||||
}
|
||||
|
||||
public void visitParameter(ParameterExpression expr) {
|
||||
}
|
||||
|
||||
public void visitSelect(SelectExpression expr) {
|
||||
visit(expr.getFrom());
|
||||
}
|
||||
|
||||
public void visitTable(TableExpression expr) {
|
||||
}
|
||||
|
||||
public void visitTernary(TernaryExpression expr) {
|
||||
visit(expr.getCond());
|
||||
visit(expr.getWhenTrue());
|
||||
visit(expr.getWhenFalse());
|
||||
}
|
||||
|
||||
public void visitUnary(UnaryExpression expr) {
|
||||
visit(expr.getExpr());
|
||||
}
|
||||
|
||||
public void visitWhere(WhereExpression expr) {
|
||||
visit(expr.getQueryable());
|
||||
visit(expr.getWhere());
|
||||
}
|
||||
}
|
||||
58
src/main/java/jef/operations/FilterOp.java
Normal file
58
src/main/java/jef/operations/FilterOp.java
Normal file
@@ -0,0 +1,58 @@
|
||||
package jef.operations;
|
||||
|
||||
import jef.Queryable;
|
||||
import jef.asm.AsmParseException;
|
||||
import jef.asm.PredicateParser;
|
||||
import jef.expressions.Expression;
|
||||
import jef.expressions.SelectExpression;
|
||||
import jef.expressions.WhereExpression;
|
||||
import jef.expressions.modifier.ExpressionOptimizer;
|
||||
import jef.expressions.modifier.ExpressionOptimizerBottomUp;
|
||||
import jef.expressions.modifier.TableAliasInjector;
|
||||
import jef.expressions.modifier.TernaryRewriter;
|
||||
import jef.serializable.SerializablePredicate;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.List;
|
||||
import java.util.function.Predicate;
|
||||
|
||||
public class FilterOp<T extends Serializable> implements Queryable<T>, Operation<T> {
|
||||
|
||||
private final Queryable<T> queryable;
|
||||
private final Predicate<? super T> predicate;
|
||||
private final Expression predicateExpr;
|
||||
|
||||
public FilterOp(Queryable<T> queryable, SerializablePredicate<? super T> predicate) {
|
||||
this.queryable = queryable;
|
||||
this.predicate = predicate;
|
||||
var parser = new PredicateParser(predicate);
|
||||
Expression expr;
|
||||
try {
|
||||
expr = parser.parse();
|
||||
} catch (AsmParseException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
System.out.println(expr);
|
||||
expr = new TernaryRewriter().modify(expr);
|
||||
System.out.println(expr);
|
||||
expr = new ExpressionOptimizer().modify(expr);
|
||||
// expr = new ExpressionOptimizerBottomUp().modify(expr);
|
||||
expr = new TableAliasInjector(getTableAlias()).modify(expr);
|
||||
this.predicateExpr = expr;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getTableAlias() {
|
||||
return String.valueOf((char) (queryable.getTableAlias().charAt(0) + (char) 1));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Expression getExpression() {
|
||||
return new WhereExpression(new SelectExpression(List.of("*"), queryable.getExpression(), getTableAlias()), predicateExpr);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return getExpression().toString();
|
||||
}
|
||||
}
|
||||
9
src/main/java/jef/operations/Operation.java
Normal file
9
src/main/java/jef/operations/Operation.java
Normal file
@@ -0,0 +1,9 @@
|
||||
package jef.operations;
|
||||
|
||||
import jef.Queryable;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
public interface Operation<T extends Serializable> extends Queryable<T> {
|
||||
|
||||
}
|
||||
7
src/main/java/jef/serializable/SerializableFunction.java
Normal file
7
src/main/java/jef/serializable/SerializableFunction.java
Normal file
@@ -0,0 +1,7 @@
|
||||
package jef.serializable;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.function.Function;
|
||||
|
||||
public interface SerializableFunction<T, R> extends Function<T, R>, Serializable {
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
package jef.serializable;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.function.Predicate;
|
||||
|
||||
public interface SerializablePredicate<T> extends Predicate<T>, Serializable {
|
||||
}
|
||||
144
src/test/java/jef/operations/FilterOpTest.java
Normal file
144
src/test/java/jef/operations/FilterOpTest.java
Normal file
@@ -0,0 +1,144 @@
|
||||
package jef.operations;
|
||||
|
||||
import jef.DBSet;
|
||||
import org.junit.jupiter.api.Assertions;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.List;
|
||||
|
||||
public class FilterOpTest {
|
||||
|
||||
@Test
|
||||
public void testTrue() {
|
||||
// String act;
|
||||
// act = new DBSet<TestClass>("table1")
|
||||
// .filter(e -> true)
|
||||
// .toString();
|
||||
// Assertions.assertEquals("SELECT * FROM (SELECT * FROM `table1`) a WHERE 1", act);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCompareWithEntityMember() {
|
||||
String act;
|
||||
act = new DBSet<TestClass>("table1")
|
||||
.filter(e -> e.b == 1)
|
||||
.toString();
|
||||
Assertions.assertEquals("SELECT * FROM (SELECT * FROM `table1`) a WHERE a.b = 1", act);
|
||||
|
||||
|
||||
act = new DBSet<TestClass>("table1")
|
||||
.filter(e -> e.b != 1)
|
||||
.toString();
|
||||
Assertions.assertEquals("SELECT * FROM (SELECT * FROM `table1`) a WHERE a.b <> 1", act);
|
||||
|
||||
|
||||
act = new DBSet<TestClass>("table1")
|
||||
.filter(e -> e.b < 1)
|
||||
.toString();
|
||||
Assertions.assertEquals("SELECT * FROM (SELECT * FROM `table1`) a WHERE a.b < 1", act);
|
||||
|
||||
|
||||
act = new DBSet<TestClass>("table1")
|
||||
.filter(e -> e.b > 1)
|
||||
.toString();
|
||||
Assertions.assertEquals("SELECT * FROM (SELECT * FROM `table1`) a WHERE a.b > 1", act);
|
||||
|
||||
|
||||
act = new DBSet<TestClass>("table1")
|
||||
.filter(e -> e.b <= 1)
|
||||
.toString();
|
||||
Assertions.assertEquals("SELECT * FROM (SELECT * FROM `table1`) a WHERE a.b <= 1", act);
|
||||
|
||||
|
||||
act = new DBSet<TestClass>("table1")
|
||||
.filter(e -> e.b >= 1)
|
||||
.toString();
|
||||
Assertions.assertEquals("SELECT * FROM (SELECT * FROM `table1`) a WHERE a.b >= 1", act);
|
||||
|
||||
|
||||
act = new DBSet<TestClass>("table1")
|
||||
.filter(e -> e.b == 1337)
|
||||
.toString();
|
||||
Assertions.assertEquals("SELECT * FROM (SELECT * FROM `table1`) a WHERE a.b = 1337", act);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testContainsWithEntityMember() {
|
||||
var s = List.of(1, 3);
|
||||
String act;
|
||||
act = new DBSet<TestClass>("table1")
|
||||
.filter(e -> s.contains(e.b))
|
||||
.toString();
|
||||
Assertions.assertEquals("SELECT * FROM (SELECT * FROM `table1`) a WHERE a.b IN (1,3)", act);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMultipleFilter() {
|
||||
String act;
|
||||
var s = List.of(1, 3);
|
||||
act = new DBSet<TestClass>("table1")
|
||||
.filter(e -> s.contains(e.b))
|
||||
.filter(e -> e.b == 1337)
|
||||
.toString();
|
||||
Assertions.assertEquals("SELECT * FROM (SELECT * FROM (SELECT * FROM `table1`) a WHERE a.b IN (1,3)) b WHERE b.b = 1337", act);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testComplexExpression() {
|
||||
String act;
|
||||
var s = List.of(1, 3);
|
||||
act = new DBSet<TestClass>("table1")
|
||||
.filter(e -> s.contains(e.b) && e.b == 1337)
|
||||
.toString();
|
||||
Assertions.assertEquals("SELECT * FROM (SELECT * FROM `table1`) a WHERE a.b IN (1,3) AND a.b = 1337", act);
|
||||
|
||||
|
||||
act = new DBSet<TestClass>("table1")
|
||||
.filter(e -> s.contains(e.b) && e.b == 1337 && e.b == 420)
|
||||
.toString();
|
||||
Assertions.assertEquals("SELECT * FROM (SELECT * FROM `table1`) a WHERE a.b IN (1,3) AND a.b = 1337 AND a.b = 420", act);
|
||||
|
||||
|
||||
act = new DBSet<TestClass>("table1")
|
||||
.filter(e -> e.b == 1337 || e.b != 420 || s.contains(e.b))
|
||||
.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<TestClass>("table1")
|
||||
.filter(e -> e.b == 1337 || e.b == 420 || s.contains(e.b))
|
||||
.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<TestClass>("table1")
|
||||
.filter(e -> e.b == 1337 || s.contains(e.b) || e.b == 420)
|
||||
.toString();
|
||||
Assertions.assertEquals("SELECT * FROM (SELECT * FROM `table1`) a WHERE a.b = 1337 OR a.b IN (1,3) OR a.b = 420", act);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testComplexExpressionMixedAndOr() {
|
||||
String act;
|
||||
var s = List.of(1, 3);
|
||||
act = new DBSet<TestClass>("table1")
|
||||
.filter(e -> s.contains(e.b) && (e.b == 1337 || e.b == 420))
|
||||
.toString();
|
||||
Assertions.assertEquals("SELECT * FROM (SELECT * FROM `table1`) a WHERE a.b IN (1,3) AND (a.b = 1337 OR a.b = 420)", act);
|
||||
|
||||
|
||||
act = new DBSet<TestClass>("table1")
|
||||
.filter(e -> (e.b == 1337 || e.b == 420) && s.contains(e.b))
|
||||
.toString();
|
||||
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 test() {
|
||||
|
||||
}
|
||||
|
||||
public static class TestClass implements Serializable {
|
||||
public int b = 1;
|
||||
}
|
||||
}
|
||||
23
src/test/java/jef/visitors/DebugExpressionVisitorTest.java
Normal file
23
src/test/java/jef/visitors/DebugExpressionVisitorTest.java
Normal file
@@ -0,0 +1,23 @@
|
||||
package jef.visitors;
|
||||
|
||||
import jef.DBSet;
|
||||
import jef.Queryable;
|
||||
import jef.expressions.visitors.DebugExpressionVisitor;
|
||||
import jef.operations.FilterOpTest;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
class DebugExpressionVisitorTest {
|
||||
@Test
|
||||
public void test() {
|
||||
var s = List.of(1, 3);
|
||||
Queryable<FilterOpTest.TestClass> q = new DBSet<FilterOpTest.TestClass>("table1")
|
||||
.filter(e -> s.contains(e.b) && e.b == 1337 && e.b == 420);
|
||||
new DebugExpressionVisitor().visit(q.getExpression());
|
||||
|
||||
Queryable<FilterOpTest.TestClass> q2 = new DBSet<FilterOpTest.TestClass>("table1")
|
||||
.filter(e -> s.contains(e.b) || e.b == 1337);
|
||||
new DebugExpressionVisitor().visit(q2.getExpression());
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user