This commit is contained in:
wea_ondara
2022-12-20 17:33:29 +01:00
committed by apucher
parent c0f7b919f7
commit b805eed622
34 changed files with 2319 additions and 35 deletions

View File

@@ -4,19 +4,24 @@ import jef.expressions.Expression;
import jef.expressions.SelectExpression;
import jef.expressions.TableExpression;
import jef.expressions.selectable.DatabaseSelectAllExpression;
import jef.model.DbContext;
import jef.serializable.SerializableObject;
import lombok.Getter;
import java.sql.SQLException;
import java.util.Iterator;
import java.util.List;
import java.util.Spliterator;
public class DbSet<T extends SerializableObject> implements Queryable<T> {
@Getter
private final DbContext context;
@Getter
private final Class<T> clazz;
private final String table;
public DbSet(Class<T> clazz, String table) {
public DbSet(DbContext context, Class<T> clazz, String table) {
this.context = context;
this.clazz = clazz;
this.table = table;
}
@@ -36,6 +41,23 @@ public class DbSet<T extends SerializableObject> implements Queryable<T> {
return "SELECT * FROM `" + table + "`";
}
@Override
public DbSet<T> originalSet() {
return this;
}
public void add(T entity) throws SQLException {
context.getDatabase().add(context, clazz, entity);
}
public void update(T entity) throws SQLException {
context.getDatabase().update(context, clazz, entity);
}
public void delete(T entity) throws SQLException {
context.getDatabase().delete(context, clazz, entity);
}
@Override
public Iterator<T> iterator() {
return null;

View File

@@ -6,13 +6,15 @@ import jef.operations.FilterOp;
import jef.operations.LimitOp;
import jef.operations.SortOp;
import jef.serializable.SerializableFunction;
import jef.serializable.SerializableObject;
import jef.serializable.SerializablePredicate;
import java.io.Serializable;
import java.sql.SQLException;
import java.util.Iterator;
import java.util.List;
import java.util.Spliterator;
public interface Queryable<T extends Serializable> {
public interface Queryable<T extends SerializableObject> {
//TODO documentation
//TODO table alias thing still not ready
@@ -22,6 +24,8 @@ public interface Queryable<T extends Serializable> {
String toString();
DbSet<T> originalSet();
//stream functions
default Iterator<T> iterator() {
return null;
@@ -131,6 +135,9 @@ public interface Queryable<T extends Serializable> {
// default Object[] toArray() {
// return new Object[0];
// }
default List<T> toList() throws SQLException {
return originalSet().getContext().getDatabase().queryExpression(originalSet().getContext(), originalSet().getClazz(), getExpression());
}
//
// default <A> A[] toArray(IntFunction<A[]> intFunction) {
// return null;

View File

@@ -1,13 +1,13 @@
package jef;
import jef.expressions.Expression;
import jef.serializable.SerializableObject;
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> {
public class QueryableProxy<T extends SerializableObject> implements Queryable<T> {
private final Queryable<T> delegate;
public QueryableProxy(Queryable<T> delegate) {
@@ -24,6 +24,11 @@ public class QueryableProxy<T extends Serializable> implements Queryable<T> {
return delegate.getExpression();
}
@Override
public DbSet<T> originalSet() {
return delegate.originalSet();
}
@Override
public Iterator<T> iterator() {
return delegate.iterator();

View File

@@ -0,0 +1,26 @@
package jef.asm.access;
import jef.asm.access.model.ClassDescription;
import org.objectweb.asm.ClassReader;
import org.objectweb.asm.Opcodes;
import java.io.IOException;
public class ClassAnalyzer {
public ClassDescription analyze(Class clazz) {
return analyzeClass(clazz);
}
private ClassDescription analyzeClass(Class clazz) {
try (var is = clazz.getClassLoader().getResourceAsStream(clazz.getName().replace(".", "/") + ".class")) {
var result = new ClassDescription[1];
var byteCode = is.readAllBytes();
var reader = new ClassReader(byteCode);
var visitor = new ClassAnalyzerVisitor(Opcodes.ASM9, description -> result[0] = description);
reader.accept(visitor, 0);
return result[0];
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}

View File

@@ -0,0 +1,69 @@
package jef.asm.access;
import jef.asm.access.model.ClassDescription;
import jef.asm.access.model.ConstructorDescription;
import jef.asm.access.model.FieldDescription;
import jef.asm.access.model.GetterDescription;
import jef.asm.access.model.SetterDescription;
import org.objectweb.asm.ClassVisitor;
import org.objectweb.asm.FieldVisitor;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Opcodes;
import org.objectweb.asm.Type;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.function.Consumer;
class ClassAnalyzerVisitor extends ClassVisitor {
private final Consumer<ClassDescription> callback;
private String className;
private String superClassName;
private final List<FieldDescription> declaredFields = new ArrayList<>();
private final List<ConstructorDescription> declaredConstructor = new ArrayList<>();
private final List<GetterDescription> declaredGetters = new ArrayList<>();
private final List<SetterDescription> declaredSetters = new ArrayList<>();
protected ClassAnalyzerVisitor(int api, Consumer<ClassDescription> callback) {
super(api);
this.callback = callback;
}
protected ClassAnalyzerVisitor(int api, ClassVisitor classVisitor, Consumer<ClassDescription> callback) {
super(api, classVisitor);
this.callback = callback;
}
@Override
public void visit(int version, int access, String name, String signature, String superName, String[] interfaces) {
className = name.replace("/", ".");
superClassName = superName.replace("/", ".");
super.visit(version, access, name, signature, superName, interfaces);
}
@Override
public FieldVisitor visitField(int access, String name, String descriptor, String signature, Object value) {
declaredFields.add(new FieldDescription(className, name));
return super.visitField(access, name, descriptor, signature, value);
}
@Override
public MethodVisitor visitMethod(int access, String name, String descriptor, String signature, String[] exceptions) {
var type = Type.getMethodType(descriptor);
var returnClassName = type.getReturnType().getClassName();
var parameterClassNames = Arrays.stream(type.getArgumentTypes()).map(Type::getClassName).toArray(String[]::new);
return new MethodAnalyzerVisitor(Opcodes.ASM9, className, name, returnClassName, parameterClassNames, entityAccess -> {
declaredConstructor.addAll(entityAccess.getDeclaredConstructors());
declaredGetters.addAll(entityAccess.getDeclaredGetters());
declaredSetters.addAll(entityAccess.getDeclaredSetters());
});
}
@Override
public void visitEnd() {
super.visitEnd();
callback.accept(new ClassDescription(className, superClassName, declaredFields, declaredConstructor, declaredGetters, declaredSetters));
}
}

View File

@@ -0,0 +1,308 @@
package jef.asm.access;
import jef.asm.access.model.ClassDescription;
import jef.asm.access.model.ConstructorDescription;
import jef.asm.access.model.ConstructorParameterDescription;
import jef.asm.access.model.GetterDescription;
import jef.asm.access.model.SetterDescription;
import jef.asm.access.model.SuperConstructorParameterDescription;
import lombok.Getter;
import org.objectweb.asm.Handle;
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.List;
import java.util.Set;
import java.util.Stack;
import java.util.function.Consumer;
@Getter
public class MethodAnalyzerVisitor extends MethodVisitor {//TODO verify param count
private final Consumer<ClassDescription> callback;
private final String methodClassName;
private final String methodName;
private final String returnClassName;
private final String[] parameterClassNames;
private List<ConstructorParameterDescription> constructorParameterDescriptions = new ArrayList<>();
private List<SuperConstructorParameterDescription> superConstructorParameterDescriptions = new ArrayList<>();
private Stack<Integer> varIndexStack = new Stack<>();
private String fieldDeclaringClassName = null;
private String fieldName = null;
private ConstructorSteps constructorStep = ConstructorSteps.INIT;
private GetterSteps getterStep = GetterSteps.INIT;
private SetterSteps setterStep = SetterSteps.INIT;
enum ConstructorSteps {
INIT,
CODE,
PRE_SUPER_INIT_ALOAD_0,
PRE_SUPER_INIT_VAR_LOAD,
SUPER_INIT,
ALOAD_0,
VAR_LOAD,
PUT_FIELD,
RETURN,
INVALID
}
enum GetterSteps {
INIT,
CODE,
ALOAD_0,
GET_FIELD,
RETURN,
INVALID
}
enum SetterSteps {
INIT,
CODE,
ALOAD_0,
VAR_LOAD,
PUT_FIELD,
RETURN,
INVALID
}
protected MethodAnalyzerVisitor(int api, String methodClassName, String methodName, String returnClassName, String[] parameterClassNames, Consumer<ClassDescription> callback) {
super(api);
this.methodClassName = methodClassName;
this.methodName = methodName;
this.returnClassName = returnClassName;
this.parameterClassNames = parameterClassNames;
this.callback = callback;
}
@Override
public void visitCode() {
constructorStep = constructorStep == ConstructorSteps.INIT ? ConstructorSteps.CODE : ConstructorSteps.INVALID;
getterStep = getterStep == GetterSteps.INIT ? GetterSteps.CODE : GetterSteps.INVALID;
setterStep = setterStep == SetterSteps.INIT ? SetterSteps.CODE : SetterSteps.INVALID;
super.visitCode();
}
@Override
public void visitFrame(int type, int numLocal, Object[] local, int numStack, Object[] stack) {
constructorStep = ConstructorSteps.INVALID;
getterStep = GetterSteps.INVALID;
setterStep = SetterSteps.INVALID;
super.visitFrame(type, numLocal, local, numStack, stack);
}
@Override
public void visitInsn(int opcode) {
if (Set.of(Opcodes.IRETURN, Opcodes.LRETURN, Opcodes.FRETURN, Opcodes.DRETURN, Opcodes.ARETURN).contains(opcode)) {
getterStep = getterStep == GetterSteps.GET_FIELD ? GetterSteps.RETURN : GetterSteps.INVALID;
} else {
getterStep = GetterSteps.INVALID;
}
if (opcode == Opcodes.RETURN) {
constructorStep = constructorStep == ConstructorSteps.PUT_FIELD ? ConstructorSteps.RETURN : ConstructorSteps.INVALID;
setterStep = setterStep == SetterSteps.PUT_FIELD ? SetterSteps.RETURN : SetterSteps.INVALID;
} else {
constructorStep = ConstructorSteps.INVALID;
setterStep = SetterSteps.INVALID;
}
super.visitInsn(opcode);
}
@Override
public void visitIntInsn(int opcode, int operand) {
constructorStep = ConstructorSteps.INVALID;
getterStep = GetterSteps.INVALID;
setterStep = SetterSteps.INVALID;
super.visitIntInsn(opcode, operand);
}
@Override
public void visitVarInsn(int opcode, int varIndex) {
if (Set.of(Opcodes.ILOAD, Opcodes.LLOAD, Opcodes.FLOAD, Opcodes.DLOAD, Opcodes.ALOAD).contains(opcode)) {
varIndexStack.push(varIndex);
}
if (opcode == Opcodes.ALOAD && varIndex == 0) {
if (constructorStep == ConstructorSteps.CODE) {
constructorStep = ConstructorSteps.PRE_SUPER_INIT_ALOAD_0;
} else if (constructorStep == ConstructorSteps.SUPER_INIT || constructorStep == ConstructorSteps.PUT_FIELD) {
constructorStep = ConstructorSteps.ALOAD_0;
} else {
constructorStep = ConstructorSteps.INVALID;
}
} else if (Set.of(Opcodes.ILOAD, Opcodes.LLOAD, Opcodes.FLOAD, Opcodes.DLOAD, Opcodes.ALOAD).contains(opcode)) {
if (constructorStep == ConstructorSteps.PRE_SUPER_INIT_ALOAD_0) {
constructorStep = ConstructorSteps.PRE_SUPER_INIT_VAR_LOAD;
} else if (constructorStep == ConstructorSteps.PRE_SUPER_INIT_VAR_LOAD) {
constructorStep = ConstructorSteps.PRE_SUPER_INIT_VAR_LOAD;
} else if (constructorStep == ConstructorSteps.ALOAD_0) {
constructorStep = ConstructorSteps.VAR_LOAD;
} else {
constructorStep = ConstructorSteps.INVALID;
}
} else {
constructorStep = ConstructorSteps.INVALID;
}
if (opcode == Opcodes.ALOAD && varIndex == 0) {
getterStep = getterStep == GetterSteps.CODE ? GetterSteps.ALOAD_0 : GetterSteps.INVALID;
} else {
getterStep = GetterSteps.INVALID;
}
if (opcode == Opcodes.ALOAD && varIndex == 0) {
setterStep = setterStep == SetterSteps.CODE ? SetterSteps.ALOAD_0 : SetterSteps.INVALID;
} else if (Set.of(Opcodes.ILOAD, Opcodes.LLOAD, Opcodes.FLOAD, Opcodes.DLOAD, Opcodes.ALOAD).contains(opcode)) {
setterStep = setterStep == SetterSteps.ALOAD_0 ? SetterSteps.VAR_LOAD : SetterSteps.INVALID;
} else {
setterStep = SetterSteps.INVALID;
}
super.visitVarInsn(opcode, varIndex);
}
@Override
public void visitTypeInsn(int opcode, String type) {
constructorStep = ConstructorSteps.INVALID;
getterStep = GetterSteps.INVALID;
setterStep = SetterSteps.INVALID;
super.visitTypeInsn(opcode, type);
}
@Override
public void visitFieldInsn(int opcode, String owner, String name, String descriptor) {
var lastLoadedVarIndex = -1;
if (opcode == Opcodes.PUTFIELD) {
lastLoadedVarIndex = varIndexStack.pop();
varIndexStack.pop();
constructorStep = constructorStep == ConstructorSteps.VAR_LOAD ? ConstructorSteps.PUT_FIELD : ConstructorSteps.INVALID;
getterStep = GetterSteps.INVALID;
setterStep = setterStep == SetterSteps.VAR_LOAD ? SetterSteps.PUT_FIELD : SetterSteps.INVALID;
} else if (opcode == Opcodes.GETFIELD) {
lastLoadedVarIndex = varIndexStack.pop();
constructorStep = ConstructorSteps.INVALID;
getterStep = getterStep == GetterSteps.ALOAD_0 ? GetterSteps.GET_FIELD : GetterSteps.INVALID;
setterStep = SetterSteps.INVALID;
} else {
constructorStep = ConstructorSteps.INVALID;
getterStep = GetterSteps.INVALID;
setterStep = SetterSteps.INVALID;
}
this.fieldDeclaringClassName = owner.replace("/", ".");
this.fieldName = name;
this.constructorParameterDescriptions.add(new ConstructorParameterDescription(lastLoadedVarIndex - 1, this.fieldDeclaringClassName, this.fieldName)); //lastLoadedVarIndex - 1 because arg 0 of an instance function is always this
super.visitFieldInsn(opcode, owner, name, descriptor);
}
@Override
public void visitMethodInsn(int opcode, String owner, String name, String descriptor, boolean isInterface) {
var argIndexes = new ArrayList<Integer>();
for (var i = 0; i < Type.getArgumentTypes(descriptor).length; i++) {
argIndexes.add(0, varIndexStack.pop());
}
varIndexStack.pop(); // also pop ALOAD0
if (opcode == Opcodes.INVOKESPECIAL && name.equals("<init>") //TODO does it have to be called '<init>' or is this just convention
&& Set.of(ConstructorSteps.PRE_SUPER_INIT_ALOAD_0, ConstructorSteps.PRE_SUPER_INIT_VAR_LOAD).contains(constructorStep)) {
for (int i = 0; i < argIndexes.size(); i++) {
superConstructorParameterDescriptions.add(new SuperConstructorParameterDescription(argIndexes.get(i), i));
}
constructorStep = ConstructorSteps.SUPER_INIT;
} else {
constructorStep = ConstructorSteps.INVALID;
}
getterStep = GetterSteps.INVALID;
setterStep = SetterSteps.INVALID;
super.visitMethodInsn(opcode, owner, name, descriptor, isInterface);
}
@Override
public void visitInvokeDynamicInsn(String name, String descriptor, Handle bootstrapMethodHandle, Object... bootstrapMethodArguments) {
constructorStep = ConstructorSteps.INVALID;
getterStep = GetterSteps.INVALID;
setterStep = SetterSteps.INVALID;
super.visitInvokeDynamicInsn(name, descriptor, bootstrapMethodHandle, bootstrapMethodArguments);
}
@Override
public void visitJumpInsn(int opcode, Label label) {
constructorStep = ConstructorSteps.INVALID;
getterStep = GetterSteps.INVALID;
setterStep = SetterSteps.INVALID;
super.visitJumpInsn(opcode, label);
}
@Override
public void visitLabel(Label label) {
// constructorStep = ConstructorSteps.INVALID;
// getterStep = GetterSteps.INVALID;
// setterStep = SetterSteps.INVALID;
super.visitLabel(label);
}
@Override
public void visitLdcInsn(Object value) {
constructorStep = ConstructorSteps.INVALID;
getterStep = GetterSteps.INVALID;
setterStep = SetterSteps.INVALID;
super.visitLdcInsn(value);
}
@Override
public void visitIincInsn(int varIndex, int increment) {
constructorStep = ConstructorSteps.INVALID;
getterStep = GetterSteps.INVALID;
setterStep = SetterSteps.INVALID;
super.visitIincInsn(varIndex, increment);
}
@Override
public void visitTableSwitchInsn(int min, int max, Label dflt, Label... labels) {
constructorStep = ConstructorSteps.INVALID;
getterStep = GetterSteps.INVALID;
setterStep = SetterSteps.INVALID;
super.visitTableSwitchInsn(min, max, dflt, labels);
}
@Override
public void visitLookupSwitchInsn(Label dflt, int[] keys, Label[] labels) {
constructorStep = ConstructorSteps.INVALID;
getterStep = GetterSteps.INVALID;
setterStep = SetterSteps.INVALID;
super.visitLookupSwitchInsn(dflt, keys, labels);
}
@Override
public void visitMultiANewArrayInsn(String descriptor, int numDimensions) {
constructorStep = ConstructorSteps.INVALID;
getterStep = GetterSteps.INVALID;
setterStep = SetterSteps.INVALID;
super.visitMultiANewArrayInsn(descriptor, numDimensions);
}
@Override
public void visitTryCatchBlock(Label start, Label end, Label handler, String type) {
constructorStep = ConstructorSteps.INVALID;
getterStep = GetterSteps.INVALID;
setterStep = SetterSteps.INVALID;
super.visitTryCatchBlock(start, end, handler, type);
}
@Override
public void visitLocalVariable(String name, String descriptor, String signature, Label start, Label end, int index) {
super.visitLocalVariable(name, descriptor, signature, start, end, index);
}
@Override
public void visitMaxs(int maxStack, int maxLocals) {
super.visitMaxs(maxStack, maxLocals);
}
@Override
public void visitEnd() {
super.visitEnd();
List<ConstructorDescription> constructors = constructorStep == ConstructorSteps.RETURN ? List.of(new ConstructorDescription(constructorParameterDescriptions, superConstructorParameterDescriptions)) : List.of();
List<GetterDescription> getters = getterStep == GetterSteps.RETURN ? List.of(new GetterDescription(methodClassName, methodName, returnClassName, parameterClassNames, fieldDeclaringClassName, fieldName)) : List.of();
List<SetterDescription> setters = setterStep == SetterSteps.RETURN ? List.of(new SetterDescription(methodClassName, methodName, returnClassName, parameterClassNames, fieldDeclaringClassName, fieldName)) : List.of();
var access = new ClassDescription(null, null, null, constructors, getters, setters);
callback.accept(access);
}
}

View File

@@ -0,0 +1,41 @@
package jef.asm.access.model;
import lombok.AllArgsConstructor;
import lombok.Getter;
import java.util.List;
import java.util.stream.Collectors;
@AllArgsConstructor
@Getter
public class ClassDescription {
private final String className;
private final String superClassName;
private final List<FieldDescription> declaredFields;
private final List<ConstructorDescription> declaredConstructors;
private final List<GetterDescription> declaredGetters;
private final List<SetterDescription> declaredSetters;
@Override
public String toString() {
return "ClassDescription{" +
"className='" + className + '\'' +
", superClassName='" + superClassName + '\'' +
", declaredFields=" + declaredFields +
", declaredConstructors=" + declaredConstructors +
", declaredGetters=" + declaredGetters +
", declaredSetters=" + declaredSetters +
'}';
}
public String toStringFriendly() {
return "ClassAccessDescription{\n"
+ " className: " + className + '\n'
+ " superClassName: " + superClassName + '\n'
+ " declaredFields: " + declaredFields + '\n'
+ " declaredConstructors:\n" + declaredConstructors.stream().map(e -> " " + e.toString() + '\n').collect(Collectors.joining())
+ " declaredGetters:\n" + declaredGetters.stream().map(e -> " " + e.toString() + '\n').collect(Collectors.joining())
+ " declaredSetters:\n" + declaredSetters.stream().map(e -> " " + e.toString()+ '\n').collect(Collectors.joining())
+ '}';
}
}

View File

@@ -0,0 +1,15 @@
package jef.asm.access.model;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.ToString;
import java.util.List;
@AllArgsConstructor
@Getter
@ToString
public class ConstructorDescription {
private final List<ConstructorParameterDescription> constructorParameterDescriptions;
private final List<SuperConstructorParameterDescription> superConstructorParameterDescriptions;
}

View File

@@ -0,0 +1,14 @@
package jef.asm.access.model;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.ToString;
@AllArgsConstructor
@Getter
@ToString
public class ConstructorParameterDescription {
private final int parameterIndex;
private final String declaringClassName;
private final String fieldName;
}

View File

@@ -0,0 +1,13 @@
package jef.asm.access.model;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.ToString;
@AllArgsConstructor
@Getter
@ToString
public class FieldDescription {
private final String fieldDeclaringClassName;
private final String fieldName;
}

View File

@@ -0,0 +1,17 @@
package jef.asm.access.model;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.ToString;
@AllArgsConstructor
@Getter
@ToString
public class GetterDescription {
private final String methodDeclaringClassName;
private final String methodName;
private final String methodReturnClassName;
private final String[] methodParameterClassNames;
private final String fieldDeclaringClassName;
private final String fieldName;
}

View File

@@ -0,0 +1,17 @@
package jef.asm.access.model;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.ToString;
@AllArgsConstructor
@Getter
@ToString
public class SetterDescription {
private final String methodDeclaringClassName;
private final String methodName;
private final String methodReturnClassName;
private final String[] methodParameterClassNames;
private final String fieldDeclaringClassName;
private final String fieldName;
}

View File

@@ -0,0 +1,13 @@
package jef.asm.access.model;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.ToString;
@AllArgsConstructor
@Getter
@ToString
public class SuperConstructorParameterDescription {
private final int constructorParameterIndex;
private final int superConstructorParameterIndex;
}

View File

@@ -40,7 +40,7 @@ public abstract class DbContext {
f.setAccessible(true);
Clazz anno = f.getAnnotation(Clazz.class);
try {
f.set(this, new DbSet(anno.value(), f.getName()));//TODO use table name from modelbuilder
f.set(this, new DbSet(this, anno.value(), f.getName()));//TODO use table name from modelbuilder
} catch (IllegalAccessException e) {
throw new RuntimeException(e);
}

View File

@@ -1,5 +1,6 @@
package jef.operations;
import jef.DbSet;
import jef.Queryable;
import jef.asm.AsmParseException;
import jef.asm.OptimizedAsmParser;
@@ -8,13 +9,13 @@ import jef.expressions.SelectExpression;
import jef.expressions.WhereExpression;
import jef.expressions.modifier.TableAliasInjector;
import jef.expressions.selectable.DatabaseSelectAllExpression;
import jef.serializable.SerializableObject;
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> {
public class FilterOp<T extends SerializableObject> implements Queryable<T>, Operation<T> {
private final Queryable<T> queryable;
private final Predicate<? super T> predicate;
@@ -50,4 +51,9 @@ public class FilterOp<T extends Serializable> implements Queryable<T>, Operation
public String toString() {
return getExpression().toString();
}
@Override
public DbSet<T> originalSet() {
return queryable.originalSet();
}
}

View File

@@ -1,15 +1,16 @@
package jef.operations;
import jef.DbSet;
import jef.Queryable;
import jef.expressions.Expression;
import jef.expressions.LimitExpression;
import jef.expressions.SelectExpression;
import jef.expressions.selectable.DatabaseSelectAllExpression;
import jef.serializable.SerializableObject;
import java.io.Serializable;
import java.util.List;
public class LimitOp<T extends Serializable> implements Queryable<T> {
public class LimitOp<T extends SerializableObject> implements Queryable<T> {
private final Queryable<T> queryable;
private Long start;
private Long count;
@@ -35,6 +36,11 @@ public class LimitOp<T extends Serializable> implements Queryable<T> {
return getExpression().toString();
}
@Override
public DbSet<T> originalSet() {
return queryable.originalSet();
}
@Override
public Queryable<T> limit(long l) {
count = l;

View File

@@ -1,9 +1,8 @@
package jef.operations;
import jef.Queryable;
import jef.serializable.SerializableObject;
import java.io.Serializable;
public interface Operation<T extends Serializable> extends Queryable<T> {
public interface Operation<T extends SerializableObject> extends Queryable<T> {//TODO is this class required
}

View File

@@ -1,5 +1,6 @@
package jef.operations;
import jef.DbSet;
import jef.Queryable;
import jef.asm.AsmParseException;
import jef.asm.AsmParser;
@@ -13,14 +14,14 @@ import jef.expressions.modifier.TableAliasInjector;
import jef.expressions.modifier.TernaryRewriter;
import jef.expressions.selectable.DatabaseSelectAllExpression;
import jef.serializable.SerializableFunction;
import jef.serializable.SerializableObject;
import lombok.AllArgsConstructor;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;
@AllArgsConstructor
public class SortOp<T extends Serializable> implements Queryable<T> {
public class SortOp<T extends SerializableObject> implements Queryable<T> {
private final Queryable<T> queryable;
private final List<OrderExpression.Sort> sorts = new ArrayList<>();
@@ -45,6 +46,11 @@ public class SortOp<T extends Serializable> implements Queryable<T> {
return getExpression().toString();
}
@Override
public DbSet<T> originalSet() {
return queryable.originalSet();
}
public SortOp<T> thenSorted(SerializableFunction<? super T, ?> fieldSelector) {
var f = parseFunction(fieldSelector);
this.sorts.add(new OrderExpression.Sort(f, OrderExpression.SortDirection.ASCENDING));

View File

@@ -1,25 +1,96 @@
package jef.platform.base;
import jef.MigrationException;
import jef.expressions.Expression;
import jef.model.DbContext;
import jef.model.DbFieldBuilder;
import jef.model.ModelBuilder;
import jef.serializable.SerializableObject;
import lombok.AllArgsConstructor;
import lombok.Getter;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;
@Getter
@AllArgsConstructor
public abstract class Database {
// private final DatabaseConfiguration config;
protected final Connection connection;
protected final DatabaseOptions options;
public abstract void migrate() throws MigrationException;
// public ResultSet executeRaw(String s) throws SQLException {
// try (var stmt = connection.prepareStatement(s)) {
// try (var res = stmt.executeQuery()) {
// return res;
// }
// }
// }
public abstract <T extends SerializableObject> List<T> queryExpression(DbContext context, Class<T> clazz, Expression expression) throws SQLException;
public <T extends SerializableObject> List<T> queryRaw(DbContext context, Class<T> clazz, String query) throws SQLException {
var modelBuilder = ModelBuilder.from(context.getClass());
var entity = modelBuilder.entity(clazz);
var fields = entity.fields();
try (var stmt = connection.prepareStatement(query)) {
return queryRaw(stmt, clazz, fields);
}
}
protected <T extends SerializableObject> List<T> queryRaw(PreparedStatement stmt, Class<T> clazz, List<DbFieldBuilder<?>> fields) throws SQLException {
try (var result = stmt.executeQuery()) {
var ret = new ArrayList<T>();
while (result.next()) {
ret.add(readRow(result, clazz, fields));
}
return ret;
}
}
protected <T extends SerializableObject> T readRow(ResultSet res, Class<T> clazz, List<DbFieldBuilder<?>> fields) throws SQLException {
//TODO replace with by runtime generated class
try {
var model = (T) clazz.getConstructor().newInstance();
for (DbFieldBuilder<?> field : fields) {
if (!field.getField().isDatabaseField()) {
continue;
}
Field f = field.getField().getField();
if (f == null) {
System.out.println("not class member for " + field.getField().getName());
continue;
}
f.setAccessible(true);
if (field.getField().getType() == String.class) {
f.set(model, res.getString(f.getName()));
} else if (field.getField().getType() == byte.class || field.getField().getType() == Byte.class) {
f.set(model, res.getByte(f.getName()));
} else if (field.getField().getType() == char.class || field.getField().getType() == Character.class) {
// f.set(model, res.getString(f.getName()));//TODO handle char
throw new UnsupportedOperationException("char");
} else if (field.getField().getType() == short.class || field.getField().getType() == Short.class) {
f.set(model, res.getShort(f.getName()));
} else if (field.getField().getType() == int.class || field.getField().getType() == Integer.class) {
f.set(model, res.getInt(f.getName()));
} else if (field.getField().getType() == long.class || field.getField().getType() == Long.class) {
f.set(model, res.getLong(f.getName()));
} else if (field.getField().getType() == float.class || field.getField().getType() == Float.class) {
f.set(model, res.getFloat(f.getName()));
} else if (field.getField().getType() == double.class || field.getField().getType() == Double.class) {
f.set(model, res.getDouble(f.getName()));
} else {
throw new UnsupportedOperationException();
}
}
return model;
} catch (IllegalAccessException | InvocationTargetException | InstantiationException | NoSuchMethodException e) {//TODO better handling
throw new RuntimeException(e);
}
}
public abstract <T extends SerializableObject> void add(DbContext context, Class<T> clazz, T model) throws SQLException;
public abstract <T extends SerializableObject> void update(DbContext context, Class<T> clazz, T model) throws SQLException;
public abstract <T extends SerializableObject> void delete(DbContext context, Class<T> clazz, T model) throws SQLException;
}

View File

@@ -1,7 +1,13 @@
package jef.platform.dummy;
import jef.MigrationException;
import jef.expressions.Expression;
import jef.model.DbContext;
import jef.platform.base.Database;
import jef.serializable.SerializableObject;
import java.sql.SQLException;
import java.util.List;
public class DummyDatabase extends Database {
public DummyDatabase() {
@@ -11,4 +17,28 @@ public class DummyDatabase extends Database {
@Override
public void migrate() throws MigrationException {
}
@Override
public <T extends SerializableObject> List<T> queryExpression(DbContext context, Class<T> clazz, Expression expression) {
return List.of();
}
@Override
public <T extends SerializableObject> List<T> queryRaw(DbContext context, Class<T> clazz, String query) {
return List.of();
}
@Override
public <T extends SerializableObject> void add(DbContext context, Class<T> clazz, T entity) throws SQLException {
}
@Override
public <T extends SerializableObject> void update(DbContext context, Class<T> clazz, T model) throws SQLException {
}
@Override
public <T extends SerializableObject> void delete(DbContext context, Class<T> clazz, T model) throws SQLException {
}
}

View File

@@ -26,6 +26,11 @@ public abstract class Util {
R apply(T t, U u) throws Throwable;
}
@FunctionalInterface
public interface ThrowableBiConsumer<T, U> {
void accept(T t, U u) throws Throwable;
}
public interface Equality<T> {
boolean check(T t1, T t2);
}

View File

@@ -0,0 +1,58 @@
package jef.asm.access;
import jef.model.ModelBuilder;
import jef.model.annotations.ForeignKey;
import jef.serializable.SerializableObject;
import lombok.AllArgsConstructor;
import lombok.EqualsAndHashCode;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import lombok.ToString;
import org.junit.jupiter.api.Test;
import java.sql.Connection;
import java.sql.SQLException;
import java.sql.Statement;
class ClassAnalyzerTest {
@Test
void analyze() {
var analyzer = new ClassAnalyzer();
var result = analyzer.analyze(Employee.class);
System.out.println(result.toStringFriendly());
}
@Getter
@Setter
@AllArgsConstructor
@NoArgsConstructor
@ToString
@EqualsAndHashCode(callSuper = false)
public static class Company extends SerializableObject {
private int id;
private String name;
private Employee ceo;
}
@Getter
@Setter
@AllArgsConstructor
@NoArgsConstructor
@ToString
@EqualsAndHashCode(callSuper = false)
public static class Employee extends SerializableObject {
private int id;
private String name;
private Employee boss;
@ForeignKey(getterOrField = "boss")
private int bossId;
private Company company;
@ForeignKey(getterOrField = "company")
private int companyId;
}
}

View File

@@ -13,7 +13,7 @@ public class FilterOpTest {
@Test
public void testSimple() {
String act;
act = new DbSet<>(TestClass.class, "table1")
act = new DbSet<>(null, TestClass.class, "table1")
.filter(e -> true)
.toString();
Assertions.assertEquals("SELECT * FROM (SELECT * FROM `table1`) a WHERE 1", act);
@@ -23,7 +23,7 @@ public class FilterOpTest {
public void testMultipleFilter() {
String act;
var s = List.of(1, 3);
act = new DbSet<>(TestClass.class, "table1")
act = new DbSet<>(null, TestClass.class, "table1")
.filter(e -> s.contains(e.i))
.filter(e -> e.i == 1337)
.toString();

View File

@@ -8,25 +8,25 @@ public class LimitOpTest {
@Test
public void test() {
String act;
act = new DbSet<>(FilterOpTest.TestClass.class, "table1")
act = new DbSet<>(null, FilterOpTest.TestClass.class, "table1")
.limit(10).skip(5)
.toString();
Assertions.assertEquals("SELECT * FROM (SELECT * FROM `table1`) a OFFSET 5 LIMIT 10", act);
act = new DbSet<>(FilterOpTest.TestClass.class, "table1")
act = new DbSet<>(null, FilterOpTest.TestClass.class, "table1")
.skip(5).limit(10)
.toString();
Assertions.assertEquals("SELECT * FROM (SELECT * FROM `table1`) a OFFSET 5 LIMIT 10", act);
act = new DbSet<>(FilterOpTest.TestClass.class, "table1")
act = new DbSet<>(null, FilterOpTest.TestClass.class, "table1")
.skip(5)
.toString();
Assertions.assertEquals("SELECT * FROM (SELECT * FROM `table1`) a OFFSET 5", act);
act = new DbSet<>(FilterOpTest.TestClass.class, "table1")
act = new DbSet<>(null, FilterOpTest.TestClass.class, "table1")
.limit(10)
.toString();
Assertions.assertEquals("SELECT * FROM (SELECT * FROM `table1`) a LIMIT 10", act);

View File

@@ -8,27 +8,27 @@ class SortOpTest {
@Test
public void test() {
String act;
act = new DbSet<>(FilterOpTest.TestClass.class, "table1")
act = new DbSet<>(null, FilterOpTest.TestClass.class, "table1")
.sorted(e -> e.getI()).thenSorted(e -> e.getD()).thenSorted(e -> e.getF()).thenSorted(e -> e.getL()).thenSorted(e -> e.getO())
.toString();
Assertions.assertEquals("SELECT * FROM (SELECT * FROM `table1`) a ORDER BY `a`.`i` ASC, `a`.`d` ASC, `a`.`f` ASC, `a`.`l` ASC, `a`.`o` ASC", act);
act = new DbSet<>(FilterOpTest.TestClass.class, "table1")
act = new DbSet<>(null, FilterOpTest.TestClass.class, "table1")
.sortedDescending(e -> e.getI()).thenSortedDescending(e -> e.getD()).thenSortedDescending(e -> e.getF())
/**/.thenSortedDescending(e -> e.getL()).thenSortedDescending(e -> e.getO())
.toString();
Assertions.assertEquals("SELECT * FROM (SELECT * FROM `table1`) a ORDER BY `a`.`i` DESC, `a`.`d` DESC, `a`.`f` DESC, `a`.`l` DESC, `a`.`o` DESC", act);
//alternating patterns
act = new DbSet<>(FilterOpTest.TestClass.class, "table1")
act = new DbSet<>(null, FilterOpTest.TestClass.class, "table1")
.sortedDescending(e -> e.getI()).thenSorted(e -> e.getD()).thenSortedDescending(e -> e.getF()).thenSorted(e -> e.getL())
/**/.thenSortedDescending(e -> e.getO())
.toString();
Assertions.assertEquals("SELECT * FROM (SELECT * FROM `table1`) a ORDER BY `a`.`i` DESC, `a`.`d` ASC, `a`.`f` DESC, `a`.`l` ASC, `a`.`o` DESC", act);
act = new DbSet<>(FilterOpTest.TestClass.class, "table1")
act = new DbSet<>(null, FilterOpTest.TestClass.class, "table1")
.sorted(e -> e.getI()).thenSortedDescending(e -> e.getD()).thenSorted(e -> e.getF()).thenSortedDescending(e -> e.getL())
/**/.thenSorted(e -> e.getO())
.toString();

View File

@@ -12,11 +12,11 @@ class DebugExpressionVisitorTest {
@Test
public void test() {
var s = List.of(1, 3);
Queryable<FilterOpTest.TestClass> q = new DbSet<>(FilterOpTest.TestClass.class, "table1")
Queryable<FilterOpTest.TestClass> q = new DbSet<>(null, FilterOpTest.TestClass.class, "table1")
.filter(e -> s.contains(e.i) && e.i == 1337 && e.i == 420);
new DebugExpressionVisitor().visit(q.getExpression());
Queryable<FilterOpTest.TestClass> q2 = new DbSet<>(FilterOpTest.TestClass.class, "table1")
Queryable<FilterOpTest.TestClass> q2 = new DbSet<>(null, FilterOpTest.TestClass.class, "table1")
.filter(e -> s.contains(e.i) || e.i == 1337);
new DebugExpressionVisitor().visit(q2.getExpression());
}