Back to project page androidata.
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.
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(); }