package com.github.florent37.rxandroidorm.generator; import com.squareup.javapoet.ArrayTypeName; import com.squareup.javapoet.ClassName; import com.squareup.javapoet.MethodSpec; import com.squareup.javapoet.ParameterizedTypeName; import com.squareup.javapoet.TypeName; import com.squareup.javapoet.TypeSpec; import com.squareup.javapoet.TypeVariableName; import java.util.ArrayList; import java.util.List; import javax.lang.model.element.Modifier; import com.github.florent37.rxandroidorm.Constants; import com.github.florent37.rxandroidorm.ProcessUtils; /** * Created by florentchampigny on 26/01/2016. */ public class QueryBuilderGenerator { public TypeSpec generate() { return TypeSpec.classBuilder(Constants.QUERY_BUILDER_SUFFIX) .addModifiers(Modifier.PUBLIC, Modifier.ABSTRACT) .addField(ClassName.get(StringBuilder.class), "queryBuilder", Modifier.PROTECTED) .addField(ClassName.get(StringBuilder.class), "orderBuilder", Modifier.PROTECTED) .addField(ClassName.get(StringBuilder.class), "limitBuilder", Modifier.PROTECTED) .addField(ProcessUtils.listOf(String.class), "args", Modifier.PROTECTED) .addField(ProcessUtils.listOf(String.class), "fromTables", Modifier.PROTECTED) .addField(ProcessUtils.listOf(String.class), "fromTablesNames", Modifier.PROTECTED) .addField(ProcessUtils.listOf(String.class), "fromTablesId", Modifier.PROTECTED) .addField(TypeName.BOOLEAN, "named", Modifier.PROTECTED) .addField(ClassName.get(Constants.DAO_PACKAGE, Constants.QUERY_LOGGER), "logger", Modifier.PROTECTED) .addMethod(MethodSpec.constructorBuilder() .addModifiers(Modifier.PUBLIC) .addStatement("this.queryBuilder = new $T()", ClassName.get(StringBuilder.class)) .addStatement("this.orderBuilder = new $T()", ClassName.get(StringBuilder.class)) .addStatement("this.args = new $T()", ProcessUtils.arraylistOf(String.class)) .addStatement("this.fromTables = new $T()", ProcessUtils.arraylistOf(String.class)) .addStatement("this.fromTablesNames = new $T()", ProcessUtils.arraylistOf(String.class)) .addStatement("this.fromTablesId = new $T()", ProcessUtils.arraylistOf(String.class)) .build()) .addMethod(MethodSpec.constructorBuilder() .addModifiers(Modifier.PUBLIC) .addParameter(TypeName.BOOLEAN, "named") .addParameter(ClassName.get(Constants.DAO_PACKAGE, Constants.QUERY_LOGGER), "logger") .addStatement("this()") .addStatement("this.named = named") .addStatement("this.logger = logger") .build()) .addMethod(MethodSpec.methodBuilder("appendOr") .addModifiers(Modifier.PROTECTED) .addStatement("queryBuilder.append($S)", " or ") .build()) .addMethod(MethodSpec.methodBuilder("appendAnd") .addModifiers(Modifier.PROTECTED) .addStatement("queryBuilder.append($S)", " and ") .build()) .addMethod(MethodSpec.methodBuilder("appendBeginGroup") .addModifiers(Modifier.PROTECTED) .addStatement("queryBuilder.append($S)", " ( ") .build()) .addMethod(MethodSpec.methodBuilder("appendEndGroup") .addModifiers(Modifier.PROTECTED) .addStatement("queryBuilder.append($S)", " ) ") .build()) .addMethod(MethodSpec.methodBuilder("limitStartNumber") .addModifiers(Modifier.PROTECTED) .addParameter(TypeName.INT, "start") .addParameter(TypeName.INT, "count") .addStatement("this.limitBuilder = new $T()", ClassName.get(StringBuilder.class)) .addStatement("this.limitBuilder.append(start).append($S).append(count)", ", ") .build()) .addMethod(MethodSpec.methodBuilder("appendSortAsc") .addModifiers(Modifier.PROTECTED) .addParameter(ClassName.get(String.class), "tableName") .addParameter(ClassName.get(String.class), "column") .addStatement("if(orderBuilder.length() != 0) queryBuilder.append(',')") .addStatement("orderBuilder.append(tableName).append(column)") .addStatement("orderBuilder.append($S)", " ASC ") .build()) .addMethod(MethodSpec.methodBuilder("appendSortDesc") .addModifiers(Modifier.PROTECTED) .addParameter(ClassName.get(String.class), "tableName") .addParameter(ClassName.get(String.class), "column") .addStatement("if(orderBuilder.length() != 0) queryBuilder.append(',')") .addStatement("orderBuilder.append(tableName).append(column)") .addStatement("orderBuilder.append($S)", " DESC ") .build()) .addMethod(MethodSpec.methodBuilder("query") .returns(TypeName.get(String.class)) .addModifiers(Modifier.PUBLIC) .addParameter(TypeName.get(String.class), "fromTable") .addParameter(TypeName.get(String.class), "joinTable") .addParameter(TypeName.get(String.class), "joinIdFrom") .addParameter(TypeName.get(String.class), "joinIdTo") .addParameter(TypeName.get(String.class), "table") .addParameter(TypeName.get(String.class), "variable") .addParameter(ProcessUtils.listOf(String.class), "args") .addStatement("args.addAll(this.args)") .addStatement("queryBuilder.append(\" AND \").append(joinTable).append(\".\").append(joinIdFrom).append(\" = \").append(fromTable).append(\".$L\")", Constants.FIELD_ID) .addStatement("queryBuilder.append(\" AND \").append(joinTable).append(\".\").append(joinIdTo).append(\" = \").append(table).append(\".$L\")", Constants.FIELD_ID) .addStatement("queryBuilder.append(\" AND \").append(joinTable).append(\".$L = '\").append(variable).append(\"'\")", Constants.FIELD_NAME) .addStatement("return queryBuilder.toString().replace($S,table)", Constants.QUERY_NAMED) .build()) .addMethod(MethodSpec.methodBuilder("constructArgs") .returns(TypeName.get(String[].class)) .addModifiers(Modifier.PROTECTED) .addStatement("return args.toArray(new String[args.size()])") .build()) .addMethod(MethodSpec.methodBuilder("constructQuery") .returns(TypeName.get(String.class)) .addModifiers(Modifier.PUBLIC) .addStatement("$T query = new $T()", ClassName.get(StringBuilder.class), ClassName.get(StringBuilder.class)) .addStatement("for($T s : fromTables) query.append($S).append(s)", ClassName.get(String.class), ", ") .addStatement("if (queryBuilder.length() != 0) query.append($S)", " where ") .addStatement("query.append(queryBuilder.toString())") .addStatement("if(orderBuilder.length() != 0) query.append($S)", " ORDER BY ") .addStatement("query.append(orderBuilder.toString())") .addStatement("if(limitBuilder != null && limitBuilder.length() != 0){ query.append($S); query.append(limitBuilder.toString()); }", " LIMIT ") .addStatement("return query.toString()") .build()) .addMethod(MethodSpec.methodBuilder("appendQuery") .addModifiers(Modifier.PROTECTED) .addParameter(TypeName.get(String.class), "conditional") .addParameter(TypeName.get(String.class), "arg") .addStatement("if (named) queryBuilder.append($S)", "NAMED.") .addStatement("queryBuilder.append(conditional)") .addStatement("if(arg != null) args.add(arg)") .build()) .addMethod(MethodSpec.methodBuilder("getTableId") .addModifiers(Modifier.PROTECTED) .returns(ClassName.get(String.class)) .addParameter(ClassName.get(String.class), "tableName") .addStatement("$T tableId", ClassName.get(String.class)) .addStatement("int tablePos = fromTablesNames.indexOf(tableName)") .addStatement("if(tablePos != -1) tableId = fromTablesId.get(tablePos)") .addStatement("else{ tableId = $S + fromTables.size(); fromTablesId.add(tableId); fromTables.add(tableName + \" \" + tableId); fromTablesNames.add(tableName); }", Constants.QUERY_TABLE_VARIABLE) .addStatement("return tableId") .build()) .addTypes(generateSelectors()) .build(); } protected List<TypeSpec> generateSelectors() { List<TypeSpec> typeSpecs = new ArrayList<>(); typeSpecs.add(TypeSpec.classBuilder(Constants.SELECTOR_NUMBER) .addModifiers(Modifier.PUBLIC, Modifier.STATIC) .addTypeVariable(TypeVariableName.get("Q1", Constants.queryBuilderClassName)) .addTypeVariable(TypeVariableName.get("M1", TypeName.get(Number.class))) .addField(TypeVariableName.get("Q1"), "queryBuilder", Modifier.PROTECTED) .addField(TypeName.get(String.class), "column", Modifier.PROTECTED) .addMethod(MethodSpec.constructorBuilder() .addModifiers(Modifier.PUBLIC) .addParameter(TypeVariableName.get("Q1"), "queryBuilder") .addParameter(TypeName.get(String.class), "column") .addStatement("this.queryBuilder = queryBuilder") .addStatement("this.column = column") .build()) .addMethod(MethodSpec.methodBuilder("equalsTo") .addModifiers(Modifier.PUBLIC) .returns(TypeVariableName.get("Q1")) .addParameter(TypeVariableName.get("M1"), "value") .addStatement("queryBuilder.appendQuery(column+\" = ?\",String.valueOf(value))") .addStatement("return queryBuilder") .build()) .addMethod(MethodSpec.methodBuilder("notEqualsTo") .addModifiers(Modifier.PUBLIC) .returns(TypeVariableName.get("Q1")) .addParameter(TypeVariableName.get("M1"), "value") .addStatement("queryBuilder.appendQuery(column+\" != ?\",String.valueOf(value))") .addStatement("return queryBuilder") .build()) .addMethod(MethodSpec.methodBuilder("between") .addModifiers(Modifier.PUBLIC) .returns(TypeVariableName.get("Q1")) .addParameter(TypeVariableName.get("M1"), "min") .addParameter(TypeVariableName.get("M1"), "max") .addStatement("queryBuilder.appendQuery(column+\" > ?\",String.valueOf(min))") .addStatement("queryBuilder.appendAnd()") .addStatement("queryBuilder.appendQuery(column+\" < ?\",String.valueOf(max))") .addStatement("return queryBuilder") .build()) .addMethod(MethodSpec.methodBuilder("in") .addModifiers(Modifier.PUBLIC) .returns(TypeVariableName.get("Q1")) .varargs() .addParameter(ArrayTypeName.of(TypeVariableName.get("M1")), "values") .addStatement("$T sb = new $T()", Constants.stringBuilderClassName, Constants.stringBuilderClassName) .addStatement("sb.append($S)", " in (") .beginControlFlow("for(int i=0;i<values.length;++i)") .addStatement("sb.append(values[i])") .addStatement("if(i != values.length-1) sb.append($S)", ",") .endControlFlow() .addStatement("sb.append($S)", ")") .addStatement("queryBuilder.appendQuery(column+sb.toString(), null)") .addStatement("return queryBuilder") .build()) .addMethod(MethodSpec.methodBuilder("greatherThan") .addModifiers(Modifier.PUBLIC) .returns(TypeVariableName.get("Q1")) .addParameter(TypeVariableName.get("M1"), "value") .addStatement("queryBuilder.appendQuery(column+\" > ?\",String.valueOf(value))") .addStatement("return queryBuilder") .build()) .addMethod(MethodSpec.methodBuilder("lessThan") .addModifiers(Modifier.PUBLIC) .returns(TypeVariableName.get("Q1")) .addParameter(TypeVariableName.get("M1"), "value") .addStatement("queryBuilder.appendQuery(column+\" < ?\",String.valueOf(value))") .addStatement("return queryBuilder") .build()) .build()); typeSpecs.add(TypeSpec.classBuilder(Constants.SELECTOR_NUMBER_LIST) .addModifiers(Modifier.PUBLIC, Modifier.STATIC) .addTypeVariable(TypeVariableName.get("P1", Constants.queryBuilderClassName)) .addTypeVariable(TypeVariableName.get("N1", TypeName.get(Number.class))) .addField(TypeVariableName.get("P1"), "queryBuilder", Modifier.PROTECTED) .addField(TypeName.get(String.class), "column", Modifier.PROTECTED) .addMethod(MethodSpec.constructorBuilder() .addModifiers(Modifier.PUBLIC) .addParameter(TypeVariableName.get("P1"), "queryBuilder") .addParameter(TypeName.get(String.class), "column") .addStatement("this.queryBuilder = queryBuilder") .addStatement("this.column = column") .build()) .addMethod(MethodSpec.methodBuilder("contains") .addModifiers(Modifier.PUBLIC) .returns(ParameterizedTypeName.get(Constants.queryBuilder_NumberSelectorClassName, TypeVariableName.get("P1"), TypeVariableName.get("N1"))) .addStatement("return new $L<$L,$L>(queryBuilder,column)", Constants.SELECTOR_NUMBER, "P1", "N1") .build()) .build()); typeSpecs.add(TypeSpec.classBuilder(Constants.SELECTOR_STRING) .addModifiers(Modifier.PUBLIC, Modifier.STATIC) .addTypeVariable(TypeVariableName.get("Q2", Constants.queryBuilderClassName)) .addField(TypeVariableName.get("Q2"), "queryBuilder", Modifier.PROTECTED) .addField(TypeName.get(String.class), "column", Modifier.PROTECTED) .addMethod(MethodSpec.constructorBuilder() .addModifiers(Modifier.PUBLIC) .addParameter(TypeVariableName.get("Q2"), "queryBuilder") .addParameter(TypeName.get(String.class), "column") .addStatement("this.queryBuilder = queryBuilder") .addStatement("this.column = column") .build()) .addMethod(MethodSpec.methodBuilder("equalsTo") .addModifiers(Modifier.PUBLIC) .returns(TypeVariableName.get("Q2")) .addParameter(TypeName.get(String.class), "value") .addStatement("queryBuilder.appendQuery(column+\" = ?\",value)") .addStatement("return queryBuilder") .build()) .addMethod(MethodSpec.methodBuilder("notEqualsTo") .addModifiers(Modifier.PUBLIC) .returns(TypeVariableName.get("Q2")) .addParameter(TypeName.get(String.class), "value") .addStatement("queryBuilder.appendQuery(column+\" != ?\",value)") .addStatement("return queryBuilder") .build()) .addMethod(MethodSpec.methodBuilder("contains") .addModifiers(Modifier.PUBLIC) .returns(TypeVariableName.get("Q2")) .addParameter(TypeName.get(String.class), "value") .addStatement("queryBuilder.appendQuery(column+\" LIKE '%\"+value+\"%'\",null)") .addStatement("return queryBuilder") .build()) .addMethod(MethodSpec.methodBuilder("like") .addModifiers(Modifier.PUBLIC) .returns(TypeVariableName.get("Q2")) .addParameter(TypeName.get(String.class), "value") .addStatement("queryBuilder.appendQuery(column+\" LIKE '\"+value+\"'\",null)") .addStatement("return queryBuilder") .build()) .addMethod(MethodSpec.methodBuilder("in") .addModifiers(Modifier.PUBLIC) .returns(TypeVariableName.get("Q2")) .varargs() .addParameter(ArrayTypeName.of(TypeName.get(String.class)), "values") .addStatement("$T sb = new $T()", Constants.stringBuilderClassName, Constants.stringBuilderClassName) .addStatement("sb.append($S)", " in (") .beginControlFlow("for(int i=0;i<values.length;++i)") .addStatement("sb.append($S).append(values[i]).append($S)", "'", "'") .addStatement("if(i != values.length-1) sb.append($S)", ",") .endControlFlow() .addStatement("sb.append($S)", ")") .addStatement("queryBuilder.appendQuery(column+sb.toString(), null)") .addStatement("return queryBuilder") .build()) .build()); typeSpecs.add(TypeSpec.classBuilder(Constants.SELECTOR_STRING_LIST) .addModifiers(Modifier.PUBLIC, Modifier.STATIC) .addTypeVariable(TypeVariableName.get("P2", Constants.queryBuilderClassName)) .addField(TypeVariableName.get("P2"), "queryBuilder", Modifier.PROTECTED) .addField(TypeName.get(String.class), "column", Modifier.PROTECTED) .addMethod(MethodSpec.constructorBuilder() .addModifiers(Modifier.PUBLIC) .addParameter(TypeVariableName.get("P2"), "queryBuilder") .addParameter(TypeName.get(String.class), "column") .addStatement("this.queryBuilder = queryBuilder") .addStatement("this.column = column") .build()) .addMethod(MethodSpec.methodBuilder("contains") .addModifiers(Modifier.PUBLIC) .returns(ParameterizedTypeName.get(Constants.queryBuilder_StringSelectorClassName, TypeVariableName.get("P2"))) .addStatement("return new $L<$L>(queryBuilder,column)", Constants.SELECTOR_STRING, "P2") .build()) .build()); typeSpecs.add(TypeSpec.classBuilder(Constants.SELECTOR_BOOLEAN) .addModifiers(Modifier.PUBLIC, Modifier.STATIC) .addTypeVariable(TypeVariableName.get("Q3", Constants.queryBuilderClassName)) .addField(TypeVariableName.get("Q3"), "queryBuilder", Modifier.PROTECTED) .addField(TypeName.get(String.class), "column", Modifier.PROTECTED) .addMethod(MethodSpec.constructorBuilder() .addModifiers(Modifier.PUBLIC) .addParameter(TypeVariableName.get("Q3"), "queryBuilder") .addParameter(TypeName.get(String.class), "column") .addStatement("this.queryBuilder = queryBuilder") .addStatement("this.column = column") .build()) .addMethod(MethodSpec.methodBuilder("equalsTo") .addModifiers(Modifier.PUBLIC) .returns(TypeVariableName.get("Q3")) .addParameter(TypeName.BOOLEAN, "value") .addStatement("queryBuilder.appendQuery(column+\" = ?\", String.valueOf(value ? 1 : 0))") .addStatement("return queryBuilder") .build()) .addMethod(MethodSpec.methodBuilder("isTrue") .addModifiers(Modifier.PUBLIC) .returns(TypeVariableName.get("Q3")) .addStatement("queryBuilder.appendQuery(column+\" = ?\", String.valueOf(1))") .addStatement("return queryBuilder") .build()) .addMethod(MethodSpec.methodBuilder("isFalse") .addModifiers(Modifier.PUBLIC) .returns(TypeVariableName.get("Q3")) .addStatement("queryBuilder.appendQuery(column+\" = ?\", String.valueOf(0))") .addStatement("return queryBuilder") .build()) .build()); typeSpecs.add(TypeSpec.classBuilder(Constants.SELECTOR_BOOLEAN_LIST) .addModifiers(Modifier.PUBLIC, Modifier.STATIC) .addTypeVariable(TypeVariableName.get("P3", Constants.queryBuilderClassName)) .addField(TypeVariableName.get("P3"), "queryBuilder", Modifier.PROTECTED) .addField(TypeName.get(String.class), "column", Modifier.PROTECTED) .addMethod(MethodSpec.constructorBuilder() .addModifiers(Modifier.PUBLIC) .addParameter(TypeVariableName.get("P3"), "queryBuilder") .addParameter(TypeName.get(String.class), "column") .addStatement("this.queryBuilder = queryBuilder") .addStatement("this.column = column") .build()) .addMethod(MethodSpec.methodBuilder("contains") .addModifiers(Modifier.PUBLIC) .returns(ParameterizedTypeName.get(Constants.queryBuilder_BooleanSelectorClassName, TypeVariableName.get("P3"))) .addStatement("return new $L<$L>(queryBuilder,column)", Constants.SELECTOR_BOOLEAN, "P3") .build()) .build()); typeSpecs.add(TypeSpec.classBuilder(Constants.SELECTOR_DATE) .addModifiers(Modifier.PUBLIC, Modifier.STATIC) .addTypeVariable(TypeVariableName.get("Q3", Constants.queryBuilderClassName)) .addField(TypeVariableName.get("Q3"), "queryBuilder", Modifier.PROTECTED) .addField(TypeName.get(String.class), "column", Modifier.PROTECTED) .addMethod(MethodSpec.constructorBuilder() .addModifiers(Modifier.PUBLIC) .addParameter(TypeVariableName.get("Q3"), "queryBuilder") .addParameter(TypeName.get(String.class), "column") .addStatement("this.queryBuilder = queryBuilder") .addStatement("this.column = column") .build()) .addMethod(MethodSpec.methodBuilder("equalsTo") .addModifiers(Modifier.PUBLIC) .returns(TypeVariableName.get("Q3")) .addParameter(Constants.dateClassName, "date") .addStatement("queryBuilder.appendQuery(column+\" = Datetime(?)\", new $T($S).format(date))", Constants.simpleDateFormatClassName, Constants.DATE_FORMAT) .addStatement("return queryBuilder") .build()) .addMethod(MethodSpec.methodBuilder("notEqualsTo") .addModifiers(Modifier.PUBLIC) .returns(TypeVariableName.get("Q3")) .addParameter(Constants.dateClassName, "date") .addStatement("queryBuilder.appendQuery(column+\" != Datetime(?)\", new $T($S).format(date))", Constants.simpleDateFormatClassName, Constants.DATE_FORMAT) .addStatement("return queryBuilder") .build()) .addMethod(MethodSpec.methodBuilder("before") .addModifiers(Modifier.PUBLIC) .returns(TypeVariableName.get("Q3")) .addParameter(Constants.dateClassName, "date") .addStatement("queryBuilder.appendQuery(column+\" < Datetime(?)\", new $T($S).format(date))", Constants.simpleDateFormatClassName, Constants.DATE_FORMAT) .addStatement("return queryBuilder") .build()) .addMethod(MethodSpec.methodBuilder("after") .addModifiers(Modifier.PUBLIC) .returns(TypeVariableName.get("Q3")) .addParameter(Constants.dateClassName, "date") .addStatement("queryBuilder.appendQuery(column+\" > Datetime(?)\", new $T($S).format(date))", Constants.simpleDateFormatClassName, Constants.DATE_FORMAT) .addStatement("return queryBuilder") .build()) .addMethod(MethodSpec.methodBuilder("between") .addModifiers(Modifier.PUBLIC) .returns(TypeVariableName.get("Q3")) .addParameter(Constants.dateClassName, "min") .addParameter(Constants.dateClassName, "max") .addStatement("queryBuilder.appendQuery(column+\" > Datetime(?)\", new $T($S).format(min))", Constants.simpleDateFormatClassName, Constants.DATE_FORMAT) .addStatement("queryBuilder.appendAnd()") .addStatement("queryBuilder.appendQuery(column+\" < Datetime(?)\", new $T($S).format(max))", Constants.simpleDateFormatClassName, Constants.DATE_FORMAT) .addStatement("return queryBuilder") .build()) .build()); return typeSpecs; } }