Android Open Source - bamboo-storage Bamboo Storage






From Project

Back to project page bamboo-storage.

License

The source code is released under:

MIT License

If you think the Android project bamboo-storage 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.pushtorefresh.bamboostorage;
/*from w  w w . j a v a 2  s.c o  m*/
import android.content.ContentResolver;
import android.content.ContentUris;
import android.content.Context;
import android.content.res.Resources;
import android.database.ContentObserver;
import android.database.Cursor;
import android.net.Uri;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;

import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

/**
 * @author Artem Zinnatullin [artem.zinnatullin@gmail.com]
 */
public class BambooStorage {

    /**
     * Thread-safe cache of (StorableItemClass, BambooStorableTypeMeta) pairs for better performance
     */
    private static final Map<Class<? extends IBambooStorableItem>, BambooStorableTypeMetaWithExtra> CACHE_OF_STORABLE_ITEM_TYPES_AND_THEIR_META = new ConcurrentHashMap<Class<? extends IBambooStorableItem>, BambooStorableTypeMetaWithExtra>();

    /**
     * Content path string for building uris for requests
     */
    private final String mContentPath;

    /**
     * Content resolver for work with content provider
     */
    private final ContentResolver mContentResolver;

    /**
     * Resources for StorableItem.toContentValues() calls
     */
    private final Resources mResources;

    /**
     * Notifier
     */
    private final IBambooStorageNotifier mNotifier;

    /**
     * Creates BambooStorage object
     *
     * @param context                  feel free to pass any context, no memory leaks. Required to work with ContentResolver and provide Resources to IBambooStorableItem.toContentValues(res)
     * @param contentProviderAuthority authority of your ContentProvider
     */
    public BambooStorage(@NonNull Context context, @NonNull String contentProviderAuthority) {
        this(context, contentProviderAuthority, new BambooStorageDefaultNotifier());
    }

    /**
     * Creates BambooStorage object
     *
     * @param context                  feel free to pass any context, no memory leaks. Required to work with ContentResolver and provide Resources to IBambooStorableItem.toContentValues(res)
     * @param contentProviderAuthority authority of your ContentProvider
     * @param notifier                 for notifying BambooStorage listeners
     */
    public BambooStorage(@NonNull Context context, @NonNull String contentProviderAuthority, @NonNull IBambooStorageNotifier notifier) {
        mContentPath = "content://" + contentProviderAuthority + "/%s";
        mContentResolver = context.getContentResolver();
        mResources = context.getResources();
        mNotifier = notifier;
    }

    /**
     * Adds listener to the BambooStorage
     *
     * @param listener listener to add
     */
    public void addListener(@NonNull ABambooStorageListener listener) {
        mNotifier.addListener(listener);
    }

    /**
     * Removes listener from the BambooStorage
     *
     * @param listener listener to remove
     */
    public void removeListener(@NonNull ABambooStorageListener listener) {
        mNotifier.removeListener(listener);
    }

    /**
     * Adds storableItem to the storage and notify storage listeners about that
     *
     * @param storableItem to add
     */
    public void add(@NonNull IBambooStorableItem storableItem) {
        add(storableItem, true);
    }

    /**
     * Adds storableItem to the storage
     *
     * @param storableItem item to add
     * @param notify       true if notifier should notify listeners, false otherwise
     */
    public void add(@NonNull IBambooStorableItem storableItem, boolean notify) {
        storableItem.setInternalId(IBambooStorableItem.DEFAULT_INTERNAL_ITEM_ID);
        Uri uri = mContentResolver.insert(buildUri(storableItem.getClass()), storableItem.toContentValues(mResources));
        storableItem.setInternalId(ContentUris.parseId(uri));

        if (notify) {
            mNotifier.notifyAboutAdd(storableItem);
        }
    }

    /**
     * Updates storable item in the storage
     *
     * @param storableItem to update
     * @return count of updated items
     * @throws IllegalArgumentException if storable item internal id less or equals zero  it was not stored in StorageManager
     */
    public int update(@NonNull IBambooStorableItem storableItem) {
        return update(storableItem, true);
    }

