Android Open Source - adme A D M E






From Project

Back to project page adme.

License

The source code is released under:

Apache License

If you think the Android project adme listed in this page is inappropriate, such as containing malicious code/tools or violating the copyright, please email info at java2s dot com, thanks.

Java Source Code

package com.danielesegato.adme;
/*  w  ww  .  ja  v a  2 s . com*/
import android.content.ContentValues;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.util.Log;

import com.danielesegato.adme.config.ADMEConfigUtils;
import com.danielesegato.adme.config.ADMEEntityConfig;
import com.danielesegato.adme.config.ADMEFieldConfig;
import com.danielesegato.adme.config.ADMEIndexConstraintConfig;
import com.danielesegato.adme.config.OnForeignUpdateDelete;
import com.danielesegato.adme.db.ADMESerializer;
import com.danielesegato.adme.db.ADMESerializerMapping;
import com.danielesegato.adme.utils.SQLStringHelper;

import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

/**
 * Android Database Made Easy library entry point.
 * <p/>
 * Utility methods to create and drop the entities in the database.
 */
public class ADME {

    private static final String TAG = ADME.class.getSimpleName();

    /**
     * Convert an entity row into it's Andoird {@link ContentValues} ready to use in an insert or update
     * query.
     * <p/>
     * If the class of the entityRow is not annotated with {@link com.danielesegato.adme.annotation.ADMEEntity}
     * this method will throw a RuntimeException.
     *
     * @param values             The ContentValues to recycle or null (one will be created), it will not
     *                           be cleared, it's the caller job to do so if you require it.
     * @param entityRow          The instance of the entity from which you want to extract values.
     * @param includeId          <em>True</em> if you want to include the ID in the content values,
     *                           <em>false</em> otherwise, if the id is autogenerated it will never be
     *                           included, regardless of the value you pass here
     * @param includeForeignKeys <em>True</em> to include foreign key, <em>false</em> otherwise (useful
     *                           if you need to use back references)
     * @return the ContentValues from the entity row
     */
    public static <T> ContentValues entityToContentValues(ContentValues values, T entityRow, boolean includeId, boolean includeForeignKeys) {
        Set<String> columns = getAllColumnsSet(entityRow.getClass(), includeId, includeForeignKeys);
        return entityToContentValues(values, entityRow, columns);
    }

    /**
     * Build a set with all the columns of of an entity
     *
     * @param entityClass        the entity class
     * @param <T>                the type of object
     * @param includeId          <em>True</em> if you want to include the ID in the content values,
     *                           <em>false</em> otherwise, if the id is autogenerated it will never be
     *                           included, regardless of the value you pass here
     * @param includeForeignKeys <em>True</em> to include foreign key, <em>false</em> otherwise (useful
     *                           if you need to use back references)
     * @return the complete set of columns for this entity
     */
    public static <T> Set<String> getAllColumnsSet(Class<T> entityClass, boolean includeId, boolean includeForeignKeys) {
        final ADMEEntityConfig<T> entityConfig = ADMEConfigUtils.lookupADMEEntityConfig(entityClass);
        Set<String> columns = new HashSet<String>();
        for (ADMEFieldConfig field : entityConfig.getFieldsConfig()) {
            if (field.isGeneratedId()) {
                continue;
            } else if (!includeId && field.isId()) {
                continue;
            }
            if (!includeForeignKeys && field.isForeign()) {
                continue;
            }
            columns.add(field.getColumnName());
        }
        return columns;
    }

    /**
     * Convert an entity row into it's Andoird {@link ContentValues} ready to use in an insert or update
     * query.
     * <p/>
     * If the class of the entityRow is not annotated with {@link com.danielesegato.adme.annotation.ADMEEntity}
     * this method will throw a RuntimeException.
     *
     * @param values    The ContentValues to recycle or null (one will be created), it will not
     *                  be cleared, it's the caller job to do so if you require it.
     * @param entityRow The instance of the entity from which you want to extract values.
     * @param columns   The set of columns to include in the content values.
     * @return the ContentValues from the entity row
     */
    public static <T> ContentValues entityToContentValues(ContentValues values, T entityRow, Set<String> columns) {
        final ADMEEntityConfig<T> entityConfig = ADMEConfigUtils.lookupADMEEntityConfig((Class<T>) entityRow.getClass());
        if (values == null) {
            values = new ContentValues();
        }
        try {
            for (final ADMEFieldConfig fieldConfig : entityConfig.getFieldsConfig()) {
                if (!columns.contains(fieldConfig.getColumnName())) {
                    continue;
                }
                final Field field;
                final Object instance;
                if (!fieldConfig.isForeign()) {
                    field = fieldConfig.getJavaField();
                    instance = entityRow;
                } else {
                    field = fieldConfig.getForeignFieldConfig().getJavaField();
                    instance = fieldConfig.getJavaField().get(entityRow);
                }
                // TODO use get method if annotated like that?
                final Object fieldValue = instance != null ? field.get(instance) : null;
                final ADMESerializer admeSerializer = fieldConfig.getADMESerializer();
                admeSerializer.storeInContentValues(fieldConfig.getColumnName(), values, fieldValue, fieldConfig);
            }
        } catch (IllegalAccessException e) {
            String msg = String.format("Error serializing entity %s, couldn't access some field, class: %s", entityConfig.getEntityName(), entityRow);
            Log.e(TAG, msg, e);
            throw new RuntimeException(msg, e);
        }
        return values;
    }

    /**
     * Convert a {@link android.database.Cursor} into an Entity instance.
     * <p/>
     * If the class of the entityRow is not annotated with {@link com.danielesegato.adme.annotation.ADMEEntity}
     * this method will throw a RuntimeException.
     * <p/>
     * Any missing column will be ignored.
     *
     * @param cursor  The Cursor containing the data, it will not
     *                be cleared, it's the caller job to do so if you require it.
     * @param clazz   The class of the instance to be created, it must have a public empty constructor
     * @param <T>     the type of entity
     * @return the Entity with the data extracted by the cursor
     */
    private static <T> T cursorToEntity(Cursor cursor, Class<T> clazz) {
        return cursorToEntity(cursor, clazz, getAllColumnsSet(clazz, true, true));
    }

    /**
     * Convert a {@link android.database.Cursor} into an Entity instance.
     * <p/>
     * If the class of the entityRow is not annotated with {@link com.danielesegato.adme.annotation.ADMEEntity}
     * this method will throw a RuntimeException.
     * <p/>
     * Any missing column will be ignored.
     *
     * @param cursor  The Cursor containing the data, it will not
     *                be cleared, it's the caller job to do so if you require it.
     * @param entity  an instance of the entity, fields will be overridden)
     * @param <T>     the type of entity
     * @return the Entity with the data extracted by the cursor
     */
    public static <T> T cursorToEntity(Cursor cursor, T entity) {
        return cursorToEntity(cursor, entity, getAllColumnsSet(entity.getClass(), true, true));
    }

    /**
     * Convert a {@link android.database.Cursor} into an Entity instance. Only the given set of columns will be read from the cursor.
     * <p/>
     * If the class of the entityRow is not annotated with {@link com.danielesegato.adme.annotation.ADMEEntity}
     * this method will throw a RuntimeException.
     * <p/>
     * Any missing column will be ignored.
     *
     * @param cursor  The Cursor containing the data, it will not
     *                be cleared, it's the caller job to do so if you require it.
     * @param clazz   The class of the instance to be created, it must have a public empty constructor
     * @param columns The set of columns to extract from the Cursor, any missing column will be ignored.
     * @param <T>     the type of entity
     * @return the Entity with the data extracted by the cursor
     */
    private static <T> T cursorToEntity(Cursor cursor, Class<T> clazz, Set<String> columns) {
        try {
            T entity = clazz.newInstance();
            return cursorToEntity(cursor, entity, columns);
        } catch (InstantiationException e) {
            throw new RuntimeException(String.format("the instance for class %s cannot be created", clazz.getName()), e);
        } catch (IllegalAccessException e) {
            throw new RuntimeException(String.format("the default constructor for class %s is not visible", clazz.getName()), e);
        }
    }

