All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.abubusoft.kripton.processor.sqlite.SqlInsertBuilder Maven / Gradle / Ivy

There is a newer version: 8.2.0-rc.4
Show newest version
/*******************************************************************************
 * Copyright 2015, 2017 Francesco Benincasa ([email protected]).
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *******************************************************************************/
package com.abubusoft.kripton.processor.sqlite;

import static com.abubusoft.kripton.processor.core.reflect.TypeUtility.typeName;

import java.util.LinkedHashSet;
import java.util.Set;

import javax.lang.model.element.Modifier;

import com.abubusoft.kripton.android.annotation.BindSqlInsert;
import com.abubusoft.kripton.android.sqlite.ConflictAlgorithmType;
import com.abubusoft.kripton.android.sqlite.KriptonContentValues;
import com.abubusoft.kripton.common.Pair;
import com.abubusoft.kripton.exception.KriptonRuntimeException;
import com.abubusoft.kripton.processor.core.AnnotationAttributeType;
import com.abubusoft.kripton.processor.core.AssertKripton;
import com.abubusoft.kripton.processor.core.ModelAnnotation;
import com.abubusoft.kripton.processor.core.reflect.AnnotationUtility;
import com.abubusoft.kripton.processor.core.reflect.TypeUtility;
import com.abubusoft.kripton.processor.exceptions.InvalidMethodSignException;
import com.abubusoft.kripton.processor.sqlite.GenericSQLHelper.SubjectType;
import com.abubusoft.kripton.processor.sqlite.grammars.jql.JQLChecker;
import com.abubusoft.kripton.processor.sqlite.grammars.jql.JQLReplacerListenerImpl;
import com.abubusoft.kripton.processor.sqlite.grammars.uri.ContentUriPlaceHolder;
import com.abubusoft.kripton.processor.sqlite.model.SQLiteDaoDefinition;
import com.abubusoft.kripton.processor.sqlite.model.SQLiteEntity;
import com.abubusoft.kripton.processor.sqlite.model.SQLProperty;
import com.abubusoft.kripton.processor.sqlite.model.SQLiteModelMethod;
import com.squareup.javapoet.MethodSpec;
import com.squareup.javapoet.ParameterSpec;
import com.squareup.javapoet.TypeName;
import com.squareup.javapoet.TypeSpec;

import android.content.ContentValues;
import android.net.Uri;

/**
 * The Class SqlInsertBuilder.
 *
 * @author Francesco Benincasa ([email protected])
 * @since 05/mag/2016
 */
public abstract class SqlInsertBuilder {

	/**
	 * The Enum InsertType.
	 */
	public enum InsertType {
		
		/** The insert bean. */
		INSERT_BEAN(InsertBeanHelper.class, true), 
 /** The insert raw. */
 INSERT_RAW(InsertRawHelper.class, false);

		/** The code generator. */
		private InsertCodeGenerator codeGenerator;

		/** The map fields. */
		private boolean mapFields;

		/**
		 * if true, map cursor fields to bean attributes.
		 * 
		 * @return the mapFields
		 */
		public boolean isMapFields() {
			return mapFields;
		}

		/**
		 * Instantiates a new insert type.
		 *
		 * @param codeGenerator the code generator
		 * @param mapFields the map fields
		 */
		private InsertType(Class codeGenerator, boolean mapFields) {
			try {
				this.mapFields = mapFields;
				this.codeGenerator = codeGenerator.newInstance();
			} catch (InstantiationException | IllegalAccessException e) {
				e.printStackTrace();
				throw new KriptonRuntimeException(e);
			}
		}

		/**
		 * Generate.
		 *
		 * @param classBuilder the class builder
		 * @param methodBuilder the method builder
		 * @param method the method
		 * @param returnType the return type
		 */
		public void generate(TypeSpec.Builder classBuilder, MethodSpec.Builder methodBuilder, SQLiteModelMethod method, TypeName returnType) {
			codeGenerator.generate(classBuilder, methodBuilder, this.isMapFields(), method, returnType);

		}
	}

	/**
	 * The Interface InsertCodeGenerator.
	 */
	public interface InsertCodeGenerator {
		
		/**
		 * Generate.
		 *
		 * @param classBuilder the class builder
		 * @param methodBuilder the method builder
		 * @param mapFields the map fields
		 * @param method the method
		 * @param returnType the return type
		 */
		void generate(TypeSpec.Builder classBuilder, MethodSpec.Builder methodBuilder, boolean mapFields, SQLiteModelMethod method, TypeName returnType);
	}