    /**
     * Updates storable item in the storage
     *
     * @param storableItem to update
     * @param notify       true if notifier should notify listeners, false otherwise
     * @return count of updated items
     * @throws IllegalArgumentException if storable item internal id less or equals zero  it was not stored in StorageManager
     */
    public int update(@NonNull IBambooStorableItem storableItem, boolean notify) {
        final long itemInternalId = storableItem.getInternalId();

        if (itemInternalId <= 0) {
            throw new IllegalArgumentException("Item: " + storableItem + " can not be updated, because its internal id is <= 0");
        } else {
            final Class<? extends IBambooStorableItem> classOfStorableItem = storableItem.getClass();

            final int count = mContentResolver.update(
                    buildUri(classOfStorableItem),
                    storableItem.toContentValues(mResources),
                    getTypeMetaWithExtra(classOfStorableItem).whereById,
                    buildWhereArgsByInternalId(storableItem)
            );

            if (notify) {
                mNotifier.notifyAboutUpdate(storableItem, count);
            }

            return count;
        }
    }

    /**
     * Adds or updates storable item to/in storage with notifying listeners
     *
     * @param storableItem to add or update
     * @return true if item was added, false if item was updated
     */
    public boolean addOrUpdate(@NonNull IBambooStorableItem storableItem) {
        return addOrUpdate(storableItem, true);
    }

    /**
     * Adds or updates storable item to/in storage
     *
     * @param storableItem to add or update
     * @param notify       true if notifier should notify listeners, false otherwise
     * @return true if item was added, false if item was updated
     */
    public boolean addOrUpdate(@NonNull IBambooStorableItem storableItem, boolean notify) {
        if (storableItem.getInternalId() <= 0) {
            // item was not stored in the storage
            add(storableItem, notify);
            return true;
        } else {
            update(storableItem, notify);
            return false;
        }
    }

    /**
     * Adds collection of items to the storage
     *
     * NOTICE try to avoid adding collections with multiple classes because storage listener will receive onAnyCRUDOperation only about first item's class
     *
     * @param storableItems collection
     * @param notify        true if notifier should notify listeners, false otherwise
     */
    public void addAll(@NonNull Collection<? extends IBambooStorableItem> storableItems, boolean notify) {
        if (storableItems.isEmpty()) {
            return;
        }

        for (IBambooStorableItem storableItem : storableItems) {
            if (storableItem != null) {
                add(storableItem, false);
            }
        }

        if (notify) {
            mNotifier.notifyAboutAddAll(storableItems);
        }
    }

    /**
     * Adds collection of items to the storage with notifying listeners
     *
     * NOTICE try to avoid adding collections with multiple classes because storage listener will receive onAnyCRUDOperation only about first item's class
     *
     * @param storableItems collection
     */
    public void addAll(@NonNull Collection<? extends IBambooStorableItem> storableItems) {
        addAll(storableItems, true);
    }

    /**
     * Adds or updates all items from collection to the storage
     *
     * NOTICE try to avoid adding collections with multiple classes because storage listener will receive onAnyCRUDOperation only about first item's class
     *
     * @param storableItems collection of items
     * @param notify        true if notifier should notify listeners, false otherwise
     */
    public void addOrUpdateAll(@NonNull Collection<? extends IBambooStorableItem> storableItems, boolean notify) {
        if (storableItems.isEmpty()) {
            return;
        }

        for (IBambooStorableItem storableItem : storableItems) {
            if (storableItem != null) {
                addOrUpdate(storableItem, false);
            }
        }

        if (notify) {
            mNotifier.notifyAboutAddOrUpdateAll(storableItems);
        }
    }

    /**
     * Adds or updates all items from collection to the storage with notifying listeners
     *
     * NOTICE try to avoid adding collections with multiple classes because storage listener will receive onAnyCRUDOperation only about first item's class
     *
     * @param storableItems collection of items
     */
    public void addOrUpdateAll(@NonNull Collection<? extends IBambooStorableItem> storableItems) {
        addOrUpdateAll(storableItems, true);
    }