    /**
     * Convert a {@link android.database.Cursor} into an Entity instance. Only the given set of columns will be read from the cursor.
     * <p/>
     * If the class of the entityRow is not annotated with {@link com.danielesegato.adme.annotation.ADMEEntity}
     * this method will throw a RuntimeException.
     * <p/>
     * Any missing column will be ignored.
     *
     * @param cursor  The Cursor containing the data, it will not
     *                be cleared, it's the caller job to do so if you require it.
     * @param entity  an instance of the entity, fields will be overridden)
     * @param columns The set of columns to extract from the Cursor, any missing column will be ignored.
     * @param <T>     the type of entity
     * @return the Entity with the data extracted by the cursor
     */
    public static <T> T cursorToEntity(Cursor cursor, T entity, Set<String> columns) {
        final ADMEEntityConfig<T> entityConfig = ADMEConfigUtils.lookupADMEEntityConfig((Class<T>)entity.getClass());
        try {
            for (final ADMEFieldConfig fieldConfig : entityConfig.getFieldsConfig()) {
                if (!columns.contains(fieldConfig.getColumnName())) {
                    continue;
                }
                final Field field;
                Object instance;
                if (!fieldConfig.isForeign()) {
                    field = fieldConfig.getJavaField();
                    instance = entity;
                } else {
                    field = fieldConfig.getForeignFieldConfig().getJavaField();
                    instance = fieldConfig.getJavaField().get(entity);
                    if (instance == null) {
                        try {
                            instance = fieldConfig.getJavaField().getType().newInstance();
                        } catch (InstantiationException e) {
                            throw new RuntimeException(String.format("the instance for class %s of foreign field %s in entity %s cannot be created",
                                    fieldConfig.getJavaField().getType().getName(), fieldConfig.getJavaField().getName(), entityConfig.getClass().getName()), e);
                        } catch (IllegalAccessException e) {
                            throw new RuntimeException(String.format("the default constructor for class %s of foreign field %s in entity %s is not visible",
                                    fieldConfig.getJavaField().getType().getName(), fieldConfig.getJavaField().getName(), entityConfig.getClass().getName()), e);
                        }
                        fieldConfig.getJavaField().setAccessible(true);
                        fieldConfig.getJavaField().set(entity, instance);
                    }
                }
                // TODO use set method if annotated like that?
                final ADMESerializer admeSerializer = fieldConfig.getADMESerializer();
                int columnIndex = cursor.getColumnIndex(fieldConfig.getColumnName());
                if (columnIndex >= 0) {
                    final Object fieldValue = admeSerializer.sqlToJava(cursor, columnIndex, fieldConfig);
                    field.setAccessible(true);
                    field.set(instance, fieldValue);
                }
            }
        } catch (IllegalAccessException e) {
            String msg = String.format("Error serializing entity %s, couldn't access some field, class: %s", entityConfig.getEntityName(), entity);
            Log.e(TAG, msg, e);
            throw new RuntimeException(msg, e);
        }
        return entity;
    }

    /**
     * Create the table for the entity of the entityClass. The entityClass must be annotated with
     * an {@link com.danielesegato.adme.annotation.ADMEEntity} annotation.
     * <p/>
     * This method will parse the {@link com.danielesegato.adme.config.ADMEEntityConfig} of the given
     * class if needed, then cache it (it will also parse and cache the config for every foreign class).
     * <p/>
     * Every issue in parsing the entity configuration will raise an exception.
     * <p/>
     * Every index for the table is also created.
     * <p/>
     * If the table or one index already exist an exception will be raised.
     *
     * @param db          the database in which the table should be created.
     * @param entityClass the {@link com.danielesegato.adme.annotation.ADMEEntity} annotated class.
     * @param <T>         the type of the class
     */
    public static <T> void createTable(final SQLiteDatabase db, final Class<T> entityClass) {
        ADMEEntityConfig<T> entityConfig = ADMEConfigUtils.lookupADMEEntityConfig(entityClass);
        List<String> statements = getCreateTableStatements(entityConfig);
        for (String statement : statements) {
            Log.d(TAG, String.format("Creating table for %s, executing statement: %s", entityClass.getSimpleName(), statement));
            db.execSQL(statement);
        }
        Log.i(TAG, String.format("Table for %s created SUCCESSFULLY", entityClass.getName()));
    }

