added modelbuilder

This commit is contained in:
wea_ondara
2022-07-17 12:09:03 +02:00
parent 00b7fb2f45
commit 878d235e64
12 changed files with 396 additions and 1 deletions

View File

@@ -0,0 +1,4 @@
package jef.model;
public abstract class DbContext {
}

View File

@@ -0,0 +1,94 @@
package jef.model;
import jef.asm.AsmParseException;
import jef.asm.AsmParser;
import jef.expressions.Expression;
import jef.expressions.IntermediateFieldExpression;
import jef.serializable.SerializableFunction;
import jef.serializable.SerializableObject;
import lombok.Getter;
import lombok.Setter;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
@Getter
@Setter
public class DbEntity<T extends SerializableObject> {
private final Class<T> entityClazz;
private final List<DbField<?>> fields;
private String name;
public DbEntity(Class<T> entityClazz) {
this(entityClazz, new ArrayList<>());
}
public DbEntity(Class<T> entityClazz, List<DbField<?>> fields) {
this.entityClazz = entityClazz;
this.fields = fields;
this.name = entityClazz.getSimpleName();
}
public List<DbField<?>> getFields() {
return Collections.unmodifiableList(fields);
}
/**
* Returns the database model for the requested property or null if not present.
*
* @param getter the expression selecting the property
* @param <R> the type of property class
* @return the database model for the requested property or null if not present.
*/
public <R> DbField<R> getField(SerializableFunction<T, R> getter) {
var name = extractFieldName(getter);
var prop = (DbField<R>) this.fields.stream().filter(p -> p.getField().getName().equals(name)).findFirst().orElse(null);
return prop;
}
public <R> DbField<R> getOrCreateField(SerializableFunction<T, R> getter) {
try {
var prop = getField(getter);
if (prop == null) {
var name = extractFieldName(getter);
var field = ReflectionUtil.getFieldsRecursive(entityClazz).stream().filter(f -> f.getName().equals(name)).findFirst().orElse(null);
if (field == null) {
throw new RuntimeException("Field not found: " + name);
}
prop = new DbField<>((Class<R>) field.getType(), field);
fields.add(prop);
}
return prop;
} catch (Exception e) {
throw new RuntimeException("Invalid expression", e);
}
}
public <R> DbField<R> getOrCreateField(Field f) {
try {
var prop = (DbField<R>) fields.stream().filter(e -> e.getField() == f).findFirst().orElse(null);
if (prop == null) {
prop = new DbField<>((Class<R>) f.getType(), f);
fields.add(prop);
}
return prop;
} catch (Exception e) {
throw new RuntimeException("Invalid expression", e);
}
}
private <R> String extractFieldName(SerializableFunction<T, R> getter) {
try {
var expr = new AsmParser(getter).parse();
if (expr.getType() != Expression.Type.INTERMEDIATE_FIELD) {
throw new RuntimeException(expr.getClass().getSimpleName() + " is not a field expression");
}
var name = ((IntermediateFieldExpression) expr).getName();
return name;
} catch (AsmParseException e) {
throw new RuntimeException("Invalid expression", e);
}
}
}

View File

@@ -0,0 +1,21 @@
package jef.model;
import lombok.Getter;
import lombok.Setter;
import java.lang.reflect.Field;
@Getter
@Setter
public class DbField<T> {
private final Class<T> propertyClazz;
private final Field field;
private String name;
private boolean notNull = false;
public DbField(Class<T> propertyClazz, Field field) {
this.propertyClazz = propertyClazz;
this.field = field;
this.name = field.getName();
}
}

View File

@@ -0,0 +1,116 @@
package jef.model;
import jef.DbSet;
import jef.model.annotations.Clazz;
import jef.model.annotations.processors.AnnotationProcessor;
import jef.model.annotations.processors.NotNullProcessor;
import jef.serializable.SerializableObject;
import javax.persistence.Transient;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
public class ModelBuilder {
private static final List<AnnotationProcessor> annotationProcessors = new ArrayList<>();
static {
annotationProcessors.add(NotNullProcessor.INSTANCE);
}
public static ModelBuilder from(DbContext context) {
try {
return from0(context);
} catch (Exception e) {
throw e instanceof RuntimeException ? (RuntimeException) e : new RuntimeException(e);
}
}
private static ModelBuilder from0(DbContext context) throws Exception {
var mb = new ModelBuilder(new ArrayList<>());
for (Field ctxfield : context.getClass().getDeclaredFields()) {
System.out.println(ctxfield);
if (!DbSet.class.isAssignableFrom(ctxfield.getType())) {
continue;
}
Clazz clazzAnnontation = ctxfield.getAnnotation(Clazz.class);
if (clazzAnnontation == null) {
throw new ModelException("DbSet " + ctxfield.getName() + " is missing the Clazz annotation");
}
var dbsetClazz = (Class<? extends SerializableObject>)clazzAnnontation.clazz();
var entity = mb.getOrCreateEntity(dbsetClazz);
var fields = ReflectionUtil.getFieldsRecursive(dbsetClazz);
for (var f : fields) {
if (f.getAnnotationsByType(Transient.class).length > 0) {
continue;
}
if (Collection.class.isAssignableFrom(f.getType())) {
throw new UnsupportedOperationException();
} else {
var dbField = entity.getOrCreateField(f);
if (f.getType().isPrimitive()) {
dbField.setNotNull(true);
}
}
}
}
for (AnnotationProcessor processor : annotationProcessors) {
processor.apply(mb);
}
return mb;
// var entities = new HashMap<Class<? extends SerializableObject>, DbEntity<? extends SerializableObject>>();
// for (Field ctxfield : context.getClass().getDeclaredFields()) {
// if (!DbSet.class.isAssignableFrom(ctxfield.getClass())) {
// continue;
// }
// ctxfield.setAccessible(true);
// var dbset = (DbSet<? extends SerializableObject>) ctxfield.get(context);
// var dbsetClazz = dbset.getClazz();
// var fields = getFieldsRecursive(dbsetClazz);
// entities.put(dbsetClazz, createEntity(dbsetClazz, fields));
// }
//
// return new ModelBuilder(new ArrayList<>(entities.values()));
}
private final List<DbEntity<? extends SerializableObject>> entities;
public ModelBuilder(List<DbEntity<? extends SerializableObject>> entities) {
this.entities = entities;
}
public List<DbEntity<? extends SerializableObject>> getEntities() {
return Collections.unmodifiableList(entities);
}
/**
* Returns the database model for the requested class or null if not present.
*
* @param clazz the class of the model class
* @param <T> the type of model class
* @return the database model for the requested class or null if not present.
*/
public <T extends SerializableObject> DbEntity<T> getEntity(Class<T> clazz) {
return (DbEntity<T>) entities.stream().filter(e -> e.getEntityClazz() == clazz).findFirst().orElse(null);
}
/**
* Returns the database model for the requested class or creates a new one if none exists.
*
* @param clazz the class of the model class
* @param <T> the type of model class
* @return the database model for the requested class or the newly created one if none existed.
*/
public <T extends SerializableObject> DbEntity<T> getOrCreateEntity(Class<T> clazz) {
var entity = getEntity(clazz);
if (entity == null) {
entity = new DbEntity<>(clazz);
entities.add(entity);
}
return entity;
}
}

View File

@@ -0,0 +1,15 @@
package jef.model;
public class ModelException extends RuntimeException {
public ModelException(String message) {
super(message);
}
public ModelException(String message, Throwable cause) {
super(message, cause);
}
public ModelException(Throwable cause) {
super(cause);
}
}

View File

@@ -0,0 +1,22 @@
package jef.model;
import jef.serializable.SerializableObject;
import java.lang.reflect.Field;
import java.util.Collection;
import java.util.HashMap;
class ReflectionUtil {
static Collection<Field> getFieldsRecursive(Class<? extends SerializableObject> clazz) {
var fields = new HashMap<String, Field>();
do {
for (Field f : clazz.getDeclaredFields()) {
if (!fields.containsKey(f.getName())) {
fields.put(f.getName(), f);
}
}
clazz = (Class<? extends SerializableObject>) clazz.getSuperclass();
} while (clazz != SerializableObject.class);
return fields.values();
}
}

View File

@@ -0,0 +1,12 @@
package jef.model.annotations;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Clazz {
Class<?> clazz();
}

View File

@@ -0,0 +1,11 @@
package jef.model.annotations;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface NotNull {
}

View File

@@ -0,0 +1,8 @@
package jef.model.annotations.processors;
import jef.model.ModelBuilder;
public interface AnnotationProcessor {
void apply(ModelBuilder mb);
}

View File

@@ -0,0 +1,25 @@
package jef.model.annotations.processors;
import jef.model.DbEntity;
import jef.model.DbField;
import jef.model.ModelBuilder;
import jef.model.annotations.NotNull;
import jef.serializable.SerializableObject;
import lombok.AccessLevel;
import lombok.NoArgsConstructor;
@NoArgsConstructor(access = AccessLevel.PRIVATE)
public class NotNullProcessor implements AnnotationProcessor {
public static final NotNullProcessor INSTANCE = new NotNullProcessor();
@Override
public void apply(ModelBuilder mb) {
for (DbEntity<? extends SerializableObject> entity : mb.getEntities()) {
for (DbField<?> field : entity.getFields()) {
if (field.getField().getAnnotationsByType(NotNull.class).length > 0) {
field.setNotNull(true);
}
}
}
}
}