package io.trygvis.container.compiler; import io.trygvis.container.compiler.model.ClassG; import io.trygvis.container.compiler.model.TypeRef; import io.trygvis.persistence.EntityMirror; import io.trygvis.persistence.FieldMirror; import io.trygvis.persistence.GeneratorConfiguration; import io.trygvis.persistence.SqlEntity; import io.trygvis.persistence.TypeHandler; import javax.annotation.processing.ProcessingEnvironment; import javax.lang.model.element.AnnotationMirror; import javax.lang.model.element.AnnotationValue; import javax.lang.model.element.ExecutableElement; import javax.lang.model.element.PackageElement; import javax.lang.model.element.TypeElement; import javax.lang.model.element.VariableElement; import javax.lang.model.type.TypeMirror; import javax.persistence.Id; import javax.tools.JavaFileObject; import java.io.IOException; import java.io.PrintWriter; import java.util.ArrayList; import java.util.List; import java.util.Map; import java.util.Set; import static io.trygvis.container.compiler.Utils.toJavaString; import static io.trygvis.persistence.FieldMirror.PrimitiveFieldMirror; import static java.lang.Character.isUpperCase; import static javax.lang.model.util.ElementFilter.fieldsIn; public class EntityHandler extends AbstractHandler { private GeneratorConfiguration generatorConfiguration = new GeneratorConfiguration(); private SqlUnitModel sqlUnit = new SqlUnitModel(); private PackageElement packageElement; public EntityHandler(ProcessingEnvironment processingEnv) { super(processingEnv); } public void phase1(Set sqlEntities, Set packages) throws Exception { for (TypeElement entity : sqlEntities) { AnnotationMirror sqlEntity = findAnnotation(SqlEntity.class, entity); for (Map.Entry v : sqlEntity.getElementValues().entrySet()) { switch (v.getKey().getSimpleName().toString()) { case "value": Class typeHandlerClass = getClass().getClassLoader().loadClass(v.getValue().getValue().toString()); TypeHandler typeHandler = (TypeHandler) typeHandlerClass.newInstance(); String type = entity.asType().toString(); generatorConfiguration.addTypeHandler(new TypeRef(type, type), typeHandler); System.out.println("Loaded TypeHandler for " + type + " through " + typeHandlerClass.getCanonicalName()); break; } } // System.out.println("sqlEntity.getElementValues() = " + sqlEntity.getElementValues()); } System.out.println("packages = " + packages); if (packages.size() == 0) { throw new CompilerException("There has to be exactly one @SqlEntitySet annotated package."); } packageElement = packages.iterator().next(); if (packages.size() != 1) { throw new CompilerException(packageElement, "There can only be one @SqlEntitySet annotated package."); } } private AnnotationMirror findAnnotation(Class c, TypeElement type) { TypeMirror annotationType = elements.getTypeElement(c.getCanonicalName()).asType(); for (AnnotationMirror a : type.getAnnotationMirrors()) { if (types.isSameType(a.getAnnotationType(), annotationType)) { return a; } } throw new CompilerException(type, "Could not find annotation " + c.getSimpleName()); } public void recordEntity(TypeElement element) throws Exception { EntityMirror entityMirror = new EntityMirror(generatorConfiguration, new TypeRef(types.getDeclaredType(element)), sqlName(element.getSimpleName().toString())); for (VariableElement f : fieldsIn(elements.getAllMembers(element))) { entityMirror.add(fromElement(generatorConfiguration, f)); } List idFields = new ArrayList<>(); for (FieldMirror field : entityMirror.fields) { if (field.id) { idFields.add(field); } } if (idFields.size() == 0) { throw new CompilerException(element, "An @Entity is required to have at least one @Id field."); } if (idFields.size() != 1) { throw new CompilerException(element, "This implementation only support a single @Id annotated field."); } sqlUnit.add(entityMirror); } public void generate(EntityMirror entityMirror) throws IOException { ClassG g = new ClassG(entityMirror.daoType); g.addPublicStaticFinalField(String.class, "createTableSql").value(toJavaString(entityMirror.createTableSql(sqlUnit))); g.addPublicStaticFinalField(String.class, "dropTableSql").value(toJavaString(entityMirror.dropTableSql())); g.addPublicStaticFinalField(String.class, "insertIntoSql").value(toJavaString(entityMirror.insertIntoSql())); g.addPublicStaticFinalField(String.class, "deleteFromSql").value(toJavaString(entityMirror.deleteFromSql())); entityMirror.insertInto(sqlUnit, g); entityMirror.delete(g); entityMirror.deleteById(g); JavaFileObject sourceFile = processingEnv.getFiler().createSourceFile(entityMirror.daoType.fqName, sqlUnit.elementForEntity(entityMirror)); try (PrintWriter w = new PrintWriter(sourceFile.openWriter())) { g.write(w); } } public FieldMirror fromElement(GeneratorConfiguration generatorConfiguration, VariableElement var) { TypeRef type = new TypeRef(var.asType()); // System.out.print("element = "); // elements.printElements(new PrintWriter(System.out), var); String javaName = var.getSimpleName().toString(); String sqlName = sqlName(javaName); boolean notNull = false; boolean unique = false; boolean id = isId(var); boolean primitive = generatorConfiguration.isPrimitive(type); if (id && !primitive) { throw new CompilerException(var, "A @Id field has to be a primitive or embedded."); } FieldMirror field; if (primitive) { field = new PrimitiveFieldMirror(type, javaName, sqlName, id, notNull, unique); } else if (generatorConfiguration.hasTypeHandler(type)) { throw new CompilerException(var, "Missing type handler for type: " + type.fqName); } else { field = new FieldMirror.ReferenceFieldMirror(type, javaName, sqlName, notNull, unique); } return field; } public static boolean isId(VariableElement var) { return var.getAnnotation(Id.class) != null; } public void phase3() throws Exception { for (EntityMirror entity : sqlUnit.getEntities().values()) { generate(entity); } generateSession(); } private void generateSession() throws IOException { String p = packageElement.getQualifiedName().toString(); // TODO: Support a name prefix from @SqlEntitySet TypeRef type = new TypeRef(p + ".Session"); ClassG g = new ClassG(type); /* TypeRef conType = g.addImport(Connection.class); Parameters parameters = new Parameters(); ParameterRef c = parameters.addParameter(conType, "c"); List body = new ArrayList<>(); for (EntityMirror entity : entities) { FieldRef fieldRef = g.addField(entity.javaName.asElement().asType(), toFieldName(entity.daoName)); body.add("this." + fieldRef.name + " = new " + entity.daoName + "(" + c.name + ");"); } g.addConstructor(parameters, body); */ JavaFileObject sourceFile = processingEnv.getFiler().createSourceFile(type.fqName, packageElement); try (PrintWriter w = new PrintWriter(sourceFile.openWriter())) { g.write(w); } } public static String sqlName(String javaName) { StringBuilder builder = new StringBuilder(); for (char c : javaName.toCharArray()) { char lower = Character.toLowerCase(c); if (isUpperCase(c) && builder.length() > 0) { builder.append("_"); } builder.append(lower); } return builder.toString(); } }