    /**
     * Same as {@link #createTable(android.database.sqlite.SQLiteDatabase, Class)} but it will not
     * execute the table and indexes statements, it will return them.
     *
     * @param dbEntityConfig the {@link com.danielesegato.adme.annotation.ADMEEntity} annotated class.
     * @return the list of SQLite statements to create this table and its indexes in the database.
     */
    public static List<String> getCreateTableStatements(final ADMEEntityConfig<?> dbEntityConfig) {
        // http://www.sqlite.org/lang_createtable.html
        final List<String> statements = new ArrayList<String>();
        final StringBuilder sb = new StringBuilder();
        sb.append("CREATE TABLE ");
        SQLStringHelper.appendEscapedEntityOrField(sb, dbEntityConfig.getEntityName());
        sb.append(" (");
        appendColumnsDefinitions(sb, dbEntityConfig);
        appendEntityConstraints(sb, dbEntityConfig);
        sb.append(") ");
        statements.add(sb.toString());

        for (final ADMEIndexConstraintConfig indexConstraintConfig : dbEntityConfig.getIndexConstraintConfigList()) {
            sb.setLength(0);
            appendIndexStatement(sb, indexConstraintConfig);
            statements.add(sb.toString());
        }
        return statements;
    }

    public static <T> void dropTable(final SQLiteDatabase db, final Class<T> entityClass) {
        ADMEEntityConfig<T> entityConfig = ADMEConfigUtils.lookupADMEEntityConfig(entityClass);
        dropTable(db, entityConfig.getEntityName());
    }

    public static void dropTable(final SQLiteDatabase db, final String tableName) {
        List<String> statements = getDropTableStatements(db, tableName);
        for (String statement : statements) {
            Log.d(TAG, String.format("Dropping table for %s, executing statement: %s", tableName, statement));
            db.execSQL(statement);
        }
        Log.i(TAG, String.format("Table for %s dropped SUCCESSFULLY", tableName));
    }

    public static List<String> getDropTableStatements(final SQLiteDatabase db, final String tableName) {
        return getDropTableStatements(tableName, getTableIndexNameSet(db, tableName));
    }

    private static <T> List<String> getDropTableStatements(final String tableName, final Set<String> indexNameSet) {
        // http://www.sqlite.org/lang_droptable.html
        final List<String> statements = new ArrayList<String>();
        final StringBuilder sb = new StringBuilder();
        for (final String indexName : indexNameSet) {
            sb.append("DROP INDEX IF EXISTS ");
            SQLStringHelper.appendEscapedEntityOrField(sb, indexName);
            statements.add(sb.toString());
            sb.setLength(0);
        }
        sb.append("DROP TABLE IF EXISTS ");
        SQLStringHelper.appendEscapedEntityOrField(sb, tableName);
        statements.add(sb.toString());
        return statements;
    }

    /**
     * Query the database to obtain the set of index name associated to a given table.
     *
     * @param db        the database in to query
     * @param tableName the table name
     * @return the set of index name
     */
    public static Set<String> getTableIndexNameSet(final SQLiteDatabase db, final String tableName) {
        Set<String> indexNameSet = new HashSet<String>();
        Cursor c = db.rawQuery("SELECT name FROM sqlite_master WHERE type == ? AND tbl_name == ?", new String[]{"index", tableName});
        while (c.moveToNext()) {
            final String indexName = c.getString(0);
            if (!indexName.startsWith("sqlite_autoindex_")) {
                indexNameSet.add(c.getString(0));
            }
        }
        c.close();
        return indexNameSet;
    }

    private static StringBuilder appendColumnsDefinitions(final StringBuilder sb, final ADMEEntityConfig<?> dbEntityConfig) {
        boolean first = true;
        for (ADMEFieldConfig fieldConfig : dbEntityConfig.getFieldsConfig()) {
            if (first) {
                first = false;
            } else {
                sb.append(", ");
            }
            appendColumnDefinition(sb, fieldConfig);
        }
        return sb;
    }

