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

com.mobgen.halo.android.content.generated.HaloContentDatabaseProcessor Maven / Gradle / Ivy

Go to download

Processor to generate compile time information that allows us to perform queries on sync

The newest version!
package com.mobgen.halo.android.content.generated;

import com.google.auto.service.AutoService;
import com.mobgen.halo.android.content.annotations.HaloConstructor;
import com.mobgen.halo.android.content.annotations.HaloField;
import com.mobgen.halo.android.content.annotations.HaloQueries;
import com.mobgen.halo.android.content.annotations.HaloQuery;
import com.mobgen.halo.android.content.annotations.HaloSearchable;
import com.squareup.javapoet.AnnotationSpec;
import com.squareup.javapoet.ClassName;
import com.squareup.javapoet.FieldSpec;
import com.squareup.javapoet.JavaFile;
import com.squareup.javapoet.MethodSpec;
import com.squareup.javapoet.ParameterizedTypeName;
import com.squareup.javapoet.TypeSpec;

import java.io.File;
import java.io.IOException;
import java.io.Writer;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Set;

import javax.annotation.processing.AbstractProcessor;
import javax.annotation.processing.Filer;
import javax.annotation.processing.Messager;
import javax.annotation.processing.ProcessingEnvironment;
import javax.annotation.processing.Processor;
import javax.annotation.processing.RoundEnvironment;
import javax.annotation.processing.SupportedAnnotationTypes;
import javax.annotation.processing.SupportedSourceVersion;
import javax.lang.model.SourceVersion;
import javax.lang.model.element.Element;
import javax.lang.model.element.Modifier;
import javax.lang.model.element.TypeElement;
import javax.lang.model.element.VariableElement;
import javax.lang.model.type.TypeMirror;
import javax.lang.model.util.Elements;
import javax.lang.model.util.Types;
import javax.tools.JavaFileObject;

import static javax.lang.model.element.ElementKind.FIELD;

@AutoService(Processor.class)
@SupportedAnnotationTypes({"com.mobgen.halo.android.content.annotations.HaloQuery",
        "com.mobgen.halo.android.content.annotations.HaloQueries",
        "com.mobgen.halo.android.content.annotations.HaloField",
        "com.mobgen.halo.android.content.annotations.HaloSearchable",
        "com.mobgen.halo.android.content.annotations.HaloConstructor"})
@SupportedSourceVersion(SourceVersion.RELEASE_7)
public class HaloContentDatabaseProcessor extends AbstractProcessor {

    private Types typeUtils;
    private Elements elementUtils;
    private Filer filer;
    private Messager messager;
    private List databaseTables = new ArrayList();
    private List databaseVersion = new ArrayList<>();
    private List constructorElements =  new ArrayList<>();
    private List queriesElments = new ArrayList<>();
    private HashMap> indexableFields = new HashMap<>();

    //CONSTANTS
    private String PACKAGE_GENERATED = "com.mobgen.halo.android.app.generated";
    private String CLASS_NAME_GENERATED = "com.mobgen.halo.android.app.generated.";

