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

com.nhaarman.ellie.internal.codegen.writer.ModelRepositoryWriter Maven / Gradle / Ivy

The newest version!
/*
 * Copyright (C) 2014 Niek Haarman
 *
 * 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.nhaarman.ellie.internal.codegen.writer;

import android.content.ContentValues;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;

import com.google.common.collect.Sets;
import com.nhaarman.ellie.Ellie;
import com.nhaarman.ellie.Model;
import com.nhaarman.ellie.ModelRepository;
import com.nhaarman.ellie.annotation.Table;
import com.nhaarman.ellie.internal.codegen.Registry;
import com.nhaarman.ellie.internal.codegen.element.ColumnElement;
import com.nhaarman.ellie.query.Select;
import com.nhaarman.ellie.util.LruCache;
import com.squareup.javawriter.JavaWriter;

import java.io.IOException;
import java.io.Writer;
import java.util.EnumSet;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;

import javax.lang.model.element.Modifier;
import javax.lang.model.element.TypeElement;

import static javax.lang.model.element.Modifier.FINAL;
import static javax.lang.model.element.Modifier.STATIC;

@SuppressWarnings("HardCodedStringLiteral") public class ModelRepositoryWriter implements SourceWriter {

    private static final Map CURSOR_METHOD_MAP = new HashMap() {
        {
            put(byte[].class.getName(), "getBlob");
            put(Byte[].class.getName(), "getBlob");
            put(double.class.getName(), "getDouble");
            put(Double.class.getName(), "getDouble");
            put(float.class.getName(), "getFloat");
            put(Float.class.getName(), "getFloat");
            put(int.class.getName(), "getInt");
            put(Integer.class.getName(), "getInt");
            put(long.class.getName(), "getLong");
            put(Long.class.getName(), "getLong");
            put(short.class.getName(), "getShort");
            put(Short.class.getName(), "getShort");
            put(String.class.getName(), "getString");
        }
    };

    private static final EnumSet PRIVATE = EnumSet.of(Modifier.PRIVATE, FINAL);
    private static final EnumSet PUBLIC = EnumSet.of(Modifier.PUBLIC, FINAL);
    private static final EnumSet PUBLIC_FINAL = EnumSet.of(Modifier.PUBLIC, FINAL);
    private static final EnumSet PRIVATE_STATIC = EnumSet.of(Modifier.PRIVATE, STATIC);

    private final Registry mRegistry;

    public ModelRepositoryWriter(final Registry registry) {
        mRegistry = registry;
    }

    @Override
    public String createSourceName(final TypeElement element) {
        return "com.nhaarman.ellie." + createSimpleName(element);
    }

    @Override
    public void writeSource(final Writer writer, final TypeElement element) throws IOException {
        final String classSimpleName = createSimpleName(element);
        final String modelSimpleName = element.getSimpleName().toString();
        final String modelQualifiedName = element.getQualifiedName().toString();
        final String tableName = element.getAnnotation(Table.class).value();
        final Set columns = mRegistry.getColumnElements(element);

        JavaWriter javaWriter = new JavaWriter(writer);
        javaWriter.setCompressingTypes(true);
        javaWriter.setIndent("    ");

        javaWriter.emitSingleLineComment("Generated by Ellie. Do not modify!");
        javaWriter.emitPackage("com.nhaarman.ellie");

        writeImports(javaWriter, modelQualifiedName, columns);

        writeBeginType(javaWriter, classSimpleName, modelSimpleName, modelQualifiedName);

        writeFields(javaWriter, modelSimpleName);
        writeConstructor(javaWriter);
        writeFind(javaWriter, modelSimpleName);
        writeCreate(javaWriter, modelSimpleName, tableName);
        writeUpdate(javaWriter, modelSimpleName, tableName);
        writeLoad(javaWriter, modelQualifiedName, columns);
        writeCreateOrUpdate(javaWriter, modelSimpleName);
        writeDelete(javaWriter, modelSimpleName, tableName);
        writePutEntity(javaWriter, modelSimpleName);
        writeGetEntity(javaWriter, modelSimpleName);
        writeRemoveEntity(javaWriter, modelSimpleName);
        writeGetOrFindEntity(javaWriter, modelSimpleName);
        writeCreateContentValues(javaWriter, modelQualifiedName, columns);
        writeGetEntityIdentifier(javaWriter, modelSimpleName);

        javaWriter.endType();
    }

    private void writeImports(final JavaWriter writer, final String modelQualifiedName, final Set columns)
            throws IOException {

        Set imports = Sets.newHashSet(
                modelQualifiedName,
                Ellie.class.getName(),
                ContentValues.class.getName(),
                Cursor.class.getName(),
                SQLiteDatabase.class.getName(),
                ModelRepository.class.getName(),
                Select.class.getName(),
                LruCache.class.getName()
        );

        for (ColumnElement column : columns) {
            if (column.isModel()) {
                imports.add(Long.class.getName());
            }
            if (column.requiresTypeAdapter()) {
                imports.add(column.getDeserializedQualifiedName());
                imports.add(column.getSerializedQualifiedName());
            }
        }

        TypeElement modelRepositoryElement = mRegistry.getModelElement(modelQualifiedName).getModelRepositoryElement();
        if (modelRepositoryElement != null) {
            imports.add(modelRepositoryElement.getQualifiedName().toString());
        }

        writer.emitImports(imports);
        writer.emitEmptyLine();
    }

    private void writeBeginType(final JavaWriter javaWriter, final String classSimpleName, final String modelSimpleName, final String modelQualifiedName) throws IOException {
        TypeElement modelRepositoryElement = mRegistry.getModelElement(modelQualifiedName).getModelRepositoryElement();
        String modelRepositoryName = modelRepositoryElement == null ? null : modelRepositoryElement.getQualifiedName().toString();

        javaWriter.beginType(classSimpleName, "class", PUBLIC_FINAL, modelRepositoryName, "ModelRepository<" + modelSimpleName + ">");
        javaWriter.emitEmptyLine();
    }

    private void writeFields(final JavaWriter javaWriter, final String modelSimpleName) throws IOException {
        javaWriter.emitField(Ellie.class.getSimpleName(), "mEllie", PRIVATE);
        javaWriter.emitField(SQLiteDatabase.class.getSimpleName(), "mDatabase", PRIVATE);
        javaWriter.emitField(
                String.format(
                        "%s<%s, %s>",
                        LruCache.class.getSimpleName(),
                        String.class.getSimpleName(),
                        modelSimpleName
                ),
                "mCache",
                PRIVATE
        );

        javaWriter.emitEmptyLine();
    }

    private void writeConstructor(final JavaWriter javaWriter) throws IOException {
        javaWriter.beginConstructor(
                EnumSet.of(Modifier.PUBLIC),
                "final " + Ellie.class.getSimpleName(), "ellie",
                "final " + SQLiteDatabase.class.getSimpleName(), "database",
                "final int", "cacheSize"
        );

        javaWriter.emitStatement("mEllie = ellie");
        javaWriter.emitStatement("mDatabase = database");
        javaWriter.emitStatement("mCache = new LruCache<>(cacheSize)");

        javaWriter.endConstructor();
        javaWriter.emitEmptyLine();
    }

    private void writeFind(final JavaWriter javaWriter, final String modelSimpleName) throws IOException {
        javaWriter.emitAnnotation(Override.class);
        javaWriter.beginMethod(modelSimpleName, "find", PUBLIC, "final long", " id");

        javaWriter.emitStatement("return new Select().from(%s.class).where(\"%s=?\", id).fetchSingle()", modelSimpleName, Model.COLUMN_ID);

        javaWriter.endMethod();
        javaWriter.emitEmptyLine();
    }

    private void writeCreate(final JavaWriter javaWriter, final String modelSimpleName, final String tableName) throws IOException {
        javaWriter.emitAnnotation(Override.class);
        javaWriter.beginMethod(Long.class.getSimpleName(), "create", PUBLIC, "final " + modelSimpleName, " entity");

        javaWriter.emitStatement("ContentValues values = createContentValues(entity)");
        javaWriter.emitStatement("entity.setId(mDatabase.insert(\"%s\", null, values))", tableName);
        javaWriter.emitStatement("return entity.getId()");

        javaWriter.endMethod();
        javaWriter.emitEmptyLine();
    }

    private void writeUpdate(final JavaWriter javaWriter, final String modelSimpleName, final String tableName) throws IOException {
        javaWriter.emitAnnotation(Override.class);
        javaWriter.beginMethod(Long.class.getSimpleName(), "update", PUBLIC, "final " + modelSimpleName, " entity");

        javaWriter.emitStatement("ContentValues values = createContentValues(entity)");
        javaWriter.emitStatement("mDatabase.update(\"%s\", values, Model.COLUMN_ID + \"=?\", new String[]{entity.getId().toString()})", tableName);
        javaWriter.emitStatement("return entity.getId()");

        javaWriter.endMethod();
        javaWriter.emitEmptyLine();
    }

    private void writeLoad(final JavaWriter writer, final String modelQualifiedName, final Set columns) throws IOException {
        writer.emitAnnotation(Override.class);
        writer.beginMethod(void.class.getSimpleName(), "load", EnumSet.of(Modifier.PUBLIC, FINAL), "final " + modelQualifiedName, "entity", "final Cursor", "cursor");

        for (ColumnElement column : columns) {
            final StringBuilder value = new StringBuilder();

            int closeParens = 1;
            if (column.isModel()) {
                closeParens++;
                value.append("mEllie.getModelRepository(")
                     .append(column.getDeserializedQualifiedName())
                     .append(".class).getOrFindEntity(");
            } else if (column.requiresTypeAdapter()) {
                closeParens++;
                value.append("mEllie.getTypeAdapter(")
                     .append(column.getDeserializedSimpleName())
                     .append(".class).deserialize(");
            }

            value.append("cursor.").append(CURSOR_METHOD_MAP.get(column.getSerializedQualifiedName())).append("(");
            value.append("cursor.getColumnIndex(\"").append(column.getColumnName()).append("\")");

            for (int i = 0; i < closeParens; i++) {
                value.append(")");
            }

            if (column.getSetter() == null) {
                writer.emitStatement("entity.%s = %s", column.getFieldName(), value.toString());
            } else {
                writer.emitStatement("entity.%s(%s)", column.getSetter().getSimpleName(), value.toString());
            }
        }

        writer.endMethod();
        writer.emitEmptyLine();
    }

    private void writeCreateOrUpdate(final JavaWriter writer, final String modelSimpleName) throws IOException {
        writer.emitAnnotation(Override.class);
        writer.beginMethod(Long.class.getSimpleName(), "createOrUpdate", PUBLIC, "final " + modelSimpleName, "entity");

        writer.beginControlFlow("if (entity.getId() == null)");
        writer.emitStatement("return create(entity)");
        writer.nextControlFlow("else");
        writer.emitStatement("return update(entity)");
        writer.endControlFlow();

        writer.endMethod();
        writer.emitEmptyLine();
    }

    private void writeDelete(final JavaWriter writer, final String modelSimpleName, final String tableName) throws IOException {
        writer.emitAnnotation(Override.class);
        writer.beginMethod(void.class.getSimpleName(), "delete", PUBLIC, "final " + modelSimpleName, "entity");

        writer.emitStatement("mDatabase.delete(\"%s\", \"%s=?\", new String[]{entity.getId().toString()});", tableName, Model.COLUMN_ID);

        writer.endMethod();
        writer.emitEmptyLine();
    }

    private void writePutEntity(final JavaWriter writer, final String modelSimpleName) throws IOException {
        writer.emitAnnotation(Override.class);
        writer.beginMethod(void.class.getSimpleName(), "putEntity", PUBLIC, "final " + modelSimpleName, "entity");

        writer.beginControlFlow("if (entity.getId() != null)");
        writer.emitStatement("mCache.put(getEntityIdentifier(entity.getId()), entity)");
        writer.endControlFlow();

        writer.endMethod();
        writer.emitEmptyLine();
    }

    private void writeGetEntity(final JavaWriter writer, final String modelSimpleName) throws IOException {
        writer.emitAnnotation(Override.class);
        writer.beginMethod(modelSimpleName, "getEntity", PUBLIC, "final long", "id");

        writer.emitStatement("return mCache.get(getEntityIdentifier(id))");

        writer.endMethod();
        writer.emitEmptyLine();
    }

    private void writeRemoveEntity(final JavaWriter writer, final String modelSimpleName) throws IOException {
        writer.emitAnnotation(Override.class);
        writer.beginMethod(void.class.getSimpleName(), "removeEntity", PUBLIC, "final " + modelSimpleName, "entity");

        writer.beginControlFlow("if (entity.getId() != null)");
        writer.emitStatement("mCache.remove(getEntityIdentifier(entity.getId()))");
        writer.endControlFlow();

        writer.endMethod();
        writer.emitEmptyLine();
    }

    private void writeGetOrFindEntity(final JavaWriter writer, final String modelSimpleName) throws IOException {
        writer.emitAnnotation(Override.class);
        writer.beginMethod(modelSimpleName, "getOrFindEntity", PUBLIC, "final long", "id");

        writer.emitStatement("%s entity = getEntity(id)", modelSimpleName);

        writer.beginControlFlow("if (entity == null)");
        writer.emitStatement("entity = find(id)");
        writer.endControlFlow();

        writer.emitStatement("return entity");

        writer.endMethod();
        writer.emitEmptyLine();
    }

    private void writeGetEntityIdentifier(final JavaWriter writer, final String modelSimpleName) throws IOException {
        writer.beginMethod(String.class.getSimpleName(), "getEntityIdentifier", PRIVATE_STATIC, "final long", "id");

        writer.emitStatement("return \"%s@\" + id", modelSimpleName);

        writer.endMethod();
        writer.emitEmptyLine();
    }

    private void writeCreateContentValues(final JavaWriter writer, final String modelQualifiedName, final Set columns) throws IOException {
        writer.beginMethod(ContentValues.class.getSimpleName(), "createContentValues", PUBLIC, "final " + modelQualifiedName, "entity");
        writer.emitStatement("ContentValues values = new ContentValues()");

        final StringBuilder value = new StringBuilder();
        for (ColumnElement column : columns) {
            value.setLength(0);
            int closeParens = 0;

            if (!column.isModel() && column.requiresTypeAdapter()) {
                closeParens++;
                value.append("(").append(column.getSerializedSimpleName())
                     .append(") mEllie.getTypeAdapter(")
                     .append(column.getDeserializedSimpleName())
                     .append(".class).serialize(");
            }

            if (column.getGetter() == null) {
                value.append("entity.").append(column.getFieldName());
            } else {
                value.append("entity.").append(column.getGetter());
            }

            if (column.isModel()) {
                value.append(" != null ? ");
                value.append("entity.");
                if (column.getGetter() == null) {
                    value.append(column.getFieldName());
                } else {
                    value.append(column.getGetter());
                }
                value.append(".getId()");
                value.append(" : null");
            }

            for (int i = 0; i < closeParens; i++) {
                value.append(")");
            }

            writer.emitStatement("values.put(\"" + column.getColumnName() + "\", " + value + ")");
        }

        writer.emitStatement("return values");
        writer.endMethod();
        writer.emitEmptyLine();
    }

    private String createSimpleName(final TypeElement element) {
        return element.getSimpleName() + "$$Repository";
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy