package com.venky.swf.maven.plugin; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import java.io.OutputStreamWriter; import java.io.PrintWriter; import java.util.ArrayList; import java.util.HashSet; import java.util.List; import java.util.Set; import java.util.StringTokenizer; import org.apache.maven.plugin.AbstractMojo; import org.apache.maven.plugin.MojoExecutionException; import org.apache.maven.plugins.annotations.LifecyclePhase; import org.apache.maven.plugins.annotations.Mojo; import org.apache.maven.plugins.annotations.Parameter; import com.venky.core.collections.IgnoreCaseSet; import com.venky.core.string.StringUtil; import com.venky.core.util.ObjectUtil; import com.venky.swf.db.Database; import com.venky.swf.db.JdbcTypeHelper; import com.venky.swf.db.JdbcTypeHelper.TypeRef; import com.venky.swf.db.annotations.column.COLUMN_DEF; import com.venky.swf.db.annotations.column.COLUMN_NAME; import com.venky.swf.db.annotations.column.COLUMN_SIZE; import com.venky.swf.db.annotations.column.DECIMAL_DIGITS; import com.venky.swf.db.annotations.column.IS_NULLABLE; import com.venky.swf.db.annotations.column.defaulting.StandardDefault; import com.venky.swf.db.annotations.model.DBPOOL; import com.venky.swf.db.jdbc.ConnectionManager; import com.venky.swf.db.model.Model; import com.venky.swf.db.model.reflection.ModelReflector; import com.venky.swf.db.table.Table; import com.venky.swf.db.table.Table.ColumnDescriptor; import com.venky.swf.routing.Config; /** * Goal which generates models from database a.k.a reverse engineering. * */ @Mojo(defaultPhase=LifecyclePhase.COMPILE,name="generate-model") public class ModelGeneratorMojo extends AbstractMojo { @Parameter(property="generate-model.srcdir",defaultValue="src/main/java") File srcDir; @Parameter(property="pool",defaultValue="") String pool; @Parameter(property="project.build.sourceEncoding",defaultValue="UTF-8") String encoding; public void execute() throws MojoExecutionException { getLog().info("Generating models..."); generateModels(srcDir); getLog().info("Done"); } public void generateModels(File directory) throws MojoExecutionException{ Database.loadTables(true); if (!ObjectUtil.isVoid(this.pool)){ generateModelClass(directory, pool); }else { for (String pool : ConnectionManager.instance().getPools()){ generateModelClass(directory, pool); } } } private void generateModelClass(File directory, String pool) throws MojoExecutionException { for (Table<?> table: Database.getTables(pool).values()){ generateModelClass(table, directory,pool); } } private void generateModelClass(Table<?> table,File directory,String pool) throws MojoExecutionException{ String simpleModelClassName = Table.getSimpleModelClassName(table.getTableName()); String fQModelClassName = null; StringBuilder packageName = new StringBuilder(Config.instance().getModelPackageRoots().get(0)); if (packageName.charAt(packageName.length()-1) == '.'){ packageName.setLength(packageName.length() -1 ); } fQModelClassName = packageName.toString(); if (!fQModelClassName.endsWith(".")){ fQModelClassName += "."; } fQModelClassName += simpleModelClassName; String srcFileName = directory.getPath() + "/" + fQModelClassName.replace('.', '/')+".java"; File srcFile = new File(srcFileName); srcFile.getParentFile().mkdirs(); //Create all directories in the path. if (srcFile.exists()){ if (!table.isExistingInDatabase() && !table.isVirtual()){ getLog().info("Manually remove " + srcFileName + " to drop model"); } }else if (table.getReflector() == null){ OutputStreamWriter wr = null; try { wr = new OutputStreamWriter(new FileOutputStream(srcFile),encoding); writeFile(wr,table, packageName.toString(),pool); } catch (Exception e) { throw new MojoExecutionException(e.getMessage(),e); }finally { if (wr!=null){ try { wr.close(); } catch (IOException e) { // } } } } } private void writeFile(OutputStreamWriter osw,Table<?> table,String packageName, String pool){ JdbcTypeHelper helper = Database.getJdbcTypeHelper(pool); Set<String> columnsPresentInFrameworkModel = new IgnoreCaseSet(); columnsPresentInFrameworkModel.addAll(ModelReflector.instance(Model.class).getRealColumns()); Set<String> imports = new HashSet<String>(); List<String> code = new ArrayList<String>(); String extendingClass = null; if (table.getModelClass() == null){ imports.add("com.venky.swf.db.model.Model"); extendingClass = "Model"; }else { extendingClass = table.getModelClass().getName(); if (extendingClass.equals(packageName + "." +Table.getSimpleModelClassName(table.getTableName()))){ imports.add("com.venky.swf.db.model.Model"); extendingClass = "Model"; } if (!table.getModelClass().getName().startsWith(packageName)){ columnsPresentInFrameworkModel.addAll(table.getReflector().getRealColumns()); } } if (!ObjectUtil.isVoid(pool)){ imports.add(DBPOOL.class.getName()); code.add("@DBPOOL(\"" + pool + "\")"); } code.add("public interface " + Table.getSimpleModelClassName(table.getTableName()) + " extends " + extendingClass + "{"); for (ColumnDescriptor cd:table.getColumnDescriptors()){ List<TypeRef<?>> refs = helper.getTypeRefs(cd.getJDBCType()); TypeRef<?> ref = null; if (refs == null){ getLog().error("cannot find jdbc type with helper for pool" + pool + " with helper " + helper.getClass().getName()); throw new NullPointerException("cannot find jdbc type for column " + cd ); } for (TypeRef<?>r :refs){ ref = r; if (cd.isNullable() ){ if (!r.getJavaClass().isPrimitive()){ break; } }else{ if (r.getJavaClass().isPrimitive()){ break; } } } String columnName = cd.getName(); if (columnsPresentInFrameworkModel.contains(columnName)){ continue; //Framework definition must stay. } String camelfieldName = StringUtil.camelize(columnName); if (ref == null){ code.add("\tpublic Unknown get"+camelfieldName+"();" ); code.add("\tpublic void set"+camelfieldName+"(Unknown " + StringUtil.camelize(columnName,false) + ");" ); }else { String getterPrefix = "get"; if (boolean.class.isAssignableFrom(ref.getJavaClass()) || Boolean.class.isAssignableFrom(ref.getJavaClass()) ){ getterPrefix = "is"; } if (!ref.getJavaClass().isPrimitive() && !ref.getJavaClass().getPackage().getName().startsWith("java.lang")){ imports.add(ref.getJavaClass().getName()); } code.add("\t"); if (!cd.isNullable() && !ref.getJavaClass().isPrimitive()){ imports.add(IS_NULLABLE.class.getName()); code.add("\t@IS_NULLABLE(false)"); } if (cd.getSize() > 0 && ref.getSize() > 0 && ref.getSize() != cd.getSize() ){ imports.add(COLUMN_SIZE.class.getName()); code.add("\t@COLUMN_SIZE("+cd.getSize()+")"); } if (cd.getScale() > 0 && ref.getScale() != cd.getScale()){ imports.add(DECIMAL_DIGITS.class.getName()); code.add("\t@DECIMAL_DIGITS("+cd.getScale() +")"); } if (!cd.isNullable() && !ObjectUtil.isVoid(cd.getColumnDefault())){ imports.add(COLUMN_DEF.class.getName()); imports.add(StandardDefault.class.getName()); code.add("\t"+toAppDefaultStr(helper, ref, cd.getColumnDefault())); } if (!StringUtil.underscorize(camelfieldName).equals(columnName.toUpperCase())){ imports.add(COLUMN_NAME.class.getName()); code.add("\t@COLUMN_NAME(\""+ cd.getName().toUpperCase() + "\")"); } code.add("\tpublic "+ ref.getJavaClass().getSimpleName() + " " + getterPrefix + camelfieldName + "();"); code.add("\tpublic void set" + camelfieldName + "("+ ref.getJavaClass().getSimpleName() + " " + StringUtil.camelize(columnName,false) + ");"); if (camelfieldName.endsWith("Id")) { String possibleReferredModelName = camelfieldName.substring(0, camelfieldName.length() - "Id".length()); String possibleReferredTableName = StringUtil.underscorize(StringUtil.pluralize(possibleReferredModelName)); Table<? extends Model> referredTable = Database.getTable(possibleReferredTableName); if (referredTable != null){ code.add("\tpublic " + possibleReferredModelName + " get" + possibleReferredModelName + "();"); } } } } code.add("}"); PrintWriter w = new PrintWriter(osw); w.println("package " + packageName + ";"); for (String imp:imports){ w.println("import " + imp + ";"); } for (String line:code){ w.println(line); } } private String toAppDefaultStr(JdbcTypeHelper helper , TypeRef<?> ref , String dbDefault){ if (ObjectUtil.equals(dbDefault,helper.getCurrentTimeStampKW())){ return "@COLUMN_DEF(StandardDefault.CURRENT_TIMESTAMP)"; }else if (ObjectUtil.equals(dbDefault, helper.getCurrentDateKW())){ return "@COLUMN_DEF(StandardDefault.CURRENT_DATE)"; }else if (dbDefault != null ){ Class<?> refClass = ref.getJavaClass(); if (refClass == boolean.class || refClass == Boolean.class){ if (helper.getDefaultKW(ref,Boolean.valueOf(true)).equals(dbDefault)){ return "@COLUMN_DEF(StandardDefault.BOOLEAN_TRUE)"; }else { return "@COLUMN_DEF(StandardDefault.BOOLEAN_FALSE)"; } }else if (ref.isNumeric()){ if (helper.getDefaultKW(ref,0).equals(dbDefault)){ return "@COLUMN_DEF(StandardDefault.ZERO)"; } if (helper.getDefaultKW(ref,1).equals(dbDefault)){ return "@COLUMN_DEF(StandardDefault.ONE)"; } } if (ref.isColumnDefaultQuoted()) { StringTokenizer tok = new StringTokenizer(dbDefault, "'",false); return "@COLUMN_DEF(value=StandardDefault.SOME_VALUE,someValue=\""+tok.nextToken() +"\")"; }else { return "@COLUMN_DEF(value=StandardDefault.SOME_VALUE,someValue=\""+dbDefault+"\")"; } }else { return "@COLUMN_DEF(value=StandardDefault.NULL)"; } } }