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

com.grosner.processor.definition.ColumnDefinition Maven / Gradle / Ivy

The newest version!
package com.grosner.processor.definition;

import com.google.common.collect.Sets;
import com.grosner.dbflow.annotation.Column;
import com.grosner.dbflow.annotation.ContainerKey;
import com.grosner.dbflow.annotation.ForeignKeyReference;
import com.grosner.dbflow.sql.SQLiteType;
import com.grosner.processor.Classes;
import com.grosner.processor.ProcessorUtils;
import com.grosner.processor.model.ProcessorManager;
import com.grosner.processor.model.builder.AdapterQueryBuilder;
import com.grosner.processor.model.builder.MockConditionQueryBuilder;
import com.grosner.processor.utils.ModelUtils;
import com.grosner.processor.writer.FlowWriter;
import com.squareup.javawriter.JavaWriter;

import javax.lang.model.element.Modifier;
import javax.lang.model.element.TypeElement;
import javax.lang.model.element.VariableElement;
import javax.lang.model.type.ArrayType;
import javax.lang.model.type.DeclaredType;
import javax.lang.model.type.PrimitiveType;
import javax.tools.Diagnostic;

import java.io.IOException;

/**
 * Author: andrewgrosner
 * Description:
 */
public class ColumnDefinition extends BaseDefinition implements FlowWriter {

    public String columnName;

    public String columnFieldName;

    public String columnFieldType;

    private String modelContainerType;

    public TypeElement modelType;

    public boolean hasTypeConverter = false;

    public int columnType;

    public Column column;

    public ForeignKeyReference[] foreignKeyReferences;

    public boolean isModel;

    /**
     * Whether this field is itself a model container
     */
    public boolean isModelContainer;

    public String containerKeyName;

    public ColumnDefinition(ProcessorManager processorManager, VariableElement element) {
        super(element, processorManager);

        column = element.getAnnotation(Column.class);
        this.columnName = column.name().equals("") ? element.getSimpleName().toString() : column.name();
        this.columnFieldName = element.getSimpleName().toString();
        this.columnFieldType = element.asType().toString();

        ContainerKey containerKey = element.getAnnotation(ContainerKey.class);
        if(containerKey != null) {
            containerKeyName = containerKey.value();
        } else {
            containerKeyName = columnFieldName;
        }

        if (element.asType().getKind().isPrimitive()) {
            this.modelType = processorManager.getTypeUtils().boxedClass((PrimitiveType) element.asType());
        } else {
            boolean isAModelContainer = false;
            DeclaredType declaredType = null;
            if (element.asType() instanceof DeclaredType) {
                declaredType = (DeclaredType) element.asType();
                isAModelContainer = !declaredType.getTypeArguments().isEmpty();
            } else if (element.asType() instanceof ArrayType) {
                processorManager.getMessager().printMessage(Diagnostic.Kind.ERROR, "Columns cannot be of array type.");
            }

            // TODO: not currently correctly supporting model containers as fields. Certainly is possible
            if (isAModelContainer) {
                isModelContainer = true;
                // TODO: hack for now
                modelContainerType = columnFieldType;
                this.modelType = (TypeElement) processorManager.getTypeUtils().asElement(declaredType.getTypeArguments().get(0));
                columnFieldType = modelType.asType().toString();
            } else {
                this.modelType = processorManager.getElements().getTypeElement(element.asType().toString());
            }
        }

        columnType = column.columnType();

        if (columnType == Column.FOREIGN_KEY) {
            foreignKeyReferences = column.references();
        }

        isModel = ProcessorUtils.implementsClass(processorManager.getProcessingEnvironment(), Classes.MODEL, modelType);

        // Any annotated members, otherwise we will use the scanner to find other ones
        final TypeConverterDefinition typeConverterDefinition = processorManager.getTypeConverterDefinition(modelType);
        if (typeConverterDefinition != null) {
            hasTypeConverter = true;
        }

        // If type cannot be represented, we will get Type converter anyways
        if (!hasTypeConverter && !isModel) {
            hasTypeConverter = !SQLiteType.containsClass(columnFieldType);
        }

    }


    @Override
    public void write(JavaWriter javaWriter) throws IOException {
        if (isModel || isModelContainer) {
            for (ForeignKeyReference reference : foreignKeyReferences) {
                writeColumnDefinition(javaWriter, (columnName + "_" + reference.columnName()).toUpperCase(), reference.columnName());
            }
        } else {
            writeColumnDefinition(javaWriter, columnName);
        }
    }

    protected void writeColumnDefinition(JavaWriter javaWriter, String columnName) throws IOException {
        writeColumnDefinition(javaWriter, columnName.toUpperCase(), columnName);
    }