    //classes
    private ClassName halo = ClassName.get("com.mobgen.halo.android.sdk.api","Halo");
    private ClassName haloPulgin = ClassName.get("com.mobgen.halo.android.sdk.api","HaloPluginApi");
    private ClassName thisClass = ClassName.get(PACKAGE_GENERATED,"HaloContentQueryApi");
    private ClassName keepClass = ClassName.get("android.support.annotation", "Keep");
    private ClassName dateClass = ClassName.get("java.util","Date");
    private ClassName tableAnnotationClass = ClassName.get("com.mobgen.halo.android.framework.storage.database.dsl.annotations", "Table");
    private ClassName tableClass = ClassName.get(" com.mobgen.halo.android.framework.storage.database.dsl", "HaloTable");
    private ClassName sqLite = ClassName.get("android.database.sqlite", "SQLiteDatabase");
    private ClassName columnClass = ClassName.get("com.mobgen.halo.android.framework.storage.database.dsl.annotations", "Column");
    private ClassName generatedHaloDatabase = ClassName.get("com.mobgen.halo.android.content.generated", "GeneratedHaloDatabase");
    private ClassName createQuery = ClassName.get("com.mobgen.halo.android.framework.storage.database.dsl.queries", "Create");
    private ClassName selectQuery = ClassName.get("com.mobgen.halo.android.framework.storage.database.dsl.queries", "Select");
    private ClassName dropQuery = ClassName.get("com.mobgen.halo.android.framework.storage.database.dsl.queries", "Drop");
    private ClassName contentValues = ClassName.get("android.content", "ContentValues");
    private ClassName contentSelectorFactory = ClassName.get("com.mobgen.halo.android.content.selectors","HaloContentSelectorFactory");
    private ClassName cursor = ClassName.get("android.database","Cursor");
    private ClassName paginated = ClassName.get("com.mobgen.halo.android.content.models","Paginated");
    private ClassName generatedContentQueriesInteractorInstance = ClassName.get("com.mobgen.halo.android.content.generated","GeneratedContentQueriesInteractor");
    private ClassName generatedContentQueriesRepositoryInstance = ClassName.get("com.mobgen.halo.android.content.generated","GeneratedContentQueriesRepository");
    private ClassName generatedContentQueriesLocalDataSource = ClassName.get("com.mobgen.halo.android.content.generated","GeneratedContentQueriesLocalDataSource");
    private ClassName cursor2ContentInstanceGeneratedModelConverter = ClassName.get("com.mobgen.halo.android.content.generated","Cursor2ContentInstanceGeneratedModelConverter");
    private ClassName cursor2GeneratedModelClassConverterFactory = ClassName.get("com.mobgen.halo.android.content.generated","Cursor2GeneratedModelClassConverterFactory");
    private ClassName storageData = ClassName.get("com.mobgen.halo.android.framework.toolbox.data","Data");
    private ClassName checkResult = ClassName.get("android.support.annotation","CheckResult");

    @Override
    public synchronized void init(ProcessingEnvironment processingEnv) {
        super.init(processingEnv);
        typeUtils = processingEnv.getTypeUtils();
        elementUtils = processingEnv.getElementUtils();
        filer = processingEnv.getFiler();
        messager = processingEnv.getMessager();
    }

    @Override
    public Set getSupportedAnnotationTypes() {
        Set annotataions = new LinkedHashSet();
        annotataions.add(HaloQuery.class.getCanonicalName());
        annotataions.add(HaloQueries.class.getCanonicalName());
        annotataions.add(HaloField.class.getCanonicalName());
        annotataions.add(HaloSearchable.class.getCanonicalName());
        annotataions.add(HaloConstructor.class.getCanonicalName());
        return annotataions;
    }

    @Override
    public SourceVersion getSupportedSourceVersion() {
        return SourceVersion.latestSupported();
    }


    @Override
    public boolean process(Set set, RoundEnvironment roundEnvironment) {

        for (Element query : roundEnvironment.getElementsAnnotatedWith(HaloQueries.class)) {
            queriesElments.add(query);
        }
        generateHaloQueryClass();

        //add all constructor to dabase
        for (Element constructor : roundEnvironment.getElementsAnnotatedWith(HaloConstructor.class)) {
            constructorElements.add(constructor);
        }
        //add all annotated query elements
        int haloConstructorIndex = 0;
        for (Element element : roundEnvironment.getElementsAnnotatedWith(HaloSearchable.class)) {
            String haloTableName = generateHaloSearchableClass(element,constructorElements.get(haloConstructorIndex));
            databaseTables.add(haloTableName);
            //get index fields
            List indexFields = new ArrayList<>();
            for(Element encloseElements : element.getEnclosedElements()){
                if(encloseElements.getAnnotation(HaloField.class)!=null && encloseElements.getAnnotation(HaloField.class).index()) {
                    indexFields.add(encloseElements.getAnnotation(HaloField.class).columnName());
                }
            }
            indexableFields.put(haloTableName,indexFields);
            databaseVersion.add(element.getAnnotation(HaloSearchable.class).version());
            haloConstructorIndex++;
        }
        //generate version table and database generated model creation
        if(databaseTables.size()>0) {
            generateHaloContentVersionTable();
            generateDatabaseMigration(roundEnvironment,1);
        }
        return true;
    }