	/**
	 * Generate.
	 *
	 * @param classBuilder the class builder
	 * @param method the method
	 */
	public static void generate(TypeSpec.Builder classBuilder, SQLiteModelMethod method) {
		InsertType insertResultType = detectInsertType(method);

		// if true, field must be associate to ben attributes
		TypeName returnType = method.getReturnClass();

		AssertKripton.failWithInvalidMethodSignException(insertResultType == null, method);

		// generate method code
		MethodSpec.Builder methodBuilder = MethodSpec.methodBuilder(method.getName()).addAnnotation(Override.class).addModifiers(Modifier.PUBLIC);

		ParameterSpec parameterSpec;
		for (Pair item : method.getParameters()) {
			parameterSpec = ParameterSpec.builder(item.value1, item.value0).build();
			methodBuilder.addParameter(parameterSpec);
		}
		methodBuilder.returns(returnType);

		// fail if we use jql to INSERT_BEAN with operation of INSERT-FOR-SELECT
		// AssertKripton.failWithInvalidMethodSignException(insertResultType ==
		// InsertType.INSERT_BEAN && method.jql.containsSelectOperation, method,
		// "INSERT-FROM-SELECT SQL can not be used with method sign");

		// generate inner code
		methodBuilder.addComment("Specialized Insert - $L - BEGIN", insertResultType.getClass().getSimpleName());
		insertResultType.generate(classBuilder, methodBuilder, method, returnType);
		methodBuilder.addComment("Specialized Insert - $L - END", insertResultType.getClass().getSimpleName());

		MethodSpec methodSpec = methodBuilder.build();
		classBuilder.addMethod(methodSpec);

		if (method.contentProviderEntryPathEnabled) {
			// we need to generate insert for content provider to
			generateInsertForContentProvider(classBuilder, method, insertResultType);
		}

	}

	/**
	 * Detect insert type.
	 *
	 * @param method the method
	 * @return the insert type
	 */
	public static InsertType detectInsertType(SQLiteModelMethod method) {
		SQLiteDaoDefinition daoDefinition = method.getParent();
		SQLiteEntity entity = daoDefinition.getEntity();

		InsertType insertResultType = null;

		// check type of arguments
		int count = 0;
		for (Pair param : method.getParameters()) {
			if (TypeUtility.isEquals(param.value1, typeName(entity.getElement()))) {
				count++;
			}
		}

		AssertKripton.failWithInvalidMethodSignException(method.getParameters().size() == 0, method, " INSERT operations require at least one parameter");

		if (count == 0) {
			// method to insert raw data: no bean is used
			insertResultType = InsertType.INSERT_RAW;

			ModelAnnotation annotation = method.getAnnotation(BindSqlInsert.class);

			// check value attribute
			AssertKripton.failWithInvalidMethodSignException(AnnotationUtility.extractAsStringArray(method, annotation, AnnotationAttributeType.FIELDS).size() > 0, method,
					" can not use attribute %s in this kind of query definition", AnnotationAttributeType.FIELDS.getValue());

			// check excludeFields attribute
			AssertKripton.failWithInvalidMethodSignException(AnnotationUtility.extractAsStringArray(method, annotation, AnnotationAttributeType.EXCLUDED_FIELDS).size() > 0, method,
					" can not use attribute %s in this kind of query definition", AnnotationAttributeType.EXCLUDED_FIELDS.getValue());

			// check if there is only one parameter
			AssertKripton.failWithInvalidMethodSignException(method.getParameters().size() != 1 && TypeUtility.isEquals(method.getParameters().get(0).value1, daoDefinition.getEntityClassName()),
					method);

			// check no
			AssertKripton.failWithInvalidMethodSignException(annotation.getAttributeAsBoolean(AnnotationAttributeType.INCLUDE_PRIMARY_KEY), method, "attribute '%s' can not be used here",
					AnnotationAttributeType.INCLUDE_PRIMARY_KEY.getValue());

		} else if (count == 1) {
			insertResultType = InsertType.INSERT_BEAN;

			AssertKripton.failWithInvalidMethodSignException(method.getParameters().size() > 1, method, " aspected only one parameter of %s type", daoDefinition.getEntityClassName());
		} else {
			throw (new InvalidMethodSignException(method, "only one parameter of type " + typeName(entity.getElement()) + " can be used"));
		}

		return insertResultType;
	}

