From d0d362c58c7d31e0bf76b4c990a199c3d3b65671 Mon Sep 17 00:00:00 2001 From: moparisthebest Date: Wed, 7 Jun 2017 21:29:28 -0400 Subject: [PATCH] Implement Cleaner interface support in JdbcMapper, relax Cleaner interface --- .../java/com/moparisthebest/jdbc/Cleaner.java | 2 +- .../codegen/CompileTimeResultSetMapper.java | 70 +++++++++++-------- .../jdbc/codegen/JdbcMapperProcessor.java | 30 ++++++-- .../jdbc/codegen/PersonDAO.java | 21 ++++++ 4 files changed, 89 insertions(+), 34 deletions(-) diff --git a/common/src/main/java/com/moparisthebest/jdbc/Cleaner.java b/common/src/main/java/com/moparisthebest/jdbc/Cleaner.java index 6db48dd..3dba658 100644 --- a/common/src/main/java/com/moparisthebest/jdbc/Cleaner.java +++ b/common/src/main/java/com/moparisthebest/jdbc/Cleaner.java @@ -2,5 +2,5 @@ package com.moparisthebest.jdbc; public interface Cleaner { - public T clean(T dto); + public E clean(E dto); } diff --git a/jdbcmapper/src/main/java/com/moparisthebest/jdbc/codegen/CompileTimeResultSetMapper.java b/jdbcmapper/src/main/java/com/moparisthebest/jdbc/codegen/CompileTimeResultSetMapper.java index 715e620..b920bd9 100644 --- a/jdbcmapper/src/main/java/com/moparisthebest/jdbc/codegen/CompileTimeResultSetMapper.java +++ b/jdbcmapper/src/main/java/com/moparisthebest/jdbc/codegen/CompileTimeResultSetMapper.java @@ -56,17 +56,17 @@ public class CompileTimeResultSetMapper { return typeMirrorStringNoGenerics(returnType); } - public void mapToResultType(final Writer w, final String[] keys, final ExecutableElement eeMethod, final int arrayMaxLength, final String cal) throws IOException, NoSuchMethodException, ClassNotFoundException { + public void mapToResultType(final Writer w, final String[] keys, final ExecutableElement eeMethod, final int arrayMaxLength, final String cal, final String cleaner) throws IOException, NoSuchMethodException, ClassNotFoundException { //final Method m = fromExecutableElement(eeMethod); //final Class returnType = m.getReturnType(); final TypeMirror returnTypeMirror = eeMethod.getReturnType(); //final Class returnType = typeMirrorToClass(returnTypeMirror); if (returnTypeMirror.getKind() == TypeKind.ARRAY) { final TypeMirror componentType = ((ArrayType) returnTypeMirror).getComponentType(); - toArray(w, keys, componentType, arrayMaxLength, cal); + toArray(w, keys, componentType, arrayMaxLength, cal, cleaner); } else if (types.isAssignable(returnTypeMirror, collectionType)) { final List typeArguments = ((DeclaredType) returnTypeMirror).getTypeArguments(); - toCollection(w, keys, returnTypeMirror, typeArguments.get(0), arrayMaxLength, cal); + toCollection(w, keys, returnTypeMirror, typeArguments.get(0), arrayMaxLength, cal, cleaner); } else if (types.isAssignable(returnTypeMirror, mapType)) { final List typeArguments = ((DeclaredType) returnTypeMirror).getTypeArguments(); //if (types[1] instanceof ParameterizedType) { // for collectionMaps @@ -78,18 +78,18 @@ public class CompileTimeResultSetMapper { typeArguments.get(0), collectionTypeMirror, componentTypeMirror, - arrayMaxLength, cal); + arrayMaxLength, cal, cleaner); return; } - toMap(w, keys, returnTypeMirror, typeArguments.get(0), typeArguments.get(1), arrayMaxLength, cal); + toMap(w, keys, returnTypeMirror, typeArguments.get(0), typeArguments.get(1), arrayMaxLength, cal, cleaner); } else if (types.isAssignable(returnTypeMirror, iteratorType)) { final List typeArguments = ((DeclaredType) returnTypeMirror).getTypeArguments(); if (types.isAssignable(returnTypeMirror, listIteratorType)) - toListIterator(w, keys, typeArguments.get(0), arrayMaxLength, cal); + toListIterator(w, keys, typeArguments.get(0), arrayMaxLength, cal, cleaner); else - toIterator(w, keys, typeArguments.get(0), arrayMaxLength, cal); + toIterator(w, keys, typeArguments.get(0), arrayMaxLength, cal, cleaner); } else { - toObject(w, keys, returnTypeMirror, cal); + toObject(w, keys, returnTypeMirror, cal, cleaner); } } @@ -101,13 +101,15 @@ public class CompileTimeResultSetMapper { getRowMapper(keys, returnTypeMirror, cal, null, null).gen(w, returnTypeMirror.toString()); } - public void toObject(final Writer w, final String[] keys, final TypeMirror returnTypeMirror, final String cal) throws IOException, ClassNotFoundException { + public void toObject(final Writer w, final String[] keys, final TypeMirror returnTypeMirror, final String cal, final String cleaner) throws IOException, ClassNotFoundException { w.write("\t\t\tif(rs.next()) {\n"); writeObject(w, keys, returnTypeMirror, cal); - w.write("\t\t\t\treturn ret;\n\t\t\t} else {\n\t\t\t\treturn null;\n\t\t\t}\n"); + w.write("\t\t\t\treturn "); + // this does not clean null on purpose, neither does CleaningResultSetMapper + clean(w, cleaner).write(";\n\t\t\t} else {\n\t\t\t\treturn null;\n\t\t\t}\n"); } - public void writeCollection(final Writer w, final String[] keys, final String returnTypeString, final String concreteTypeString, final TypeMirror componentTypeMirror, int arrayMaxLength, String cal) throws IOException, ClassNotFoundException { + public void writeCollection(final Writer w, final String[] keys, final String returnTypeString, final String concreteTypeString, final TypeMirror componentTypeMirror, int arrayMaxLength, String cal, final String cleaner) throws IOException, ClassNotFoundException { w.write("\t\t\tfinal "); w.write(returnTypeString); w.write(" _colret = new "); @@ -115,36 +117,37 @@ public class CompileTimeResultSetMapper { w.write(returnTypeString.substring(returnTypeString.indexOf('<'))); w.write("();\n\t\t\twhile(rs.next()) {\n"); writeObject(w, keys, componentTypeMirror, cal); - w.write("\t\t\t\t_colret.add(ret);\n\t\t\t}\n"); + w.write("\t\t\t\t_colret.add("); + clean(w, cleaner).write(");\n\t\t\t}\n"); } - public void toCollection(final Writer w, final String[] keys, final TypeMirror collectionTypeMirror, final TypeMirror componentTypeMirror, int arrayMaxLength, String cal) throws IOException, ClassNotFoundException { + public void toCollection(final Writer w, final String[] keys, final TypeMirror collectionTypeMirror, final TypeMirror componentTypeMirror, int arrayMaxLength, String cal, final String cleaner) throws IOException, ClassNotFoundException { final String collectionType = getConcreteClassCanonicalName(collectionTypeMirror, ArrayList.class); - writeCollection(w, keys, collectionTypeMirror.toString(), collectionType, componentTypeMirror, arrayMaxLength, cal); + writeCollection(w, keys, collectionTypeMirror.toString(), collectionType, componentTypeMirror, arrayMaxLength, cal, cleaner); w.write("\t\t\treturn _colret;\n"); } - public void toArray(final Writer w, final String[] keys, final TypeMirror componentTypeMirror, int arrayMaxLength, String cal) throws IOException, ClassNotFoundException { + public void toArray(final Writer w, final String[] keys, final TypeMirror componentTypeMirror, int arrayMaxLength, String cal, final String cleaner) throws IOException, ClassNotFoundException { final String returnTypeString = componentTypeMirror.toString(); - writeCollection(w, keys, "java.util.List<" + returnTypeString + ">", "java.util.ArrayList", componentTypeMirror, arrayMaxLength, cal); + writeCollection(w, keys, "java.util.List<" + returnTypeString + ">", "java.util.ArrayList", componentTypeMirror, arrayMaxLength, cal, cleaner); w.write("\t\t\treturn _colret.toArray(new "); w.write(returnTypeString); w.write("[_colret.size()]);\n"); } - public void toListIterator(final Writer w, final String[] keys, final TypeMirror componentTypeMirror, int arrayMaxLength, String cal) throws IOException, ClassNotFoundException { + public void toListIterator(final Writer w, final String[] keys, final TypeMirror componentTypeMirror, int arrayMaxLength, String cal, final String cleaner) throws IOException, ClassNotFoundException { final String returnTypeString = componentTypeMirror.toString(); - writeCollection(w, keys, "java.util.List<" + returnTypeString + ">", "java.util.ArrayList", componentTypeMirror, arrayMaxLength, cal); + writeCollection(w, keys, "java.util.List<" + returnTypeString + ">", "java.util.ArrayList", componentTypeMirror, arrayMaxLength, cal, cleaner); w.write("\t\t\treturn _colret.listIterator();\n"); } - public void toIterator(final Writer w, final String[] keys, final TypeMirror componentTypeMirror, int arrayMaxLength, String cal) throws IOException, ClassNotFoundException { + public void toIterator(final Writer w, final String[] keys, final TypeMirror componentTypeMirror, int arrayMaxLength, String cal, final String cleaner) throws IOException, ClassNotFoundException { final String returnTypeString = componentTypeMirror.toString(); - writeCollection(w, keys, "java.util.List<" + returnTypeString + ">", "java.util.ArrayList", componentTypeMirror, arrayMaxLength, cal); + writeCollection(w, keys, "java.util.List<" + returnTypeString + ">", "java.util.ArrayList", componentTypeMirror, arrayMaxLength, cal, cleaner); w.write("\t\t\treturn _colret.iterator();\n"); } - public void toMap(final Writer w, final String[] keys, final TypeMirror mapTypeMirror, final TypeMirror mapKeyTypeMirror, final TypeMirror componentTypeMirror, int arrayMaxLength, String cal) throws IOException, ClassNotFoundException { + public void toMap(final Writer w, final String[] keys, final TypeMirror mapTypeMirror, final TypeMirror mapKeyTypeMirror, final TypeMirror componentTypeMirror, int arrayMaxLength, String cal, final String cleaner) throws IOException, ClassNotFoundException { final String mapType = getConcreteClassCanonicalName(mapTypeMirror, HashMap.class); final String returnTypeString = mapTypeMirror.toString(); w.write("\t\t\tfinal "); @@ -159,16 +162,17 @@ public class CompileTimeResultSetMapper { rm.gen(w, componentTypeMirror.toString()); w.write("\t\t\t\t_colret.put("); rm.extractColumnValueString(w, 1, mapKeyTypeMirror); - w.write(", ret);\n\t\t\t}\n"); + w.write(", "); + clean(w, cleaner).write(");\n\t\t\t}\n"); w.write("\t\t\treturn _colret;\n"); } public void toMapCollection(final Writer w, final String[] keys, - final TypeMirror mapTypeMirror, - final TypeMirror mapKeyTypeMirror, - final TypeMirror collectionTypeMirror, - final TypeMirror componentTypeMirror, - int arrayMaxLength, String cal) throws IOException, ClassNotFoundException { + final TypeMirror mapTypeMirror, + final TypeMirror mapKeyTypeMirror, + final TypeMirror collectionTypeMirror, + final TypeMirror componentTypeMirror, + int arrayMaxLength, String cal, final String cleaner) throws IOException, ClassNotFoundException { final String mapType = getConcreteClassCanonicalName(mapTypeMirror, HashMap.class); final String collectionType = getConcreteClassCanonicalName(collectionTypeMirror, ArrayList.class); final String returnTypeString = mapTypeMirror.toString(); @@ -194,6 +198,16 @@ public class CompileTimeResultSetMapper { w.write(" _collist = _colret.get(_colkey);\n\t\t\t\tif(_collist == null) {\n\t\t\t\t\t_collist = new "); w.write(collectionType); w.write(collectionTypeString.substring(collectionTypeString.indexOf('<'))); - w.write("();\n\t\t\t\t\t_colret.put(_colkey, _collist);\n\t\t\t\t}\n\t\t\t\t_collist.add(ret);\n\t\t\t}\n\t\t\treturn _colret;\n"); + w.write("();\n\t\t\t\t\t_colret.put(_colkey, _collist);\n\t\t\t\t}\n\t\t\t\t_collist.add("); + clean(w, cleaner).write(");\n\t\t\t}\n\t\t\treturn _colret;\n"); + } + + private Writer clean(final Writer w, final String cleaner) throws IOException { + if(cleaner == null) + w.write("ret"); + else { + w.append(cleaner).append(".clean(ret)"); + } + return w; } } diff --git a/jdbcmapper/src/main/java/com/moparisthebest/jdbc/codegen/JdbcMapperProcessor.java b/jdbcmapper/src/main/java/com/moparisthebest/jdbc/codegen/JdbcMapperProcessor.java index ceac96e..3940d7f 100644 --- a/jdbcmapper/src/main/java/com/moparisthebest/jdbc/codegen/JdbcMapperProcessor.java +++ b/jdbcmapper/src/main/java/com/moparisthebest/jdbc/codegen/JdbcMapperProcessor.java @@ -1,5 +1,7 @@ package com.moparisthebest.jdbc.codegen; +import com.moparisthebest.jdbc.Cleaner; + import javax.annotation.processing.*; import javax.lang.model.SourceVersion; import javax.lang.model.element.*; @@ -29,7 +31,8 @@ public class JdbcMapperProcessor extends AbstractProcessor { private Types types; private TypeMirror sqlExceptionType, stringType, numberType, utilDateType, readerType, clobType, - byteArrayType, inputStreamType, fileType, blobType, sqlArrayType, collectionType, calendarType; + byteArrayType, inputStreamType, fileType, blobType, sqlArrayType, collectionType, calendarType, cleanerType; + private TypeElement cleanerElement; private JdbcMapper.DatabaseType defaultDatabaseType; private String defaultArrayNumberTypeName, defaultArrayStringTypeName; private CompileTimeResultSetMapper rsm; @@ -60,6 +63,10 @@ public class JdbcMapperProcessor extends AbstractProcessor { byteArrayType = types.getArrayType(types.getPrimitiveType(TypeKind.BYTE)); sqlArrayType = elements.getTypeElement(java.sql.Array.class.getCanonicalName()).asType(); collectionType = types.getDeclaredType(elements.getTypeElement(Collection.class.getCanonicalName()), types.getWildcardType(null, null)); + + cleanerElement = elements.getTypeElement(Cleaner.class.getCanonicalName()); + cleanerType = types.getDeclaredType(cleanerElement, types.getWildcardType(null, null)); + final String databaseType = processingEnv.getOptions().get("JdbcMapper.databaseType"); defaultDatabaseType = databaseType == null ? JdbcMapper.DatabaseType.STANDARD : JdbcMapper.DatabaseType.valueOf(databaseType); defaultArrayNumberTypeName = processingEnv.getOptions().get("JdbcMapper.arrayNumberTypeName"); @@ -212,7 +219,7 @@ public class JdbcMapperProcessor extends AbstractProcessor { // build query and bind param order final List bindParams = new ArrayList(); final String sqlStatement; - String calendarName = null; + String calendarName = null, cleanerName = null; boolean sqlExceptionThrown = false; { // now parameters @@ -289,9 +296,22 @@ public class JdbcMapperProcessor extends AbstractProcessor { for (final Map.Entry unusedParam : unusedParams.entrySet()) { // look for lone calendar object - if(types.isAssignable(unusedParam.getValue().asType(), calendarType) && calendarName == null) + final TypeMirror unusedType = unusedParam.getValue().asType(); + if(types.isAssignable(unusedType, calendarType) && calendarName == null) calendarName = unusedParam.getKey(); - else + else if(types.isAssignable(unusedType, cleanerType) && cleanerName == null) { + cleanerName = unusedParam.getKey(); + /* + // yuck this falls apart for anything other than plain objects, might as well just let it be a compile time error? + final TypeMirror requiredType = types.getDeclaredType(cleanerElement, types.getWildcardType(null, eeMethod.getReturnType())); + if(types.isAssignable(unusedType, requiredType)) + cleanerName = unusedParam.getKey(); + else + processingEnv.getMessager().printMessage(Diagnostic.Kind.ERROR, + String.format("@JdbcMapper.SQL method has unused parameter '%s' of cleaner type '%s' when cleaner type required is '%s'", unusedParam.getKey(), unusedType, requiredType), + unusedParam.getValue()); + */ + } else processingEnv.getMessager().printMessage(Diagnostic.Kind.ERROR, String.format("@JdbcMapper.SQL method has unused parameter '%s'", unusedParam.getKey()), unusedParam.getValue()); } } @@ -341,7 +361,7 @@ public class JdbcMapperProcessor extends AbstractProcessor { processingEnv.getMessager().printMessage(Diagnostic.Kind.ERROR, "@JdbcMapper.SQL sql parsed a wildcard column name which is not supported", methodElement); return false; } - rsm.mapToResultType(w, keys, eeMethod, sql.arrayMaxLength(), calendarName); + rsm.mapToResultType(w, keys, eeMethod, sql.arrayMaxLength(), calendarName, cleanerName); } // if no SQLException is thrown, we have to catch it here and wrap it with RuntimeException... diff --git a/jdbcmapper/src/test/java/com/moparisthebest/jdbc/codegen/PersonDAO.java b/jdbcmapper/src/test/java/com/moparisthebest/jdbc/codegen/PersonDAO.java index c99b93e..5cc1453 100644 --- a/jdbcmapper/src/test/java/com/moparisthebest/jdbc/codegen/PersonDAO.java +++ b/jdbcmapper/src/test/java/com/moparisthebest/jdbc/codegen/PersonDAO.java @@ -1,6 +1,8 @@ package com.moparisthebest.jdbc.codegen; +import com.moparisthebest.jdbc.Cleaner; import com.moparisthebest.jdbc.dto.FieldPerson; +import com.moparisthebest.jdbc.dto.Person; import java.sql.SQLException; import java.sql.Time; @@ -91,4 +93,23 @@ public interface PersonDAO { java.util.Date getBirthDateUtilDate(long personNo) throws SQLException; @JdbcMapper.SQL("SELECT birth_date FROM person WHERE person_no = {personNo}") java.util.Date getBirthDateUtilDate(long personNo, Calendar mycal) throws SQLException; + + // cleaner checks + @JdbcMapper.SQL("SELECT first_name, last_name, birth_date FROM person WHERE person_no = {personNo}") + FieldPerson getPersonCleanFieldPerson(long personNo, Cleaner clean) throws SQLException; + @JdbcMapper.SQL("SELECT first_name, last_name, birth_date FROM person WHERE person_no = {personNo}") + FieldPerson getPersonCleanPerson(long personNo, Cleaner clean) throws SQLException; + @JdbcMapper.SQL("SELECT first_name, last_name, birth_date FROM person WHERE person_no = {personNo}") + FieldPerson getPersonCleanObject(long personNo, Cleaner clean) throws SQLException; + @JdbcMapper.SQL("SELECT first_name, last_name, birth_date FROM person WHERE person_no = {personNo}") + List getPersonCleanPersonList(long personNo, Cleaner clean) throws SQLException; + @JdbcMapper.SQL("SELECT first_name, last_name, birth_date FROM person WHERE person_no = {personNo}") + Map getPersonCleanPersonMap(long personNo, Cleaner clean) throws SQLException; + @JdbcMapper.SQL("SELECT first_name, last_name, birth_date FROM person WHERE person_no = {personNo}") + Map> getPersonCleanPersonMapList(long personNo, Cleaner clean) throws SQLException; + /* + // this should NOT compile: + @JdbcMapper.SQL("SELECT first_name, last_name, birth_date FROM person WHERE person_no = {personNo}") + FieldPerson getPersonCleanNumber(long personNo, Cleaner clean) throws SQLException; + */ }