package org.commcare.models.database.user.models;
import android.content.ContentValues;
import android.util.Log;
import android.util.Pair;
import net.sqlcipher.Cursor;
import net.sqlcipher.database.SQLiteDatabase;
import org.commcare.CommCareApplication;
import org.commcare.models.database.AndroidTableBuilder;
import org.commcare.models.database.SqlStorage;
import org.commcare.modern.database.DatabaseHelper;
import org.commcare.modern.database.DatabaseIndexingUtils;
import java.util.Collection;
import java.util.List;
/**
* @author ctsims
*/
public class EntityStorageCache {
private static final String TAG = EntityStorageCache.class.getSimpleName();
private static final String TABLE_NAME = "entity_cache";
private static final String COL_CACHE_NAME = "cache_name";
private static final String COL_ENTITY_KEY = "entity_key";
private static final String COL_CACHE_KEY = "cache_key";
private static final String COL_VALUE = "value";
private static final String COL_TIMESTAMP = "timestamp";
private final SQLiteDatabase db;
private final String mCacheName;
public EntityStorageCache(String cacheName) {
this(cacheName, CommCareApplication.instance().getUserDbHandle());
}
public EntityStorageCache(String cacheName, SQLiteDatabase db) {
this.db = db;
this.mCacheName = cacheName;
}
public static String getTableDefinition() {
return "CREATE TABLE " + TABLE_NAME + "(" +
DatabaseHelper.ID_COL + " INTEGER PRIMARY KEY, " +
COL_CACHE_NAME + ", " +
COL_ENTITY_KEY + ", " +
COL_CACHE_KEY + ", " +
COL_VALUE + ", " +
COL_TIMESTAMP +
")";
}
public static void createIndexes(SQLiteDatabase db) {
db.execSQL(DatabaseIndexingUtils.indexOnTableCommand("CACHE_TIMESTAMP", TABLE_NAME, COL_CACHE_NAME + ", " + COL_TIMESTAMP));
db.execSQL(DatabaseIndexingUtils.indexOnTableCommand("NAME_ENTITY_KEY", TABLE_NAME, COL_CACHE_NAME + ", " + COL_ENTITY_KEY + ", " + COL_CACHE_KEY));
}
//TODO: We should do some synchronization to make it the case that nothing can hold
//an object for the same cache at once
public void cache(String entityKey, String cacheKey, String value) {
long timestamp = System.currentTimeMillis();
//TODO: this should probably just be an ON CONFLICT REPLACE call
int removed = db.delete(TABLE_NAME, COL_CACHE_NAME + " = ? AND " + COL_ENTITY_KEY + " = ? AND " + COL_CACHE_KEY + " =?", new String[]{this.mCacheName, entityKey, cacheKey});
if (SqlStorage.STORAGE_OUTPUT_DEBUG) {
System.out.println("Deleted " + removed + " cached values for existing cache value on entity " + entityKey + " on insert");
}
//We need to clear this cache value if it exists first.
ContentValues cv = new ContentValues();
cv.put(COL_CACHE_NAME, mCacheName);
cv.put(COL_ENTITY_KEY, entityKey);
cv.put(COL_CACHE_KEY, cacheKey);
cv.put(COL_VALUE, value);
cv.put(COL_TIMESTAMP, timestamp);
db.insert(TABLE_NAME, null, cv);
if (SqlStorage.STORAGE_OUTPUT_DEBUG) {
Log.d(TAG, "Cached value|" + entityKey + "|" + cacheKey);
}
}
public String retrieveCacheValue(String entityKey, String cacheKey) {
String whereClause = String.format("%s = ? AND %s = ? AND %s = ?", COL_CACHE_NAME, COL_ENTITY_KEY, COL_CACHE_KEY);
Cursor c = db.query(TABLE_NAME, new String[]{COL_VALUE}, whereClause, new String[]{mCacheName, entityKey, cacheKey}, null, null, null);
try {
if (c.moveToNext()) {
return c.getString(0);
} else {
return null;
}
} finally {
c.close();
}
}
/**
* Removes cache records associated with the provided ID
*/
public void invalidateCache(String recordId) {
int removed = db.delete(TABLE_NAME, COL_CACHE_NAME + " = ? AND " + COL_ENTITY_KEY + " = ?", new String[]{this.mCacheName, recordId});
if (SqlStorage.STORAGE_OUTPUT_DEBUG) {
Log.d(TAG, "Invalidated " + removed + " cached values for entity " + recordId);
}
}
/**
* Removes cache records associated with the provided IDs
*/
public void invalidateCaches(Collection<Integer> recordIds) {
List<Pair<String, String[]>> whereParamList = AndroidTableBuilder.sqlList(recordIds);
int removed = 0;
for(Pair<String, String[]> querySet : whereParamList) {
removed += db.delete(TABLE_NAME, COL_CACHE_NAME + " = '" + this.mCacheName + "' AND " + COL_ENTITY_KEY + " IN " + querySet.first, querySet.second);
}
if (SqlStorage.STORAGE_OUTPUT_DEBUG) {
Log.d(TAG, "Invalidated " + removed + " cached values for bulk entities");
}
}
public static int getSortFieldIdFromCacheKey(String detailId, String cacheKey) {
String intId = cacheKey.substring(detailId.length() + 1);
try {
return Integer.parseInt(intId);
} catch (NumberFormatException nfe) {
//TODO: Kill this cache key if this didn't work
return -1;
}
}
}