    /**
     * Gets stored item by its internal id
     *
     * @param classOfStorableItem class of storable item
     * @param itemInternalId      internal id of the item you want to getByInternalId from storage
     * @param <T>                 generic type of StorableItem
     * @return storable item with required internal id or null if storage does not contain this item
     */
    @Nullable
    public <T extends IBambooStorableItem> T getByInternalId(@NonNull Class<T> classOfStorableItem, long itemInternalId) {
        final Cursor cursor = getAsCursor(
                classOfStorableItem,
                getTypeMetaWithExtra(classOfStorableItem).whereById,
                buildWhereArgsByInternalId(itemInternalId),
                null
        );

        try {
            if (cursor != null && cursor.moveToFirst()) {
                return createStorableItemFromCursor(classOfStorableItem, cursor);
            } else {
                return null;
            }
        } finally {
            if (cursor != null) {
                cursor.close();
            }
        }
    }

    /**
     * Gets all stored items of required type which satisfies where condition as list in memory
     *
     * @param classOfStorableItem class of StorableItem
     * @param where               where clause
     * @param whereArgs           args for binding to where clause, same format as for ContentResolver
     * @param orderBy             order by clause
     * @param <T>                 generic type of StorableItem
     * @return list of StorableItems, can be empty but not null
     */
    @NonNull
    public <T extends IBambooStorableItem> List<T> getAsList(@NonNull Class<T> classOfStorableItem, @Nullable String where, @Nullable String[] whereArgs, @Nullable String orderBy) {
        final Cursor cursor = getAsCursor(classOfStorableItem, where, whereArgs, orderBy);

        try {
            final List<T> list = new ArrayList<T>(cursor == null ? 0 : cursor.getCount());

            if (cursor == null || !cursor.moveToFirst()) {
                return list;
            }

            final String internalIdFieldName = getTypeMetaWithExtra(classOfStorableItem).typeMeta.internalIdFieldName();

            do {
                list.add(createStorableItemFromCursor(classOfStorableItem, internalIdFieldName, cursor));
            } while (cursor.moveToNext());

            return list;
        } finally {
            if (cursor != null) {
                cursor.close();
            }
        }
    }

    /**
     * Gets all stored items of required type which satisfies where condition as list in memory
     *
     * NOTICE: orderBy is null, so it's default for your type of storage in ContentProvider
     *
     * @param classOfStorableItem class of StorableItem
     * @param where               where clause
     * @param whereArgs           args for binding to where clause, same format as for ContentResolver
     * @param <T>                 generic type of StorableItem
     * @return list of StorableItems, can be empty but not null
     */
    public <T extends IBambooStorableItem> List<T> getAsList(@NonNull Class<T> classOfStorableItem, @Nullable String where, @Nullable String[] whereArgs) {
        return getAsList(classOfStorableItem, where, whereArgs, null);
    }

    /**
     * Gets all stored items of required type with default order as list in memory
     *
     * @param classOfStorableItem class of StorableItem
     * @param <T>                 generic type of StorableItem
     * @return list of StorableItems, can be empty but not null
     */
    @NonNull
    public <T extends IBambooStorableItem> List<T> getAsList(@NonNull Class<T> classOfStorableItem) {
        return getAsList(classOfStorableItem, null, null);
    }

    /**
     * Gets all stored items of required type which satisfies where condition as cursor
     *
     * @param classOfStorableItem class of StorableItem
     * @param where               where clause
     * @param whereArgs           args for binding to where clause, same format as for ContentResolver
     * @param orderBy             order by clause
     * @return cursor with query result, can be null
     */
    @Nullable
    public Cursor getAsCursor(@NonNull Class<? extends IBambooStorableItem> classOfStorableItem, @Nullable String where, @Nullable String[] whereArgs, @Nullable String orderBy) {
        return mContentResolver.query(
                buildUri(classOfStorableItem),
                null,
                where,
                whereArgs,
                orderBy
        );
    }

    /**
     * Gets first item from the query result
     *
     * @param classOfStorableItem class of StorableItem
     * @param where               where clause
     * @param whereArgs           args for binding to where clause, same format as for ContentResolver
     * @param orderBy             order by clause
     * @param <T>                 generic type of StorableItem
     * @return first item in the query result or null if there are no query results
     */
    @Nullable
    public <T extends IBambooStorableItem> T getFirst(@NonNull Class<T> classOfStorableItem, @Nullable String where, @Nullable String[] whereArgs, @Nullable String orderBy) {
        final Cursor cursor = getAsCursor(classOfStorableItem, where, whereArgs, orderBy);

        try {
            if (cursor != null && cursor.moveToFirst()) {
                return createStorableItemFromCursor(classOfStorableItem, cursor);
            } else {
                return null;
            }
        } finally {
            if (cursor != null) {
                cursor.close();
            }
        }
    }

    /**
     * Gets first item of required type with default order
     *
     * @param classOfStorableItem class of StorableItem
     * @param <T>                 generic type of StorableItem
     * @return first item of required type, can be null
     */
    @Nullable
    public <T extends IBambooStorableItem> T getFirst(@NonNull Class<T> classOfStorableItem) {
        return getFirst(classOfStorableItem, null, null, null);
    }

    /**
     * Gets last item from the query result
     *
     * It's pretty fast implementation based on cursor using, it's memory and speed efficiently
     *
     * @param classOfStorableItem class of StorableItem
     * @param where               where clause
     * @param whereArgs           args for binding to where clause, same format as for ContentResolver
     * @param orderBy             order by clause
     * @param <T>                 generic type of StorableItem
     * @return last item in the query result or null if there are no query results
     */
    @Nullable
    public <T extends IBambooStorableItem> T getLast(@NonNull Class<T> classOfStorableItem, @Nullable String where, @Nullable String[] whereArgs, @Nullable String orderBy) {
        final Cursor cursor = getAsCursor(classOfStorableItem, where, whereArgs, orderBy);

        try {
            if (cursor != null && cursor.moveToFirst()) {

                while (true) {
                    if (!cursor.moveToNext()) {
                        break;
                    }
                }

                cursor.moveToPrevious();

                return createStorableItemFromCursor(classOfStorableItem, cursor);
            } else {
                return null;
            }
        } finally {
            if (cursor != null) {
                cursor.close();
            }
        }
    }

    /**
     * Gets last item of required type with default order
     *
     * @param classOfStorableItem class of StorableItem
     * @param <T>                 generic type of StorableItem
     * @return last item of required type, can be null
     */
    @Nullable
    public <T extends IBambooStorableItem> T getLast(@NonNull Class<T> classOfStorableItem) {
        return getLast(classOfStorableItem, null, null, null);
    }

    /**
     * Removes storable item from storage
     *
     * @param storableItem item to be removed from the storage
     * @return count of removed items, it could be 0, or 1, or greater than 1 if you have items with same internal id in the storage
     */
    public int remove(@NonNull IBambooStorableItem storableItem) {
        final Class<? extends IBambooStorableItem> classOfStorableItem = storableItem.getClass();

        final int count = mContentResolver.delete(
                buildUri(classOfStorableItem),
                getTypeMetaWithExtra(classOfStorableItem).whereById,
                buildWhereArgsByInternalId(storableItem)
        );

        mNotifier.notifyAboutRemove(storableItem, count);

        return count;
    }

    /**
     * Removes all storable items of required type which matched where condition
     *
     * @param classOfStorableItems type of storable item you want to delete
     * @param where                where clause
     * @param whereArgs            args for binding to where clause, same format as for ContentResolver
     * @return count of removed items
     */
    public int remove(@NonNull Class<? extends IBambooStorableItem> classOfStorableItems, @Nullable String where, @Nullable String[] whereArgs) {
        final int count = mContentResolver.delete(buildUri(classOfStorableItems), where, whereArgs);
        mNotifier.notifyAboutRemove(classOfStorableItems, where, whereArgs, count);
        return count;
    }

