DBRowSubclassGenerator.java
/*
* To change this license header, choose License Headers in Project Properties.
* To change this template file, choose Tools | Templates
* and open the template in the editor.
*/
package nz.co.gregs.dbvolution.generation;
import nz.co.gregs.dbvolution.databases.metadata.Options;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.List;
import nz.co.gregs.dbvolution.DBRow;
import nz.co.gregs.dbvolution.annotations.*;
/* TODO: upgrade BurningWave */
import org.burningwave.core.assembler.ComponentContainer;
import org.burningwave.core.assembler.ComponentSupplier;
import static org.burningwave.core.assembler.StaticComponentContainer.Constructors;
import org.burningwave.core.classes.*;
/**
*
* @author gregorygraham
*/
public class DBRowSubclassGenerator {
private DBRowSubclassGenerator() {
}
public static void generate(List<DBTableClass> dbTableClasses, Options options) {
List<String> dbTableClassNames = new ArrayList<>(0);
for (DBTableClass dbt : dbTableClasses) {
dbTableClassNames.add(dbt.getClassName());
}
for (DBTableClass dbt : dbTableClasses) {
linkForeignKeys(dbt, dbTableClassNames);
}
List<DBTableClass> compileClasses = new ArrayList<>(0);
compileClasses.addAll(dbTableClasses);
List<DBTableClass> rejectedClasses = new ArrayList<>(0);
while (rejectedClasses.size() != compileClasses.size()) {
rejectedClasses.clear();
for (DBTableClass compileThis : compileClasses) {
try {
generateForClass(compileThis, options);
} catch (Exception exc) {
rejectedClasses.add(compileThis);
}
}
compileClasses.clear();
compileClasses.addAll(rejectedClasses);
}
}
private static void linkForeignKeys(DBTableClass dbt, List<String> dbTableClassNames) {
for (DBTableField dbf : dbt.getFields()) {
if (dbf.isForeignKey) {
if (dbf.referencedTable == null || dbf.referencesClass == null || dbf.referencesField == null) {
System.out.println("INSUFFICIENT DATA FOR FOREIGN KEY");
} else {
if (!dbTableClassNames.contains(dbf.referencesClass)) {
List<String> matchingNames = new ArrayList<>();
for (String name : dbTableClassNames) {
if (name.toLowerCase().startsWith(dbf.referencesClass.toLowerCase())) {
matchingNames.add(name);
}
}
if (matchingNames.size() == 1) {
String properClassname = matchingNames.get(0);
dbf.referencesClass = properClassname;
}
}
}
}
}
}
static final String DBRowPackageName = DBRow.class
.getPackage().getName();
private static void generateForClass(DBTableClass dbt, Options options) {
UnitSourceGenerator generator = UnitSourceGenerator.create(dbt.getPackageName());
var newDBRowClass = ClassSourceGenerator
.create(TypeDeclarationSourceGenerator.create(dbt.getClassName()));
if (dbt.getTableSchema() == null || "PUBLIC".equals(dbt.getTableSchema().toUpperCase()) || "dbo".equals(dbt.getTableSchema())) {
newDBRowClass.addAnnotation(
AnnotationSourceGenerator
.create(DBTableName.class
)
.addParameter(
VariableSourceGenerator
.create("value")
.setValue("\"" + dbt.getTableName() + "\"")
)
);
} else {
newDBRowClass.addAnnotation(
AnnotationSourceGenerator
.create(DBTableName.class
)
.addParameter(
VariableSourceGenerator
.create("value")
.setValue("\"" + dbt.getTableName() + "\""),
VariableSourceGenerator
.create("schema")
.setValue("\"" + dbt.getTableSchema() + "\"")
)
);
}
newDBRowClass.expands(DBRow.class
)
.addModifier(Modifier.PUBLIC)
.addConstructor(FunctionSourceGenerator.create().addModifier(Modifier.PUBLIC).addBodyCode());
newDBRowClass.addField(
VariableSourceGenerator.create(Long.class,
"serialVersionUID")
.addModifier(Modifier.PUBLIC).addModifier(Modifier.STATIC).addModifier(Modifier.FINAL)
.setValue(options.getVersionNumber() + "L")
);
for (DBTableField field : dbt.getFields()) {
VariableSourceGenerator newField = VariableSourceGenerator
.create(field.columnType, field.fieldName)
.addModifier(Modifier.PUBLIC)
.setValue("new " + field.columnType.getSimpleName() + "()");
if (field.comments == null || field.comments.isEmpty()) {
newField.addAnnotation(
AnnotationSourceGenerator.create(DBColumn.class
)
.addParameter(VariableSourceGenerator.create("value").setValue("\"" + field.columnName + "\""))
);
} else {
newField.addAnnotation(
AnnotationSourceGenerator.create(DBColumn.class
)
.addParameter(VariableSourceGenerator.create("value").setValue("\"" + field.columnName + "\""))
.addParameter(VariableSourceGenerator.create("comments").setValue("\"" + field.comments.replaceAll("\"", "\\\"") + "\""))
);
}
if (field.isPrimaryKey) {
newField.addAnnotation(
AnnotationSourceGenerator.create(DBPrimaryKey.class
)
);
}
if (field.isAutoIncrement) {
newField.addAnnotation(
AnnotationSourceGenerator.create(DBAutoIncrement.class
)
);
}
if (field.isForeignKey) {
if (options.getIncludeForeignKeyColumnName()) {
newField.addAnnotation(
AnnotationSourceGenerator.create(DBForeignKey.class
)
.addParameter(VariableSourceGenerator.create("value").setValue(dbt.getPackageName() + "." + field.referencesClass + ".class"))
.addParameter(VariableSourceGenerator.create("column").setValue("\"" + field.referencesField + "\""))
);
} else {
newField.addAnnotation(
AnnotationSourceGenerator.create(DBForeignKey.class
)
.addParameter(VariableSourceGenerator.create("value").setValue(dbt.getPackageName() + "." + field.referencesClass + ".class"))
);
}
}
if (dbt.getUnknownDatatype().equals(field.columnType)) {
newField.addAnnotation(
AnnotationSourceGenerator.create(DBUnknownJavaSQLType.class
)
.addParameter(VariableSourceGenerator.create("value").setValue("" + field.javaSQLDatatype))
);
}
newDBRowClass.addField(newField);
}
generator.addClass(newDBRowClass);
dbt.setJavaSource(generator.make());
getClassFromGenerator(generator, dbt);
}
private static void getClassFromGenerator(UnitSourceGenerator generator, DBTableClass dbt) {
//With this we store the generated source to a path
generator.storeToClassPath(System.getProperty("user.home") + "/dbvolution/temp");
ComponentSupplier componentSupplier = ComponentContainer.getInstance();
ClassFactory classFactory = componentSupplier.getClassFactory();
//this method compile all compilation units and upload the generated classes to default
//class loader declared with property "class-factory.default-class-loader" in
//burningwave.properties file (see "Overview and configuration").
//If you need to upload the class to another class loader use
//loadOrBuildAndDefine(LoadOrBuildAndDefineConfig) method
ClassFactory.ClassRetriever classRetriever = classFactory.loadOrBuildAndDefine(generator);
@SuppressWarnings("unchecked")
Class<? extends DBRow> generatedClass = (Class<? extends DBRow>) classRetriever.get(dbt.getFullyQualifiedName());
dbt.setGeneratedClass(generatedClass);
DBRow generatedClassObject = Constructors.newInstanceOf(generatedClass);
dbt.setGeneratedInstance(generatedClassObject);
}
}