    private static void appendColumnDefinition(final StringBuilder sb, final ADMEFieldConfig fieldConfig) {
        SQLStringHelper.appendEscapedEntityOrField(sb, fieldConfig.getColumnName()).append(' ');

        ADMESerializer admeSerializer = fieldConfig.getADMESerializer();
        switch (admeSerializer.getSQLiteType()) {
            case INTEGER:
                sb.append("INTEGER");
                break;
            case TEXT:
                sb.append("TEXT");
                break;
            case REAL:
                sb.append("REAL");
                break;
            case NUMERIC:
                sb.append("NUMERIC");
                break;
            case NONE:
                sb.append("NONE");
                break;
            default:
                throw new UnsupportedOperationException(String.format(
                        "SQLiteType %s Unknown or not supported for field %s in entity %s",
                        admeSerializer.getSQLiteType(), fieldConfig.getColumnName(), fieldConfig.getADMEEntityConfig().getEntityName()
                ));
        }
        sb.append(' ');

        if (fieldConfig.isId()) {
            sb.append("PRIMARY KEY ");
            if (fieldConfig.isGeneratedId()) {
                sb.append(" AUTOINCREMENT ");
            }
        } else {
            if (fieldConfig.isNullable()) {
                sb.append("NULL ");
            } else {
                sb.append("NON NULL ");
            }
            if (fieldConfig.getIndexConstraint() != null && fieldConfig.getIndexConstraint().isUnique()) {
                sb.append("UNIQUE ");
            }
            if (fieldConfig.getDefaultValue() != null) {
                sb.append("DEFAULT ");
                sb.append(admeSerializer.stringToSqlRaw(fieldConfig.getDefaultValue(), fieldConfig));
            }
        }
    }

    private static StringBuilder appendEntityConstraints(final StringBuilder sb, final ADMEEntityConfig<?> dbEntityConfig) {
        for (final ADMEIndexConstraintConfig indexConstraintConfig : dbEntityConfig.getIndexConstraintConfigList()) {
            if (!indexConstraintConfig.isSingleField()) {
                sb.append(", UNIQUE (");
                boolean first = true;
                for (ADMEFieldConfig fieldConfig : indexConstraintConfig.getFields()) {
                    if (first) {
                        first = false;
                    } else {
                        sb.append(", ");
                    }
                    SQLStringHelper.appendEscapedEntityOrField(sb, fieldConfig.getColumnName());
                }
                sb.append(")");
            }
        }
        for (final ADMEFieldConfig fieldConfig : dbEntityConfig.getFieldsConfig()) {
            if (fieldConfig.isForeign()) {
                final ADMEFieldConfig foreignFieldConfig = fieldConfig.getForeignFieldConfig();
                sb.append(", FOREIGN KEY (");
                SQLStringHelper.appendEscapedEntityOrField(sb, fieldConfig.getColumnName());
                sb.append(") REFERENCES ");
                SQLStringHelper.appendEscapedEntityOrField(sb, foreignFieldConfig.getADMEEntityConfig().getEntityName());
                if (fieldConfig.getForeignOnDelete() != OnForeignUpdateDelete.NO_ACTION) {
                    sb.append("ON DELETE ").append(fieldConfig.getForeignOnDelete().sql()).append(' ');
                }
                if (fieldConfig.getForeignOnUpdate() != OnForeignUpdateDelete.NO_ACTION) {
                    sb.append("ON UPDATE ").append(fieldConfig.getForeignOnUpdate().sql()).append(' ');
                }
            }
        }
        return sb;
    }

