Android Open Source - androidata Androi Data Center






From Project

Back to project page androidata.

License

The source code is released under:

Apache License

If you think the Android project androidata 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.stanidesis.androidata;
//from  w ww  .  j av a 2  s.c  o  m
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;

import com.stanidesis.androidata.column.Column;
import com.stanidesis.androidata.model.IModelBuilder;
import com.stanidesis.androidata.model.IModelUpdateBuilder;
import com.stanidesis.androidata.model.ReadOnlyModel;
import com.stanidesis.androidata.table.ITable;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import java.util.Map;

/**
 * Created by Stanley Idesis on 8/19/14.
 */
public abstract class AndroiDataCenter<Model extends ReadOnlyModel, Table extends ITable> {

    /**
     * Required to provide this DataCenter with access to a SQLiteDatabase
     */
    public static interface DatabaseProvider {

        public SQLiteDatabase getReadOnlyDatabase();

        public SQLiteDatabase getWritableDatabase();
    }

    /**
     * Look for results in the cache
     */
    public static final int FLAG_CACHE    = 0x1;
    /**
     * Look for results in the database
     */
    public static final int FLAG_DATABASE = 0x2;
    /**
     * Either
     */
    public static final int FLAG_EITHER = FLAG_CACHE | FLAG_DATABASE;

    private final Table mTable;
    private Map<Long, Model> mModelCache;
    private DatabaseProvider mDatabaseProvider;

    void setDatabaseProvider(DatabaseProvider provider) {
        mDatabaseProvider = provider;
    }

    /**
     * Default constructor, requires Table.
     * AndroiDataCenter employs an LruCache of default max size 100.
     *
     * @param table
     */
    public AndroiDataCenter(Table table) {
        if (table == null) {
            throw new IllegalArgumentException("Table cannot be null");
        }
        mModelCache = Collections.synchronizedMap(new LruCache<Long, Model>());
        mTable = table;
    }

    /**
     * Secondary constructor, provide a cache size.
     * AndroiDataCenter employs an LruCache.
     *
     * @since v1.0.5
     * @param table
     * @param cacheSize
     */
    public AndroiDataCenter(Table table, int cacheSize) {
        if (table == null) {
            throw new IllegalArgumentException("Table cannot be null");
        } else if (cacheSize < 1) {
            throw new IllegalArgumentException("Cache size must be greater than 0");
        }
        mModelCache = Collections.synchronizedMap(new LruCache<Long, Model>(cacheSize));
        mTable = table;
    }

    /**
     * Recover an ordered List of Models. Models are retrieved based
     * on the flags provided.
     *
     * @param flags specify from where the models should be recovered
     * @return a List of Models in order
     */
    public List<Model> getOrderedModels(int flags) {
        flags = assignDefaultFlags(flags);
        if (useCacheOnly(flags)) {
            List<Model> models = new ArrayList<Model>(mModelCache.values());
            sortModels(models);
            return models;
        } else {
            long[] ignoredIds = null;
            if (useBoth(flags)) {
                Integer count = count(FLAG_DATABASE);
                if (count == count(FLAG_CACHE)) {
                    return getOrderedModels(FLAG_CACHE);
                }
                if (count(FLAG_CACHE) > 0) {
                    ignoredIds = new long[mModelCache.size()];
                    int index = 0;
                    for (Long id : mModelCache.keySet()) {
                        ignoredIds[index++] = id;
                    }
                }
            }
            Cursor cursor = getTable().selectAll(getDatabaseProvider().getReadOnlyDatabase(), ignoredIds);
            List<Model> models = listFromCursor(cursor);
            sortModels(models);
            autoClose(cursor);
            return models;
        }
    }

    /**
     * @return the table used by this DataCenter
     */
    public Table getTable() {
        return mTable;
    }

    /**
     * Recover a model for a given row Id
     *
     * @param rowId
     * @param flags
     * @return a Model representing the rowId or null if not found
     *          in the locations specified by flags
     */
    public Model getModel(long rowId, int flags) {
        flags = assignDefaultFlags(flags);
        Model result = null;
        if (useCache(flags)) {
            result = mModelCache.get(rowId);
        }
        if (useDatabase(flags) && result == null) {
            Model model = null;
            Cursor row = getTable().getRow(getDatabaseProvider().getReadOnlyDatabase(), rowId);
            if (moveToFirst(row)) {
                model = getOrCreateModel(row);
            }
            autoClose(row);
            result = model;
        }
        return result;
    }

    /**
     * @return the number of models currently in the cache
     */
    public int sizeOfCache() {
        return mModelCache.size();
    }

    /**
     * Recover the number of items managed by the data center.
     * Passing only @link{FLAG_CACHE} will recover the size of the
     * cache
     *
     * @param flags
     * @return
     */
    public int count(int flags) {
        flags = assignDefaultFlags(flags);
        if (useCacheOnly(flags)) {
            return sizeOfCache();
        }
        return getTable().count(getDatabaseProvider().getReadOnlyDatabase());
    }

    /**
     * Find a model whose column represents identical data to
     * the parameter
     *
     * @param column
     * @param data
     * @param flags
     * @return
     */
    public Model findMatchingModel(Column column, Object data, int flags) {
        flags = assignDefaultFlags(flags);
        Model result = null;
        if (useCache(flags)) {
            for (Model model : getOrderedModels(FLAG_CACHE)) {
                if (model.get(column) == data) {
                    result = model;
                }
            }
        }
        if (useDatabase(flags) && result == null) {
            Cursor cursor = getTable().find(getDatabaseProvider().getReadOnlyDatabase(), new Column[]{column},
                    new Object[]{data}, 1);
            if (moveToFirst(cursor)) {
                result = getOrCreateModel(cursor);
            }
            autoClose(cursor);
            return result;
        }
        return result;
    }

    /**
     * Return all Models whose entry for the given column matches that of data.
     * This method does not aggregate results from cache and database, strictly
     * either / or.
     *
     * @param column
     * @param data
     * @param flags
     * @return
     */
    public List<Model> findMatchingModels(Column column, Object data, int flags) {
        flags = assignDefaultFlags(flags);
        List<Model> result = new ArrayList<Model>();
        if (useCacheOnly(flags)) {
            for (Model model : getOrderedModels(FLAG_CACHE)) {
                if (model.get(column) == data) {
                    result.add(model);
                }
            }
            return result;
        } else if (useDatabase(flags)) {
            Cursor cursor = getTable().find(getDatabaseProvider().getReadOnlyDatabase(), new Column[]{column},
                    new Object[]{data}, Integer.MAX_VALUE);
            result.addAll(listFromCursor(cursor));
            autoClose(cursor);
            Collections.sort(result, modelComparatorInOrder());
        }
        return result;
    }

    /**
     * Update a model.
     *
     * @param updateBuilder
     * @return `true` if the row associated with this IModelUpdateBuilder was updated
     */
    public void updateModel(IModelUpdateBuilder updateBuilder) {
        getTable().update(getDatabaseProvider().getWritableDatabase(),
                updateBuilder.getValues(),
                updateBuilder.getRowId());
        Model model = getModel(updateBuilder.getRowId(), FLAG_CACHE);
        if (model != null) {
            model.commitUpdates(updateBuilder);
        }
    }

    /**
     * Update several models with a single IModelUpdateBuilder
     * @param updateBuilder
     * @param modelList
     */
    public void updateModels(IModelUpdateBuilder updateBuilder, List<Model> modelList) {
        long [] idsToUpdate = new long[modelList.size()];
        for (Model model : modelList) {
            idsToUpdate[modelList.indexOf(model)] = model.getRowId();
        }
        getTable().update(getDatabaseProvider().getWritableDatabase(),
                updateBuilder.getValues(),
                idsToUpdate);
        for (Model model : modelList) {
            model.commitUpdates(updateBuilder);
        }
    }

    /**
     * Update every model found in the Table represented by this
     * AndroiDataCenter.
     *
     * Every model in cache will commit to the update supplied.
     *
     * @since v1.0.5
     * @param updateBuilder
     */
    public void updateAllModels(IModelUpdateBuilder updateBuilder) {
        getTable().update(getDatabaseProvider().getWritableDatabase(), updateBuilder.getValues());
        for (Model model : mModelCache.values()) {
            model.commitUpdates(updateBuilder);
        }
    }

    /**
     * Sort the models based on this AndroiDataCenter's Comparator.
     *
     * @since v1.0.5
     * @param toSort
     */
    public void sortModels(List<Model> toSort) {
        Collections.sort(toSort, modelComparatorInOrder());
    }

    /**
     * Insert a brand new model into the table.
     *
     * @param builder
     * @return the row Id of the model
     */
    public long insert(IModelBuilder builder) {
        return getTable().insert(getDatabaseProvider().getWritableDatabase(), builder.getValues());
    }

    /**
     * Deletes a model from the underlying database, it is also removed from
     * cache.
     *
     * @since v1.0.5
     * @param modelToDelete
     */
    public void deleteModel(Model modelToDelete) {
        removeModelFromCache(modelToDelete);
        getTable().delete(getDatabaseProvider().getWritableDatabase(), new long[] {modelToDelete.getRowId()});
    }

