package droidkit.sqlite; import android.content.ContentProvider; import android.content.ContentProviderClient; import android.content.ContentProviderOperation; import android.content.ContentResolver; import android.content.Context; import android.content.OperationApplicationException; import android.content.pm.ProviderInfo; import android.net.Uri; import android.os.RemoteException; import android.support.annotation.NonNull; import android.text.TextUtils; import java.util.ArrayList; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.atomic.AtomicReference; import droidkit.util.Dynamic; import droidkit.util.DynamicException; /** * @author Daniel Serdyukov */ public class SQLite { private static final String SUFFIX = "$SQLiteTable"; private static final AtomicReference<String> AUTHORITY_REF = new AtomicReference<>(); private static final Map<Class<?>, SQLiteTable<?>> TABLES = new ConcurrentHashMap<>(); private static final Map<Class<?>, Uri> URIS = new ConcurrentHashMap<>(); private static final int TRANSACTION_CAPACITY = 1024; private final ContentResolver mResolver; private final String mAuthority; private ArrayList<ContentProviderOperation> mOperations; SQLite(@NonNull ContentResolver resolver, @NonNull String authority) { mResolver = resolver; mAuthority = authority; } public static SQLite with(@NonNull Context context) { return new SQLite(context.getContentResolver(), AUTHORITY_REF.get()); } static void attach(@NonNull String authority) { AUTHORITY_REF.lazySet(authority); } static void attach(@NonNull ProviderInfo info) { AUTHORITY_REF.lazySet(info.authority); } @NonNull @SuppressWarnings("unchecked") static <T> SQLiteTable<T> acquireTable(@NonNull Class<?> type) { try { SQLiteTable<?> proxy = TABLES.get(type); if (proxy == null) { proxy = Dynamic.init(type.getName() + SUFFIX); TABLES.put(type, proxy); } return (SQLiteTable<T>) proxy; } catch (DynamicException e) { throw new SQLiteException(e); } } @NonNull @SuppressWarnings("unchecked") static Uri acquireUri(@NonNull Class<?> type) { Uri uri = URIS.get(type); if (uri == null) { uri = new Uri.Builder() .scheme(SQLiteProvider.SCHEME) .authority(AUTHORITY_REF.get()) .appendPath(acquireTable(type).getName()) .build(); URIS.put(type, uri); } return uri; } public void beginTransaction() { if (mOperations == null) { mOperations = new ArrayList<>(TRANSACTION_CAPACITY); } } public void rollbackTransaction() { if (mOperations != null) { removeOperations(); } } public void commitTransaction() { if (mOperations != null) { try { mResolver.applyBatch(mAuthority, mOperations); } catch (RemoteException | OperationApplicationException e) { throw new SQLiteException(e); } removeOperations(); } } private void removeOperations() { mOperations.clear(); mOperations = null; } @NonNull public SQLite insert(@NonNull Object object) { final Class<?> type = object.getClass(); if (mOperations != null) { acquireTable(type).insert(mOperations, acquireUri(type), object); } else { acquireTable(type).insert(mResolver, acquireUri(type), object); } return this; } @NonNull public SQLite update(@NonNull Object object) { final Class<?> type = object.getClass(); if (mOperations != null) { acquireTable(type).update(mOperations, acquireUri(type), object); } else { acquireTable(type).update(mResolver, acquireUri(type), object); } return this; } @NonNull public SQLite delete(@NonNull Object object) { final Class<?> type = object.getClass(); if (mOperations != null) { acquireTable(type).delete(mOperations, acquireUri(type), object); } else { acquireTable(type).delete(mResolver, acquireUri(type), object); } return this; } @NonNull public <T> SQLiteQuery<T> where(@NonNull Class<T> type) { return new SQLiteQuery<>(mResolver, acquireUri(type), SQLite.<T>acquireTable(type)); } @NonNull public <T> SQLiteResult<T> all(@NonNull Class<T> type) { return where(type).all(); } public <T> int truncate(@NonNull Class<T> type) { return mResolver.delete(acquireUri(type), null, null); } public void clearDatabase() { final String authority = AUTHORITY_REF.get(); if (!TextUtils.isEmpty(authority)) { final ContentProviderClient providerClient = mResolver.acquireContentProviderClient(authority); if (providerClient != null) { try { final ContentProvider provider = providerClient.getLocalContentProvider(); if (provider instanceof SQLiteProvider) { ((SQLiteProvider) provider).clearDatabase(); } } finally { providerClient.release(); } } } } }