    private static StringBuilder appendIndexStatement(final StringBuilder sb, final ADMEIndexConstraintConfig indexConstraintConfig) {
        // http://www.sqlite.org/lang_createindex.html
        sb.append("CREATE ");
        if (indexConstraintConfig.isUnique()) {
            sb.append("UNIQUE ");
        }
        sb.append("INDEX ");
        SQLStringHelper.appendEscapedEntityOrField(sb, indexConstraintConfig.getIndexName());
        sb.append(" ON ");
        SQLStringHelper.appendEscapedEntityOrField(sb, indexConstraintConfig.getADMEEntityConfig().getEntityName());
        sb.append(" (");
        boolean first = true;
        for (final ADMEFieldConfig fieldConfig : indexConstraintConfig.getFields()) {
            if (first) {
                first = false;
            } else {
                sb.append(", ");
            }
            SQLStringHelper.appendEscapedEntityOrField(sb, fieldConfig.getColumnName());
        }
        sb.append(") ");
        return sb;
    }

    /**
     * Register a custom {@link com.danielesegato.adme.db.ADMESerializer} for a {@link java.lang.Class}.
     * Registering a serializer makes the use of {@link com.danielesegato.adme.annotation.ADMEField} annotation
     * on the custom class possible.
     * <p/>
     * You can also register a custom serializer for the data types already handled by the system, your
     * serializer will override the default one.
     *
     * @param clazz      the custom class type
     * @param serializer the custom serializer
     */
    public static void registerADMESerializer(final Class<?> clazz, final ADMESerializer serializer) {
        ADMESerializerMapping.registerSerializer(clazz, serializer);
    }

    /**
     * Unregister any custom {@link com.danielesegato.adme.db.ADMESerializer} associated to the given
     * {@link java.lang.Class}.
     *
     * @param clazz the custom class type
     */
    public static void unregisterADMESerializer(final Class<?> clazz) {
        ADMESerializerMapping.unregisterSerializer(clazz);
    }
}




Java Source Code List

com.danielesegato.adme.ADME.java
com.danielesegato.adme.annotation.ADMEEntity.java
com.danielesegato.adme.annotation.ADMEField.java
com.danielesegato.adme.annotation.ADMEIndexConstraint.java
com.danielesegato.adme.config.ADMEConfigUtils.java
com.danielesegato.adme.config.ADMEEntityConfig.java
com.danielesegato.adme.config.ADMEFieldConfig.java
com.danielesegato.adme.config.ADMEIndexConstraintConfig.java
com.danielesegato.adme.config.OnForeignUpdateDelete.java
com.danielesegato.adme.config.SQLiteType.java
com.danielesegato.adme.db.ADMESerializerMapping.java
com.danielesegato.adme.db.ADMESerializer.java
com.danielesegato.adme.db.ContentProviderUris.java
com.danielesegato.adme.db.SQLiteContentProvider.java
com.danielesegato.adme.db.serializer.BaseADMESerializer.java
com.danielesegato.adme.db.serializer.BigDecimalADMESerializer.java
com.danielesegato.adme.db.serializer.BooleanADMESerializer.java
com.danielesegato.adme.db.serializer.BooleanObjectADMESerializer.java
com.danielesegato.adme.db.serializer.CurrencyADMESerializer.java
com.danielesegato.adme.db.serializer.DateAsStringADMESerializer.java
com.danielesegato.adme.db.serializer.DateAsTimestampADMESerializer.java
com.danielesegato.adme.db.serializer.DoubleADMESerializer.java
com.danielesegato.adme.db.serializer.DoubleObjectADMESerializer.java
com.danielesegato.adme.db.serializer.EnumIntADMESerializer.java
com.danielesegato.adme.db.serializer.EnumStringADMESerializer.java
com.danielesegato.adme.db.serializer.IntADMESerializer.java
com.danielesegato.adme.db.serializer.IntObjectADMESerializer.java
com.danielesegato.adme.db.serializer.LongADMESerializer.java
com.danielesegato.adme.db.serializer.LongObjectADMESerializer.java
com.danielesegato.adme.db.serializer.StringADMESerializer.java
com.danielesegato.adme.provider.ADMEContentProviderComponent.java
com.danielesegato.adme.provider.ADMEContentProvider.java
com.danielesegato.adme.utils.DateHelper.java
com.danielesegato.adme.utils.SQLStringHelper.java
com.danielesegato.adme.utils.SQLiteScriptParser.java
com.danielesegato.demo.adme.ADMEDemoMainActivity.java
com.danielesegato.demo.adme.NavigationDrawerFragment.java