    /**
     * When the field name is different from the column name (foreign key names)
     *
     * @param javaWriter The writer
     * @param fieldName  The name of the filed
     * @param columnName The column name
     * @throws IOException if write fails
     */
    protected void writeColumnDefinition(JavaWriter javaWriter, String fieldName, String columnName) throws IOException {
        javaWriter.emitField("String", fieldName,
                Sets.newHashSet(Modifier.PUBLIC, Modifier.STATIC, Modifier.FINAL),
                "\"" + columnName + "\"");
        javaWriter.emitEmptyLine();
    }

    public void writeSaveDefinition(JavaWriter javaWriter, boolean isModelContainerDefinition) throws IOException {
        if (columnType == Column.FOREIGN_KEY && isModel) {
            javaWriter.emitEmptyLine();
            if (isModelContainer) {
                javaWriter.emitSingleLineComment("Begin Saving Model Container To DB");
            } else {
                javaWriter.emitSingleLineComment("Begin Saving Foreign Key References From Model");
            }

            if (isModelContainerDefinition) {
                String modelContainerName = ModelUtils.getVariable(true) + columnFieldName;
                javaWriter.emitStatement("ModelContainer %1s = %1s.getInstance(%1s.getValue(\"%1s\"), %1s.class)", modelContainerName,
                        ModelUtils.getVariable(true), ModelUtils.getVariable(true), columnFieldName, columnFieldType);
                javaWriter.emitStatement("%1s.save(false)", modelContainerName);
                for (ForeignKeyReference foreignKeyReference : foreignKeyReferences) {
                    AdapterQueryBuilder adapterQueryBuilder = new AdapterQueryBuilder();
                    adapterQueryBuilder.appendContentValues()
                            .appendPut(foreignKeyReference.columnName())
                                .appendCast(ModelUtils.getClassFromAnnotation(foreignKeyReference))
                                    .append(modelContainerName)
                                    .append(".")
                                    .appendGetValue(foreignKeyReference.foreignColumnName())
                            .append("))");
                    javaWriter.emitStatement(adapterQueryBuilder.getQuery());
                }

            } else {
                String modelStatement = ModelUtils.getModelStatement(columnFieldName);
                javaWriter.beginControlFlow("if (%1s != null)", modelStatement);
                javaWriter.emitStatement("%1s.save(false)", modelStatement);
                for (ForeignKeyReference foreignKeyReference : foreignKeyReferences) {
                    ModelUtils.writeContentValueStatement(javaWriter, foreignKeyReference.columnName(),
                            columnName, ModelUtils.getClassFromAnnotation(foreignKeyReference),
                            foreignKeyReference.foreignColumnName(), foreignKeyReference.foreignColumnName(),
                            false, isModelContainer, true, false, columnFieldType);
                }
                javaWriter.endControlFlow();
            }
            javaWriter.emitSingleLineComment("End");
            javaWriter.emitEmptyLine();
        } else {
            // Normal field
            String newFieldType = null;

            // convert field type for what type converter reports
            if (hasTypeConverter) {
                TypeConverterDefinition typeConverterDefinition = manager.getTypeConverterDefinition(modelType);
                if (typeConverterDefinition == null) {
                    manager.getMessager().printMessage(Diagnostic.Kind.ERROR, String.format("No Type Converter found for %1s", modelType));
                } else {
                    newFieldType = typeConverterDefinition.getDbElement().asType().toString();
                }
            } else {
                newFieldType = columnFieldType;
            }

            String getType = columnFieldType;
            // Type converters can never be primitive except boolean
            if (element.asType().getKind().isPrimitive()) {
                getType = manager.getTypeUtils().boxedClass((PrimitiveType) element.asType()).asType().toString();
            }

            if(isModelContainerDefinition) {
                if (element.asType().getKind().isPrimitive()) {
                    newFieldType = manager.getTypeUtils().boxedClass((PrimitiveType) element.asType()).asType().toString();
                }
            }

            ModelUtils.writeContentValueStatement(javaWriter, columnName, columnName,
                    newFieldType, columnFieldName, containerKeyName, isModelContainerDefinition, isModelContainer, false, hasTypeConverter, getType);
        }
    }

