/*
* Copyright (C) 2012-2016 The Android Money Manager Ex Project Team
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 3
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.money.manager.ex.datalayer;
import android.content.ContentProviderOperation;
import android.content.ContentProviderResult;
import android.content.ContentUris;
import android.content.ContentValues;
import android.content.Context;
import android.content.OperationApplicationException;
import android.database.Cursor;
import android.database.sqlite.SQLiteDiskIOException;
import android.net.Uri;
import android.os.RemoteException;
import com.money.manager.ex.Constants;
import com.money.manager.ex.MmxContentProvider;
import com.money.manager.ex.database.Dataset;
import com.money.manager.ex.database.DatasetType;
import com.money.manager.ex.domainmodel.AssetClass;
import com.money.manager.ex.domainmodel.EntityBase;
import java.util.ArrayList;
import java.util.List;
import timber.log.Timber;
/**
* Contains common code for repositories.
*/
public abstract class RepositoryBase<T extends EntityBase>
extends Dataset {
public RepositoryBase(Context context, String source, DatasetType type, String basePath) {
super(source, type, basePath);
this.context = context.getApplicationContext();
}
private Context context;
public int count(String selection, String[] args) {
Cursor c = openCursor(null, selection, args);
if (c == null) return Constants.NOT_SET;
int result = c.getCount();
c.close();
return result;
}
public Context getContext() {
return this.context;
}
public Cursor openCursor(String[] projection, String selection, String[] args) {
return openCursor(projection, selection, args, null);
}
public Cursor openCursor(String[] projection, String selection, String[] args, String sort) {
try {
Cursor cursor = getContext().getContentResolver().query(getUri(),
projection,
selection,
args,
sort);
return cursor;
} catch (SQLiteDiskIOException ex) {
Timber.e(ex, "querying database");
return null;
}
}
public int add(EntityBase entity) {
return insert(entity.contentValues);
}
/**
* Fetch only the first result
* @param resultType
* @param projection
* @param selection
* @param args
* @param sort Sort order to apply to the query results from which the first will be returned.
* @return
*/
public T first(Class<T> resultType, String[] projection, String selection, String[] args, String sort) {
T entity = null;
try {
Cursor c = openCursor(projection, selection, args, sort);
if (c == null) return null;
if (c.moveToNext()) {
try {
entity = resultType.newInstance();
//resultType.cast(entity);
entity.loadFromCursor(c);
} catch (Exception e) {
Timber.e(e, "creating %s", resultType.getName());
}
}
c.close();
} catch (Exception ex) {
Timber.e(ex, "fetching first record");
}
return entity;
}
public List<T> query(Class<T> resultType, Select query) {
// String[] projection, String selection, String[] args, String sort
Cursor c = openCursor(query.projection, query.selection, query.selectionArgs, query.sort);
if (c == null) return null;
List<T> results = new ArrayList<>();
//T entity = null;
while (c.moveToNext()) {
try {
T entity = resultType.newInstance();
entity.loadFromCursor(c);
results.add(entity);
} catch (Exception e) {
Timber.e(e, "creating %s", resultType.getName());
}
}
c.close();
return results;
}
// Protected
protected int bulkInsert(ContentValues[] items) {
return getContext().getContentResolver().bulkInsert(this.getUri(), items);
}
/**
* Generic insert method.
*/
protected int insert(ContentValues values) {
// sanitize
values.remove("_id");
Uri insertUri = getContext().getContentResolver().insert(this.getUri(), values);
if (insertUri == null) return Constants.NOT_SET;
long id = ContentUris.parseId(insertUri);
return (int) id;
}
protected List<T> query(Class<T> resultType, String selection) {
Select query = new Select().where(selection);
return query(resultType, query);
}
/**
* Generic update method.
* @param entity Entity values to store.
* @param where Condition for entity selection.
* @return Boolean indicating whether the operation was successful.
*/
protected boolean update(EntityBase entity, String where) {
return update(entity, where, null);
}
protected boolean update(EntityBase entity, String where, String[] selectionArgs) {
boolean result = false;
ContentValues values = entity.contentValues;
// remove "_id" from the values.
values.remove("_id");
int updateResult = getContext().getContentResolver().update(this.getUri(),
values,
where,
selectionArgs
);
if (updateResult != 0) {
result = true;
} else {
Timber.w("update failed, %s, values: %s", this.getUri(), entity.contentValues);
}
return result;
}
/**
* Warning: this works only with Asset Class entities!
* Ref:
* http://www.grokkingandroid.com/better-performance-with-contentprovideroperation/
* http://www.grokkingandroid.com/android-tutorial-using-content-providers/
* @param entities array of entities to update in a transaction
* @return results of the bulk update
*/
protected ContentProviderResult[] bulkUpdate(EntityBase[] entities) {
ArrayList<ContentProviderOperation> operations = new ArrayList<>();
for (EntityBase entity : entities) {
AssetClass assetClass = (AssetClass) entity;
operations.add(ContentProviderOperation.newUpdate(this.getUri())
.withValues(entity.contentValues)
.withSelection(AssetClass.ID + "=?", new String[] {Integer.toString(assetClass.getId())})
.build());
}
ContentProviderResult[] results = null;
try {
results = getContext().getContentResolver()
.applyBatch(MmxContentProvider.getAuthority(), operations);
} catch (RemoteException | OperationApplicationException e) {
Timber.e(e, "bulk updating");
}
return results;
}
protected int delete(String where, String[] args) {
int result = getContext().getContentResolver().delete(this.getUri(),
where,
args
);
return result;
}
protected ContentProviderResult[] bulkDelete(List<Integer> ids) {
ArrayList<ContentProviderOperation> operations = new ArrayList<>();
for (int id : ids) {
operations.add(ContentProviderOperation.newDelete(this.getUri())
// .withValues(entity.contentValues)
.withSelection(AssetClass.ID + "=?", new String[]{Integer.toString(id)})
.build());
}
ContentProviderResult[] results = null;
try {
results = getContext().getContentResolver()
.applyBatch(MmxContentProvider.getAuthority(), operations);
} catch (RemoteException | OperationApplicationException e) {
Timber.e(e, "bulk updating");
}
return results;
}
}