    /**
     * Removes all storable items of required type
     * Same as calling remove(class, null, null)
     *
     * @param classOfStorableItems type of storable item you want to delete
     * @return count of removed items
     */
    public int removeAllOfType(@NonNull Class<? extends IBambooStorableItem> classOfStorableItems) {
        final int count = mContentResolver.delete(buildUri(classOfStorableItems), null, null);
        mNotifier.notifyAboutRemoveAllOfType(classOfStorableItems, count);
        return count;
    }

    /**
     * Checks "is item currently stored in storage"
     *
     * @param storableItem to check
     * @return true if item stored in storage, false if not
     */
    public boolean contains(@NonNull IBambooStorableItem storableItem) {
        final Class<? extends IBambooStorableItem> classOfStorableItem = storableItem.getClass();

        final Cursor cursor = getAsCursor(
                classOfStorableItem,
                getTypeMetaWithExtra(classOfStorableItem).whereById,
                buildWhereArgsByInternalId(storableItem),
                null
        );

        try {
            return cursor != null && cursor.moveToFirst();
        } finally {
            if (cursor != null) {
                cursor.close();
            }
        }
    }

    /**
     * Returns count of items in the storage of required type
     *
     * @param classOfStorableItems type of storable item you want to count
     * @return count of items in the storage of required type
     */
    public int countOfItems(@NonNull Class<? extends IBambooStorableItem> classOfStorableItems) {
        Cursor cursor = getAsCursor(classOfStorableItems, null, null, null);

        try {
            return cursor != null ? cursor.getCount() : 0;
        } finally {
            if (cursor != null) {
                cursor.close();
            }
        }
    }

    /**
     * Notifying about change in the storage using content resolver notifyChange method
     *
     * @param classOfStorableItem class of StorableItem
     * @param contentObserver     the observer that originated the change, may be null
     *                            The observer that originated the change will only receive the notification if it
     *                            has requested to receive self-change notifications by implementing
     */
    public void notifyChange(@NonNull Class<? extends IBambooStorableItem> classOfStorableItem, @Nullable ContentObserver contentObserver) {
        mContentResolver.notifyChange(buildUri(classOfStorableItem), contentObserver);
    }

    /**
     * Notifying about change in the storage using content resolver notifyChange method
     *
     * @param classOfStorableItem class of StorableItem
     */
    public void notifyChange(@NonNull Class<? extends IBambooStorableItem> classOfStorableItem) {
        mContentResolver.notifyChange(buildUri(classOfStorableItem), null);
    }

    /**
     * Gets type meta with some extra from cache or directly from classOfStorableItem
     *
     * @param classOfStorableItem type of storable item to get meta info from
     * @return type meta info with some extra data
     */
    @NonNull
    private static BambooStorableTypeMetaWithExtra getTypeMetaWithExtra(@NonNull Class<? extends IBambooStorableItem> classOfStorableItem) {
        BambooStorableTypeMetaWithExtra typeMetaWithExtra = CACHE_OF_STORABLE_ITEM_TYPES_AND_THEIR_META.get(classOfStorableItem);

        // no cached value
        if (typeMetaWithExtra == null) {
            if (!classOfStorableItem.isAnnotationPresent(BambooStorableTypeMeta.class)) {
                throw new IllegalArgumentException("Class " + classOfStorableItem + " should be marked with " + BambooStorableTypeMeta.class + " annotation");
            }

            typeMetaWithExtra = new BambooStorableTypeMetaWithExtra(classOfStorableItem.getAnnotation(BambooStorableTypeMeta.class));
            CACHE_OF_STORABLE_ITEM_TYPES_AND_THEIR_META.put(classOfStorableItem, typeMetaWithExtra);
        }

        return typeMetaWithExtra;
    }

    /**
     * Builds uri for accessing content
     *
     * @param classOfStorableItem of the content to build uri
     * @return Uri for accessing content
     */
    @NonNull
    public Uri buildUri(@NonNull Class<? extends IBambooStorableItem> classOfStorableItem) {
        BambooStorableTypeMetaWithExtra typeMetaWithExtra = getTypeMetaWithExtra(classOfStorableItem);
        return Uri.parse(String.format(mContentPath, typeMetaWithExtra.typeMeta.contentPath()));
    }

    /**
     * Builds where args with storable item id for common requests to the content resolver
     *
     * @param internalStorableItemId internal id of storable item
     * @return where args
     */
    @NonNull
    private static String[] buildWhereArgsByInternalId(long internalStorableItemId) {
        return new String[]{String.valueOf(internalStorableItemId)};
    }

    /**
     * Builds where args with storable item id for common requests to the content resolver
     *
     * @param storableItem to get internal id
     * @return where args
     */
    @NonNull
    private static String[] buildWhereArgsByInternalId(@NonNull IBambooStorableItem storableItem) {
        return buildWhereArgsByInternalId(storableItem.getInternalId());
    }

    /**
     * Creates and fills storable item from cursor
     *
     * @param classOfStorableItem class of storable item to instantiate
     * @param internalIdFieldName class's internal id field name
     * @param cursor              cursor to getByInternalId fields of item
     * @param <T>                 generic type of the storable item
     * @return storable item filled with info from cursor
     * @throws IllegalArgumentException if classOfStorableItem can not be used to create item from Cursor
     */
    @NonNull
    private static <T extends IBambooStorableItem> T createStorableItemFromCursor(@NonNull Class<T> classOfStorableItem, @NonNull String internalIdFieldName, @NonNull Cursor cursor) {
        try {
            T storableItem = classOfStorableItem.newInstance();
            storableItem.setInternalId(cursor.getLong(cursor.getColumnIndex(internalIdFieldName)));
            storableItem.fillFromCursor(cursor);
            return storableItem;
        } catch (InstantiationException e) {
            throw new IllegalArgumentException(classOfStorableItem + " can not be used for createStorableItemFromCursor() because its instance can not be created by class.newInstance()");
        } catch (IllegalAccessException e) {
            throw new IllegalArgumentException(classOfStorableItem + " can not be used for createStorableItemFromCursor() because it had no default constructor or it's not public, instance can not be created by class.newInstance()");
        }
    }

    /**
     * Creates and fills storable item from cursor
     *
     * @param classOfStorableItem class of storable item to instantiate
     * @param cursor              cursor to getByInternalId fields of item
     * @param <T>                 generic type of the storable item
     * @return storable item filled with info from cursor
     * @throws IllegalArgumentException if classOfStorableItem can not be used to create item from Cursor
     */
    @NonNull
    public static <T extends IBambooStorableItem> T createStorableItemFromCursor(@NonNull Class<T> classOfStorableItem, @NonNull Cursor cursor) {
        return createStorableItemFromCursor(classOfStorableItem, getTypeMetaWithExtra(classOfStorableItem).typeMeta.internalIdFieldName(), cursor);
    }
}




Java Source Code List

com.pushtorefresh.bamboostorage.ABambooSQLiteOpenHelperContentProvider.java
com.pushtorefresh.bamboostorage.ABambooStorableItem.java
com.pushtorefresh.bamboostorage.ABambooStorageListener.java
com.pushtorefresh.bamboostorage.BambooStorableTypeMetaWithExtra.java
com.pushtorefresh.bamboostorage.BambooStorableTypeMeta.java
com.pushtorefresh.bamboostorage.BambooStorageDefaultNotifier.java
com.pushtorefresh.bamboostorage.BambooStorage.java
com.pushtorefresh.bamboostorage.IBambooStorableItem.java
com.pushtorefresh.bamboostorage.IBambooStorageNotifier.java