    /**
     * Generate Halo query class with all the HaloQuery annotation
     *
     */
    private void generateHaloQueryClass(){
        String className = "HaloContentQueryApi";

        TypeSpec.Builder contentQueryBuilder = TypeSpec.classBuilder(className);
        contentQueryBuilder.addModifiers(Modifier.PUBLIC);
        contentQueryBuilder.superclass(haloPulgin);
        contentQueryBuilder.addJavadoc("This class was autogenerated to perfom queries.");

        //prepare method
        MethodSpec.Builder constructorBuilder = MethodSpec.constructorBuilder()
                .addJavadoc("Internal private constructor for the halo plugin.")
                .addModifiers(Modifier.PRIVATE)
                .addParameter(halo,"halo")
                .addStatement("super(halo)");

        contentQueryBuilder.addMethod(constructorBuilder.build());

        MethodSpec.Builder withMethod = MethodSpec.methodBuilder("with")
                .addJavadoc("Creates the Content Query Api instance with the reference to a yet created HALO instance")
                .addModifiers(Modifier.PUBLIC)
                .addModifiers(Modifier.STATIC)
                .addParameter(halo,"halo")
                .returns(thisClass)
                .addStatement("return new $T(halo)",thisClass);

        contentQueryBuilder.addMethod(withMethod.build());

        AnnotationSpec checkResultAnnontation = AnnotationSpec.builder(checkResult)
                .addMember("suggest","\"You may want to call execute() to run the task\"")
                .build();

        //add all annotated query elements for all elements with halo queries
        for(int w=0;w paramNames = new ArrayList<>();
                //create the query method
                for (int i = 0; i < query.length(); i++) {
                    if (query.charAt(i) == '@') {
                        i++;//skip { char
                        String param = query.substring(i + 1);
                        paramNames.add(param.substring(0, param.indexOf(":")));
                        methodBuilder.addParameter(resolveDataType(param.substring(param.indexOf(":") + 1, param.indexOf("}"))), param.substring(0, param.indexOf(":")));
                        query = query.replace("@{" + param.substring(0, param.indexOf("}") + 1), "?");
                    }
                }
                //return statement of the query method
                methodBuilder.addStatement("String query = $S", query);
                methodBuilder.addStatement("Object[] bindArgs = new Object[$L]", paramNames.size());
                for (int j = 0; j < paramNames.size(); j++) {
                    methodBuilder.addStatement("bindArgs[$L]=$L", j, paramNames.get(j));
                }

                methodBuilder.returns(ParameterizedTypeName.get(contentSelectorFactory, ParameterizedTypeName.get(paginated, modelClass), cursor))
                        .addStatement("return new HaloContentSelectorFactory<>(\n" +
                                        "\thalo(),\n" +
                                        "\tnew $1T(new $2T(new $3T(halo().framework())),query,bindArgs),\n" +
                                        "\tnew $4T($5T.class),\n" +
                                        "\tnew $6T(halo().framework().parser()),\n" +
                                        "\tnull,\n" +
                                        "\t$7T.STORAGE_ONLY,\n" +
                                        "\t\"generatedModelQuery with method $8L\")", generatedContentQueriesInteractorInstance, generatedContentQueriesRepositoryInstance,
                                generatedContentQueriesLocalDataSource, cursor2ContentInstanceGeneratedModelConverter,
                                modelClass, cursor2GeneratedModelClassConverterFactory, storageData, methodName);

                MethodSpec queryDatabase = methodBuilder.build();
                contentQueryBuilder.addMethod(queryDatabase);
            }
        }
        TypeSpec contentQueryClass = contentQueryBuilder.build();
        generatedJavaFile(className, contentQueryClass);
    }

    /**
     * Create a class per model annotated with HaloSearchable annotation
     * @param element Element to generate halo database table.
     * @param constructorElement Constructor model annotation with column names.
     * @return The database table name.
     */
    private String generateHaloSearchableClass(Element element, Element constructorElement){
        String className = "HaloTable$$" + element.getAnnotation(HaloSearchable.class).tableName();
        String tableAnnotationName = element.getAnnotation(HaloSearchable.class).tableName();

        TypeSpec.Builder tableClassBuilder = TypeSpec.classBuilder(className);
        tableClassBuilder.addModifiers(Modifier.PUBLIC);
        tableClassBuilder.addJavadoc("This class was autogenerated to perfom queries.");

        AnnotationSpec tableAnnotation = AnnotationSpec.builder(tableAnnotationClass)
                .addMember("value","$S",tableAnnotationName)
                .build();

        //create table
        TypeSpec.Builder constructorBuilder = TypeSpec.interfaceBuilder(className)
                .addJavadoc("Constructor of the class to create table")
                .addSuperinterface(tableClass)
                .addAnnotation(keepClass)
                .addAnnotation(tableAnnotation)
                .addModifiers(Modifier.PUBLIC);

        //prepare all fields to convert to columns on halotable
        int haloConstructorFieldIndex = 0;
        for(int k=0;k indexFields = indexableFields.get(databaseTables.get(i));
            for(int j=0;j




© 2015 - 2024 Weber Informatics LLC | Privacy Policy