	/**
	 * 

* Generate insert used in content provider class. *

* * @param classBuilder the class builder * @param method the method * @param insertResultType the insert result type */ private static void generateInsertForContentProvider(TypeSpec.Builder classBuilder, final SQLiteModelMethod method, InsertType insertResultType) { final SQLiteDaoDefinition daoDefinition = method.getParent(); final SQLiteEntity entity = daoDefinition.getEntity(); final Set columns = new LinkedHashSet<>(); MethodSpec.Builder methodBuilder = MethodSpec.methodBuilder(method.contentProviderMethodName); if (!method.getParent().hasSamePackageOfSchema()) {methodBuilder.addModifiers(Modifier.PUBLIC); } ParameterSpec parameterSpec; parameterSpec = ParameterSpec.builder(Uri.class, "uri").build(); methodBuilder.addParameter(parameterSpec); parameterSpec = ParameterSpec.builder(ContentValues.class, "contentValues").build(); methodBuilder.addParameter(parameterSpec); methodBuilder.returns(Long.TYPE); SqlBuilderHelper.generateLogForContentProviderBeginning(method, methodBuilder); // just detect which columns are admitted JQLChecker.getInstance().replace(method, method.jql, new JQLReplacerListenerImpl(method) { @Override public String onColumnName(String columnName) { SQLProperty tempProperty = entity.get(columnName); AssertKripton.assertTrueOrUnknownPropertyInJQLException(tempProperty != null, method, columnName); columns.add(tempProperty.columnName); return tempProperty.columnName; } @Override public String onColumnFullyQualifiedName(String tableName, String columnName) { AssertKripton.fail("Inconsistent state"); return null; } }); // generate columnCheckSet SqlBuilderHelper.generateColumnCheckSet(classBuilder, method, columns); // retrieve content values methodBuilder.addStatement("$T _contentValues=contentValuesForContentProvider(contentValues)", KriptonContentValues.class); // generate column check SqlBuilderHelper.forEachColumnInContentValue(methodBuilder, method, "_contentValues.values().keySet()", true, null); methodBuilder.addCode("\n"); // extract pathVariables // generate get uri variables in content values // every controls was done in constructor of SQLiteModelMethod for (ContentUriPlaceHolder variable : method.contentProviderUriVariables) { SQLProperty entityProperty = entity.get(variable.value); if (entityProperty != null) { methodBuilder.addCode("// Add parameter $L at path segment $L\n", variable.value, variable.pathSegmentIndex); TypeName entityPropertyType = entityProperty.getPropertyType().getTypeName(); if (TypeUtility.isString(entityPropertyType)) { methodBuilder.addStatement("contentValues.put($S, uri.getPathSegments().get($L))", entityProperty.columnName, variable.pathSegmentIndex); } else { methodBuilder.addStatement("contentValues.put($S, Long.valueOf(uri.getPathSegments().get($L)))", entityProperty.columnName, variable.pathSegmentIndex); } } } // generate log for inser operation SqlBuilderHelper.generateLogForContentValuesContentProvider(method, methodBuilder); ConflictAlgorithmType conflictAlgorithmType = InsertBeanHelper.getConflictAlgorithmType(method); String conflictString1 = ""; String conflictString2 = ""; if (conflictAlgorithmType != ConflictAlgorithmType.NONE) { conflictString1 = "WithOnConflict"; conflictString2 = ", " + conflictAlgorithmType.getConflictAlgorithm(); methodBuilder.addCode("// conflict algorithm $L\n", method.jql.conflictAlgorithmType); } methodBuilder.addComment("insert operation"); methodBuilder.addStatement("long result = database().insert$L($S, null, _contentValues.values()$L)", conflictString1, daoDefinition.getEntity().getTableName(), conflictString2); if (method.getParent().getParent().generateRx) { GenericSQLHelper.generateSubjectNext(methodBuilder, SubjectType.INSERT); } // support for livedata if (daoDefinition.hasLiveData()) { methodBuilder.addComment("support for livedata"); methodBuilder.addStatement(BindDaoBuilder.METHOD_NAME_REGISTRY_EVENT + "(result>0?1:0)"); } methodBuilder.addStatement("return result"); // javadoc // we add at last javadoc, because need info is built at last. SqlBuilderHelper.generateJavaDocForContentProvider(method, methodBuilder); methodBuilder.addJavadoc("@param uri $S\n", method.contentProviderUriTemplate.replace("*", "[*]")); methodBuilder.addJavadoc("@param contentValues content values\n"); methodBuilder.addJavadoc("@return new row's id\n"); classBuilder.addMethod(methodBuilder.build()); } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy