com.abubusoft.kripton.processor.sqlite.SqlBuilderHelper Maven / Gradle / Ivy
/**
*
*/
package com.abubusoft.kripton.processor.sqlite;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
import javax.lang.model.element.Element;
import javax.lang.model.element.ElementKind;
import javax.lang.model.element.ExecutableElement;
import javax.lang.model.element.Modifier;
import javax.lang.model.element.TypeElement;
import javax.lang.model.util.Elements;
import com.abubusoft.kripton.android.Logger;
import com.abubusoft.kripton.android.annotation.BindSqlDynamicWhere;
import com.abubusoft.kripton.android.sqlite.KriptonContentValues;
import com.abubusoft.kripton.common.CollectionUtils;
import com.abubusoft.kripton.common.One;
import com.abubusoft.kripton.common.Pair;
import com.abubusoft.kripton.common.StringUtils;
import com.abubusoft.kripton.common.Triple;
import com.abubusoft.kripton.exception.KriptonRuntimeException;
import com.abubusoft.kripton.processor.BaseProcessor;
import com.abubusoft.kripton.processor.core.AssertKripton;
import com.abubusoft.kripton.processor.core.ModelMethod;
import com.abubusoft.kripton.processor.core.reflect.AnnotationUtility.MethodFoundListener;
import com.abubusoft.kripton.processor.sqlite.grammars.jql.JQL;
import com.abubusoft.kripton.processor.sqlite.grammars.jql.JQL.JQLDynamicStatementType;
import com.abubusoft.kripton.processor.sqlite.grammars.jql.JQL.JQLType;
import com.abubusoft.kripton.processor.sqlite.grammars.jql.JQLChecker;
import com.abubusoft.kripton.processor.sqlite.grammars.jql.JQLKeywords;
import com.abubusoft.kripton.processor.sqlite.grammars.jql.JQLPlaceHolder;
import com.abubusoft.kripton.processor.sqlite.grammars.jql.JQLPlaceHolder.JQLPlaceHolderType;
import com.abubusoft.kripton.processor.sqlite.grammars.jql.JQLReplaceVariableStatementListenerImpl;
import com.abubusoft.kripton.processor.sqlite.grammars.jql.JQLReplacerListenerImpl;
import com.abubusoft.kripton.processor.sqlite.grammars.jsql.JqlParser.Column_name_setContext;
import com.abubusoft.kripton.processor.sqlite.grammars.jsql.JqlParser.Where_stmtContext;
import com.abubusoft.kripton.processor.sqlite.grammars.uri.ContentUriPlaceHolder;
import com.abubusoft.kripton.processor.sqlite.model.SQLiteDaoDefinition;
import com.abubusoft.kripton.processor.sqlite.model.SQLProperty;
import com.abubusoft.kripton.processor.sqlite.model.SQLiteModelMethod;
import com.squareup.javapoet.FieldSpec;
import com.squareup.javapoet.MethodSpec;
import com.squareup.javapoet.ParameterizedTypeName;
import com.squareup.javapoet.TypeName;
import com.squareup.javapoet.TypeSpec;
// TODO: Auto-generated Javadoc
/**
*
* Utility class for build methods
*
* .
*
* @author Francesco Benincasa ([email protected])
*/
public abstract class SqlBuilderHelper {
/**
* check used columns.
*
* @param builder
* the builder
* @param method
* the method
* @param columnNames
* the column names
* @return name of column name set
*/
public static String generateColumnCheckSet(TypeSpec.Builder builder, SQLiteModelMethod method, Set columnNames) {
String columnNameSet = method.contentProviderMethodName + "ColumnSet";
StringBuilder initBuilder = new StringBuilder();
String temp = "";
for (String item : columnNames) {
initBuilder.append(temp + "\"" + item + "\"");
temp = ", ";
}
FieldSpec.Builder fieldBuilder = FieldSpec.builder(ParameterizedTypeName.get(Set.class, String.class), columnNameSet, Modifier.STATIC, Modifier.PRIVATE, Modifier.FINAL);
fieldBuilder.initializer("$T.asSet($T.class, $L)", CollectionUtils.class, String.class, initBuilder.toString());
builder.addField(fieldBuilder.build());
return columnNameSet;
}
/**
*
* Generate column check
*
* .
*
* @param methodBuilder
* the method builder
* @param method
* the method
* @param columnSetString
* the column set string
* @param generateColumnNameCheck
* the generate column name check
* @param listener
* the listener
*/
static void forEachColumnInContentValue(MethodSpec.Builder methodBuilder, final SQLiteModelMethod method, String columnSetString, boolean generateColumnNameCheck, OnColumnListener listener) {
SQLiteDaoDefinition daoDefinition = method.getParent();
methodBuilder.beginControlFlow("for (String columnName:$L)", columnSetString);
if (generateColumnNameCheck) {
methodBuilder.beginControlFlow("if (!$L.contains(columnName))", method.contentProviderMethodName + "ColumnSet");
methodBuilder.addStatement("throw new $T(String.format(\"For URI '$L', column '%s' does not exists in table '%s' or can not be defined in this $L operation\", columnName, $S ))",
KriptonRuntimeException.class, method.contentProviderUriTemplate, method.jql.operationType, daoDefinition.getEntity().getTableName());
methodBuilder.endControlFlow();
}
if (listener != null)
listener.onColumnCheck(methodBuilder, "columnName");
methodBuilder.endControlFlow();
}
/**
* Removes the dynamic place holder.
*
* @param placeHolders
* the place holders
* @return the list
*/
static List removeDynamicPlaceHolder(List placeHolders) {
List result = new ArrayList<>();
for (JQLPlaceHolder item : placeHolders) {
if (item.type != JQLPlaceHolderType.DYNAMIC_SQL) {
result.add(item);
}
}
return result;
}
/**
* look for variable name in place holders defined through path of content
* provider.
*
* @param value
* the value
* @param placeHolders
* the place holders
* @param pos
* the pos
* @return true
if we found it path
*/
static boolean validate(String value, List placeHolders, int pos) {
return placeHolders.get(pos).value.equals(value);
}
/**
* Generate java doc for content provider.
*
* @param method
* the method
* @param methodBuilder
* the method builder
*/
public static void generateJavaDocForContentProvider(final SQLiteModelMethod method, MethodSpec.Builder methodBuilder) {
// javadoc
String operation = method.jql.operationType.toString();
methodBuilder.addJavadoc("Content provider URI ($L operation):
\n", operation);
methodBuilder.addJavadoc("$L
\n\n", method.contentProviderUriTemplate.replace("*", "[*]"));
methodBuilder.addJavadoc("JQL $L for Content Provider
\n", operation);
methodBuilder.addJavadoc("$L
\n\n", method.jql.value);
methodBuilder.addJavadoc("SQL $L for Content Provider
\n", operation);
String sql = JQLChecker.getInstance().replace(method, method.jql, new com.abubusoft.kripton.processor.sqlite.grammars.jql.JQLReplacerListenerImpl(method) {
@Override
public String onColumnName(String columnName) {
SQLProperty tempProperty = currentEntity.get(columnName);
AssertKripton.assertTrueOrUnknownPropertyInJQLException(tempProperty != null, method, columnName);
return tempProperty.columnName;
}
});
methodBuilder.addJavadoc("$L
\n\n", sql);
if (method.contentProviderUriVariables.size() > 0) {
methodBuilder.addJavadoc("Path variables defined:
\n\n");
for (ContentUriPlaceHolder variable : method.contentProviderUriVariables) {
methodBuilder.addJavadoc("- $${$L} at path segment $L
\n", variable.value, variable.pathSegmentIndex);
}
methodBuilder.addJavadoc("
\n\n");
}
if (!method.hasDynamicWhereConditions()) {
methodBuilder.addJavadoc("Dynamic where statement is ignored, due no param with @$L was added.
\n\n", BindSqlDynamicWhere.class.getSimpleName());
}
methodBuilder.addJavadoc("In URI, * is replaced with [*] for javadoc rapresentation
\n\n");
}
/**
*
* Generate log for content values
*
*
* pre conditions
*
* required variable are:
*
*
* - contentValues
*
*
*
* @param method
* the method
* @param methodBuilder
* the method builder
*/
public static void generateLogForContentValues(SQLiteModelMethod method, MethodSpec.Builder methodBuilder) {
methodBuilder.addCode("\n// log for content values -- BEGIN\n");
methodBuilder.addStatement("$T _contentValue", Triple.class, KriptonContentValues.ParamType.class);
methodBuilder.beginControlFlow("for (int i = 0; i < _contentValues.size(); i++)");
methodBuilder.addStatement("_contentValue = _contentValues.get(i)");
methodBuilder.beginControlFlow("if (_contentValue.value1==null)");
methodBuilder.addStatement("$T.info(\"==> :%s = \", _contentValue.value0)", Logger.class);
methodBuilder.nextControlFlow("else");
methodBuilder.addStatement("$T.info(\"==> :%s = '%s' (%s)\", _contentValue.value0, $T.checkSize(_contentValue.value1), _contentValue.value1.getClass().getCanonicalName())", Logger.class,
StringUtils.class);
methodBuilder.endControlFlow();
methodBuilder.endControlFlow();
methodBuilder.addCode("// log for content values -- END\n");
}
/**
*
* Generate log for where conditions.
*
*
* pre conditions
*
* required variable are:
*
*
* - _sqlWhereParams
*
*
* post conditions
*
* created variables are:
*
* - _whereParamCounter
*
*
* @param method
* the method
* @param methodBuilder
* the method builder
*/
public static void generateLogForWhereParameters(SQLiteModelMethod method, MethodSpec.Builder methodBuilder) {
// manage log for where parameters
if (method.getParent().getParent().generateLog) {
methodBuilder.addCode("\n// log for where parameters -- BEGIN\n");
methodBuilder.addStatement("int _whereParamCounter=0");
// methodBuilder.beginControlFlow("for (String _whereParamItem:
// _sqlWhereParams)");
methodBuilder.beginControlFlow("for (String _whereParamItem: _contentValues.whereArgs())");
methodBuilder.addStatement("$T.info(\"==> param%s: '%s'\",(_whereParamCounter++), $T.checkSize(_whereParamItem))", Logger.class, StringUtils.class);
methodBuilder.endControlFlow();
methodBuilder.addCode("// log for where parameters -- END\n");
}
}
/**
*
* Generate log for where conditions.
*
*
* pre conditions
*
* required variable are:
*
*
* - _sqlBuilder
* - _projectionBuffer
*
*
*
* post conditions
*
* created variables are:
*
* - _sql
*
*
* @param method
* the method
* @param methodBuilder
* the method builder
*/
static void generateLogForSQL(SQLiteModelMethod method, MethodSpec.Builder methodBuilder) {
// manage log
if (method.getParent().getParent().generateLog) {
methodBuilder.addCode("\n// manage log\n");
methodBuilder.addStatement("$T.info(_sql)", Logger.class);
}
}
/**
*
* Generate log info at beginning of method
*
* .
*
* @param method
* the method
* @param methodBuilder
* the method builder
*/
static void generateLogForContentProviderBeginning(SQLiteModelMethod method, MethodSpec.Builder methodBuilder) {
if (method.getParent().isLogEnabled()) {
methodBuilder.addStatement("$T.info($S, uri.toString())", Logger.class, "Execute " + method.jql.operationType + " for URI %s");
}
}
/**
* Iterate over methods.
*
* @param typeElement
* the type element
* @param listener
* the listener
*/
public static void forEachMethods(TypeElement typeElement, MethodFoundListener listener) {
Elements elementUtils = BaseProcessor.elementUtils;
List extends Element> list = elementUtils.getAllMembers(typeElement);
for (Element item : list) {
if (item.getKind() == ElementKind.METHOD) {
listener.onMethod((ExecutableElement) item);
}
}
}
/**
* Checks for parameter of type.
*
* @param method
* the method
* @param parameter
* the parameter
* @return true, if successful
*/
public static boolean hasParameterOfType(ModelMethod method, TypeName parameter) {
return SqlBuilderHelper.countParameterOfType(method, parameter) > 0;
}
/**
* Count parameter of type.
*
* @param method
* the method
* @param parameter
* the parameter
* @return the int
*/
public static int countParameterOfType(ModelMethod method, TypeName parameter) {
int counter = 0;
for (Pair item : method.getParameters()) {
if (item.value1.equals(parameter)) {
counter++;
}
}
return counter;
}
/**
*
* Generate where management code.
*
*
* pre condition
*
* - _sqlDynamicWhere
* - dynamic where
*
*
* post conditions
*
* - _sqlWhereParams
* - ArraList
* - _sqlWhereStatement
* - String
*
*
* @param methodBuilder
* the method builder
* @param method
* the method
* @param sqlWhereParamsAlreadyDefined
* the sql where params already defined
*/
public static void generateWhereCondition(MethodSpec.Builder methodBuilder, final SQLiteModelMethod method, boolean sqlWhereParamsAlreadyDefined) {
final JQL jql = method.jql;
final JQLChecker jqlChecker = JQLChecker.getInstance();
if (jql.isWhereConditions()) {
// parameters extracted from query
final One whereStatement = new One<>();
final One alreadyFoundWhereStatement = new One<>(false);
// put in whereStatement value of where statement.
jqlChecker.replaceVariableStatements(method, method.jql.value, new JQLReplaceVariableStatementListenerImpl() {
@Override
public String onWhere(String statement) {
if (alreadyFoundWhereStatement.value0 == false) {
whereStatement.value0 = statement;
alreadyFoundWhereStatement.value0 = true;
return "";
} else {
// DO NOTHING
return null;
}
}
});
methodBuilder.addCode("\n// manage WHERE arguments -- BEGIN\n");
String sqlWhere = jqlChecker.replaceFromVariableStatement(method, whereStatement.value0, new JQLReplacerListenerImpl(method) {
@Override
public String onColumnName(String columnName) {
SQLProperty tempProperty = currentEntity.get(columnName);
AssertKripton.assertTrueOrUnknownPropertyInJQLException(tempProperty != null, method, columnName);
return tempProperty.columnName;
}
@Override
public String onDynamicSQL(JQLDynamicStatementType dynamicStatement) {
method.jql.dynamicReplace.put(dynamicStatement, "#{"+dynamicStatement+"}");
return "";
}
@Override
public String onBindParameter(String bindParameterName) {
return "?";
}
});
methodBuilder.addCode("\n// manage WHERE statement\n");
String value = sqlWhere;
String valueToReplace = jql.dynamicReplace.get(JQLDynamicStatementType.DYNAMIC_WHERE);
if (method.jql.operationType == JQLType.SELECT) {
// we have to include WHERE keywords
if (jql.isStaticWhereConditions() && !jql.isDynamicWhereConditions()) {
// case static statement and NO dynamic
methodBuilder.addStatement("String _sqlWhereStatement=$S", value);
} else if (jql.isStaticWhereConditions() && jql.isDynamicWhereConditions()) {
methodBuilder.addStatement("String _sqlWhereStatement=$S+$T.ifNotEmptyAppend($L,\" $L \")", value.replace(valueToReplace, ""), StringUtils.class, "_sqlDynamicWhere",
method.dynamicWherePrepend);
} else if (!jql.isStaticWhereConditions() && jql.isDynamicWhereConditions()) {
methodBuilder.addStatement("String _sqlWhereStatement=$T.ifNotEmptyAppend($L, \" $L \")", StringUtils.class, "_sqlDynamicWhere", JQLKeywords.WHERE_KEYWORD);
}
} else {
// we DON'T have to include WHERE keywords
value = value.replace(" " + JQLKeywords.WHERE_KEYWORD, "");
if (jql.isStaticWhereConditions() && !jql.isDynamicWhereConditions()) {
// case static statement and NO dynamic
methodBuilder.addStatement("String _sqlWhereStatement=$S", value);
} else if (jql.isStaticWhereConditions() && jql.isDynamicWhereConditions()) {
methodBuilder.addStatement("String _sqlWhereStatement=$S+$T.ifNotEmptyAppend($L,\" $L \")", value.replace(valueToReplace, ""), StringUtils.class, "_sqlDynamicWhere",
method.dynamicWherePrepend);
} else if (!jql.isStaticWhereConditions() && jql.isDynamicWhereConditions()) {
methodBuilder.addStatement("String _sqlWhereStatement=$T.ifNotEmptyAppend($L, \" \")", StringUtils.class, "_sqlDynamicWhere");
}
}
methodBuilder.addStatement("_sqlBuilder.append($L)", "_sqlWhereStatement");
methodBuilder.addCode("\n// manage WHERE arguments -- END\n");
} else {
// in every situation we need it
methodBuilder.addStatement("String _sqlWhereStatement=\"\"");
}
// manage where arguments
if (method.hasDynamicWhereConditions() && method.hasDynamicWhereArgs()) {
// ASSERT: only with dynamic where conditions
methodBuilder.beginControlFlow("if ($T.hasText(_sqlDynamicWhere) && _sqlDynamicWhereArgs!=null)", StringUtils.class);
if (method.hasDynamicWhereConditions()) {
methodBuilder.beginControlFlow("for (String _arg: _sqlDynamicWhereArgs)");
// methodBuilder.addStatement("_sqlWhereParams.add(_arg)");
methodBuilder.addStatement("_contentValues.addWhereArgs(_arg)");
methodBuilder.endControlFlow();
}
methodBuilder.endControlFlow();
}
}
/**
*
* Generate log for INSERT operations
*
* .
*
* @param method
* the method
* @param methodBuilder
* the method builder
*/
public static void generateLog(final SQLiteModelMethod method, MethodSpec.Builder methodBuilder) {
SQLiteDaoDefinition daoDefinition = method.getParent();
// log is enabled
if (daoDefinition.isLogEnabled()) {
// generate log section - BEGIN
methodBuilder.addComment("log section BEGIN");
methodBuilder.beginControlFlow("if (_context.isLogEnabled())");
methodBuilder.addCode("// log for insert -- BEGIN \n");
JQLChecker checker = JQLChecker.getInstance();
final One inWhere = new One(false);
String sql = checker.replace(method, method.jql, new JQLReplacerListenerImpl(method) {
@Override
public String onBindParameter(String bindParameterName) {
if (inWhere.value0) {
return "?";
}
return null;
}
@Override
public void onWhereStatementBegin(Where_stmtContext ctx) {
super.onWhereStatementBegin(ctx);
inWhere.value0 = true;
}
@Override
public void onWhereStatementEnd(Where_stmtContext ctx) {
super.onWhereStatementEnd(ctx);
inWhere.value0 = false;
}
});
if (method.jql.containsSelectOperation) {
// log
// manage log
methodBuilder.addCode("\n");
methodBuilder.addStatement("$T.info($S)", Logger.class, sql);
} else {
sql = checker.replaceVariableStatements(method, sql, new JQLReplaceVariableStatementListenerImpl() {
@Override
public String onColumnNameSet(String statement) {
return "%s";
}
@Override
public String onColumnValueSet(String statement) {
return "%s";
}
});
methodBuilder.addStatement("$T _columnNameBuffer=new $T()", StringBuffer.class, StringBuffer.class);
methodBuilder.addStatement("$T _columnValueBuffer=new $T()", StringBuffer.class, StringBuffer.class);
methodBuilder.addStatement("String _columnSeparator=$S", "");
SqlBuilderHelper.forEachColumnInContentValue(methodBuilder, method, "_contentValues.keys()", false, new OnColumnListener() {
@Override
public void onColumnCheck(MethodSpec.Builder methodBuilder, String columNameVariable) {
methodBuilder.addStatement("_columnNameBuffer.append(_columnSeparator+$L)", columNameVariable);
methodBuilder.addStatement("_columnValueBuffer.append(_columnSeparator+$S+$L)", ":", columNameVariable);
methodBuilder.addStatement("_columnSeparator=$S", ", ");
}
});
methodBuilder.addStatement("$T.info($S, _columnNameBuffer.toString(), _columnValueBuffer.toString())", Logger.class, sql);
}
generateLogForContentValues(method, methodBuilder);
methodBuilder.addCode("// log for insert -- END \n\n");
SqlBuilderHelper.generateLogForWhereParameters(method, methodBuilder);
// generate log section - END
methodBuilder.endControlFlow();
methodBuilder.addComment("log section END");
}
}
/**
*
* Generate log for INSERT operations
*
* .
*
* @param method
* the method
* @param methodBuilder
* the method builder
*/
public static void generateSQLForInsertDynamic(final SQLiteModelMethod method, MethodSpec.Builder methodBuilder) {
methodBuilder.addComment("generate SQL for insert");
JQLChecker checker = JQLChecker.getInstance();
// replace the table name, other pieces will be removed
String sql = checker.replace(method, method.jql, new JQLReplacerListenerImpl(method) {
@Override
public String onBindParameter(String bindParameterName) {
return "?";
}
});
final One counter = new One(0);
sql = checker.replaceVariableStatements(method, sql, new JQLReplaceVariableStatementListenerImpl() {
@Override
public String onColumnNameSet(String statement) {
counter.value0++;
return "%s";
}
@Override
public String onColumnValueSet(String statement) {
counter.value0++;
return "%s";
}
});
if (counter.value0 == 2) {
methodBuilder.addStatement("String _sql=String.format($S, _contentValues.keyList(), _contentValues.keyValueList())", sql);
} else {
methodBuilder.addStatement("String _sql=String.format($S, _contentValues.keyList())", sql);
}
}
/**
*
* Generate log for INSERT operations
*
* .
*
* @param method
* the method
* @param methodBuilder
* the method builder
*/
public static void generateSQLForStaticQuery(final SQLiteModelMethod method, MethodSpec.Builder methodBuilder) {
methodBuilder.addComment("generate static SQL for statement");
JQLChecker checker = JQLChecker.getInstance();
// replace the table name, other pieces will be removed
String sql = checker.replace(method, method.jql, new JQLReplacerListenerImpl(method) {
@Override
public String onColumnNameToUpdate(String columnName) {
return onColumnName(columnName);
}
@Override
public String onColumnName(String columnName) {
SQLProperty tempProperty = method.getParent().getEntity().get(columnName);
AssertKripton.assertTrueOrUnknownPropertyInJQLException(tempProperty != null, method, columnName);
return tempProperty.columnName;
}
@Override
public String onBindParameter(String bindParameterName) {
return "?";
}
});
methodBuilder.addStatement("String _sql=$S", sql);
}
/**
* Generate log for content values content provider.
*
* @param method
* the method
* @param methodBuilder
* the method builder
*/
public static void generateLogForContentValuesContentProvider(SQLiteModelMethod method, MethodSpec.Builder methodBuilder) {
methodBuilder.addCode("\n// log for content values -- BEGIN\n");
methodBuilder.addStatement("Object _contentValue");
methodBuilder.beginControlFlow("for (String _contentKey:_contentValues.values().keySet())");
methodBuilder.addStatement("_contentValue=_contentValues.values().get(_contentKey)");
methodBuilder.beginControlFlow("if (_contentValue==null)");
methodBuilder.addStatement("$T.info(\"==> :%s = \", _contentKey)", Logger.class);
methodBuilder.nextControlFlow("else");
methodBuilder.addStatement("$T.info(\"==> :%s = '%s' (%s)\", _contentKey, $T.checkSize(_contentValue), _contentValue.getClass().getCanonicalName())", Logger.class, StringUtils.class);
methodBuilder.endControlFlow();
methodBuilder.endControlFlow();
methodBuilder.addCode("// log for content values -- END\n");
}
/**
* Order content values.
*
* @param method
* the method
* @param updateableParams
* the updateable params
* @return the list
*/
public static List> orderContentValues(final SQLiteModelMethod method, final List> updateableParams) {
final List> result = new ArrayList>();
JQLChecker checker = JQLChecker.getInstance();
final One inserMode = new One(false);
checker.replace(method, method.jql, new JQLReplacerListenerImpl(method) {
// used in update
@Override
public String onColumnNameToUpdate(String columnName) {
String column = currentEntity.findPropertyByName(columnName).columnName;
for (Pair item : updateableParams) {
String paramNameInQuery = method.findParameterAliasByName(item.value0);
if (paramNameInQuery.equalsIgnoreCase(columnName)) {
result.add(item);
break;
}
}
return column;
}
// used in insert
@Override
public void onColumnNameSetBegin(Column_name_setContext ctx) {
inserMode.value0 = true;
}
@Override
public void onColumnNameSetEnd(Column_name_setContext ctx) {
inserMode.value0 = false;
}
@Override
public String onColumnName(String columnName) {
if (!inserMode.value0)
return columnName;
String column = currentEntity.findPropertyByName(columnName).columnName;
for (Pair item : updateableParams) {
String paramNameInQuery = method.findParameterAliasByName(item.value0);
if (paramNameInQuery.equalsIgnoreCase(columnName)) {
result.add(item);
break;
}
}
return column;
}
});
return result;
}
}