    /**
     * Remove a model from the cache.
     *
     * @since v1.0.5
     * @param modelToRemove
     * @return true if the model was found and removed
     */
    public boolean removeModelFromCache(Model modelToRemove) {
        return mModelCache.remove(modelToRemove) != null;
    }

    /*
     * Protected Methods
     */

    protected DatabaseProvider getDatabaseProvider() {
        return mDatabaseProvider;
    }

    /**
     * This method will attempt to recover the cursor's
     * representative model from cache. If not found, a model
     * is created and saved to cache before being returned.
     *
     * @param cursor
     * @return
     */
    protected Model getOrCreateModel(Cursor cursor) {
        long rowId = cursor.getLong(cursor.getColumnIndex(getTable().getRowIDColumn().getName()));
        Model modelFromCache = getModel(rowId, FLAG_CACHE);
        if (modelFromCache == null) {
            modelFromCache = createModel(cursor);
            addToCache(modelFromCache);
        }
        return modelFromCache;
    }

    /**
     * Adds an individual model to the cache.
     *
     * @param model
     */
    protected void addToCache(Model model) {
        mModelCache.put(model.getRowId(), model);
    }

    /**
     * Checks if a cursor is present, then moves it to first
     * position.
     *
     * @param cursor
     * @return
     */
    protected boolean moveToFirst(Cursor cursor) {
        if (cursor != null) {
            return cursor.moveToFirst();
        }
        return false;
    }

    /**
     * Convenience call to close a cursor if it is
     * currently open.
     *
     * @param cursor
     */
    protected void autoClose(Cursor cursor) {
        if (cursor != null && !cursor.isClosed()) {
            cursor.close();
        }
    }

    /**
     * If no flags have been set (0), this method
     * will return the default flag option,
     * {@link #FLAG_EITHER}.
     *
     * @param flags
     * @return
     */
    protected int assignDefaultFlags(int flags) {
        if (!useCache(flags) && !useDatabase(flags)) {
            return FLAG_EITHER;
        }
        return flags;
    }

    /**
     * Iterates over a cursor and inflates models for each entry.
     * Every new entry is added to the cache before being returned.
     *
     * @param cursor
     * @return an un-ordered List of as many Models as were recovered from the cursor.
     */
    protected List<Model> listFromCursor(Cursor cursor) {
        if (!moveToFirst(cursor)) {
            return new ArrayList<Model>();
        }
        List<Model> result = new ArrayList<Model>();
        while (!cursor.isAfterLast()) {
            result.add(getOrCreateModel(cursor));
            cursor.moveToNext();
        }
        return result;
    }

    protected boolean useCache(int flags) {
        return (flags & FLAG_CACHE) == FLAG_CACHE;
    }

    protected boolean useDatabase(int flags) {
        return (flags & FLAG_DATABASE) == FLAG_DATABASE;
    }

    protected boolean useCacheOnly(int flags) {
        return useCache(flags) && !useDatabase(flags);
    }

    protected boolean useBoth(int flags) {
        return useCache(flags) && useDatabase(flags);
    }

    protected boolean useDatabaseOnly(int flags) {
        return !useCache(flags) && useDatabase(flags);
    }

    /*
     * Abstract methods
     */

    /**
     * Create a brand new model object based on the cursor and row id
     * @param cursor
     * @return
     */
    protected abstract Model createModel(Cursor cursor);

    /**
     * This method will return a comparator capable of comparing which
     * will result in ordering the models properly for display
     * @return
     */
    protected abstract Comparator<Model> modelComparatorInOrder();
}




Java Source Code List

com.stanidesis.androidata.AndroiDataCenter.java
com.stanidesis.androidata.AndroiDataUpgradeHelper.java
com.stanidesis.androidata.AndroiData.java
com.stanidesis.androidata.AndroiDatabaseOpenHelper.java
com.stanidesis.androidata.ApplicationTest.java
com.stanidesis.androidata.LruCache.java
com.stanidesis.androidata.Utils.java
com.stanidesis.androidata.column.BooleanColumn.java
com.stanidesis.androidata.column.Column.java
com.stanidesis.androidata.column.DoubleColumn.java
com.stanidesis.androidata.column.IntegerColumn.java
com.stanidesis.androidata.column.RowIDColumn.java
com.stanidesis.androidata.column.StringColumn.java
com.stanidesis.androidata.model.BaseModelBuilder.java
com.stanidesis.androidata.model.BaseModelUpdateBuilder.java
com.stanidesis.androidata.model.IModelBuilder.java
com.stanidesis.androidata.model.IModelUpdateBuilder.java
com.stanidesis.androidata.model.ReadOnlyModel.java
com.stanidesis.androidata.table.BaseTable.java
com.stanidesis.androidata.table.ITable.java