    public void writeLoadFromCursorDefinition(JavaWriter javaWriter, boolean isModelContainerDefinition) throws IOException {
        if (columnType == Column.FOREIGN_KEY) {
            //TODO: This is wrong, should be using condition query builder
            javaWriter.emitEmptyLine();
            javaWriter.emitSingleLineComment("Begin Loading %1s Model Foreign Key", columnFieldName);

            // special case for model objects within class
            if (!isModelContainer && !isModelContainerDefinition && isModel) {

                ModelUtils.writeColumnIndexCheckers(javaWriter, foreignKeyReferences);
                MockConditionQueryBuilder conditionQueryBuilder = new MockConditionQueryBuilder()
                        .appendForeignKeyReferences(columnFieldType + TableDefinition.DBFLOW_TABLE_TAG, columnName, foreignKeyReferences);

                String rawConditionStatement = String.format("new Select().from(%1s).where().%1s.querySingle()",
                        ModelUtils.getFieldClass(columnFieldType), conditionQueryBuilder);

                AdapterQueryBuilder adapterQueryBuilder = new AdapterQueryBuilder().appendVariable(false);
                adapterQueryBuilder.append(".").append(columnFieldName).appendSpaceSeparated("=");
                adapterQueryBuilder.append(rawConditionStatement);
                javaWriter.emitStatement(adapterQueryBuilder.getQuery());

                javaWriter.endControlFlow();
            } else {
                if (isModelContainerDefinition) {
                    String modelContainerName = ModelUtils.getVariable(true) + columnFieldName;
                    javaWriter.emitStatement("ModelContainer %1s = %1s.getInstance(%1s.newDataInstance(), %1s.class)",
                            modelContainerName, ModelUtils.getVariable(true), ModelUtils.getVariable(true),
                            columnFieldType);
                    for (ForeignKeyReference foreignKeyReference : foreignKeyReferences) {

                        ModelUtils.writeColumnIndexCheckers(javaWriter, foreignKeyReferences);
                        AdapterQueryBuilder adapterQueryBuilder = new AdapterQueryBuilder();
                        adapterQueryBuilder.append(modelContainerName)
                                .appendPut(foreignKeyReference.foreignColumnName())
                                .append(ModelUtils.getCursorStatement(ModelUtils.getClassFromAnnotation(foreignKeyReference),
                                        foreignKeyReference.columnName()))
                                .append(")");
                        javaWriter.emitStatement(adapterQueryBuilder.getQuery());

                        javaWriter.endControlFlow();
                    }

                    javaWriter.emitStatement("%1s.put(\"%1s\",%1s.getData())", ModelUtils.getVariable(true), columnFieldName, modelContainerName);
                } else {

                    for (ForeignKeyReference foreignKeyReference : foreignKeyReferences) {
                        // instantiate model container
                        if(isModelContainer) {
                            AdapterQueryBuilder containerBuilder =
                                    new AdapterQueryBuilder().appendVariable(isModelContainerDefinition)
                                            .append(".").append(columnFieldName)
                                            .appendSpaceSeparated("=")
                                            .append("new ").append(element.asType().toString())
                                            .appendParenthesisEnclosed(ModelUtils.getFieldClass(columnFieldType));
                            javaWriter.emitStatement(containerBuilder.getQuery());
                        }

                        ModelUtils.writeLoadFromCursorDefinitionField(javaWriter, manager, ModelUtils.getClassFromAnnotation(foreignKeyReference),
                                columnFieldName, foreignKeyReference.columnName(), foreignKeyReference.foreignColumnName(),
                                foreignKeyReference.columnName(), element, false, isModelContainerDefinition, isModelContainer);
                    }
                }
            }
            javaWriter.emitSingleLineComment("End");
            javaWriter.emitEmptyLine();

        } else {
            String getType = columnFieldType;
            // Type converters can never be primitive except boolean
            if (element.asType().getKind().isPrimitive()) {
                getType = manager.getTypeUtils().boxedClass((PrimitiveType) element.asType()).asType().toString();
            }

            ModelUtils.writeLoadFromCursorDefinitionField(javaWriter, manager, getType, columnFieldName,
                    columnName, "",containerKeyName, modelType, hasTypeConverter, isModelContainerDefinition, this.isModelContainer);
        }
    }

    public void writeToModelDefinition(JavaWriter javaWriter) throws IOException {
        AdapterQueryBuilder queryBuilder = new AdapterQueryBuilder();
        queryBuilder.appendVariable(false).append(".").append(columnFieldName);
        queryBuilder.appendSpaceSeparated("=");

        if (hasTypeConverter) {
            queryBuilder.appendTypeConverter(columnFieldType, columnFieldType, true);
        } else {
            queryBuilder.appendCast(isModelContainer ? modelContainerType : columnFieldType);
        }

        if (isModel) {
            queryBuilder.appendVariable(true)
                    .append(".getInstance(");
        }

        queryBuilder.appendVariable(true).append(".").appendGetValue(containerKeyName);

        if (!isModel) {
            queryBuilder.append(")");
        }

        if (isModel) {
            queryBuilder.append(",").append(ModelUtils.getFieldClass(columnFieldType)).append(")");
        }

        if (hasTypeConverter) {
            queryBuilder.append(")");
        }

        if (isModel) {
            queryBuilder.append(".toModel())");
        }

        javaWriter.emitStatement(queryBuilder.getQuery());
    }


}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy