/* * Copyright (C) 2015 Actor LLC. <https://actor.im> */ package im.actor.runtime.android.storage; import android.database.Cursor; import android.database.sqlite.SQLiteDatabase; import java.util.ArrayList; import java.util.List; import im.actor.runtime.storage.ListEngineRecord; import im.actor.runtime.storage.ListStorageDisplayEx; public class SQLiteList implements ListStorageDisplayEx { private SQLiteDatabase database; private String tableName; private boolean isTableChecked = false; public SQLiteList(SQLiteDatabase database, String tableName) { this.database = database; this.tableName = tableName; } private void checkTable() { if (isTableChecked) { return; } isTableChecked = true; if (!SQLiteHelpers.isTableExists(database, tableName)) { database.execSQL("CREATE TABLE IF NOT EXISTS \"" + tableName + "\" (" + // "\"ID\" INTEGER NOT NULL," + // 0: id "\"SORT_KEY\" INTEGER NOT NULL," + // 1: sortKey "\"QUERY\" TEXT," + // 2: query "\"BYTES\" BLOB NOT NULL," + // 3: bytes "PRIMARY KEY(\"ID\"));"); // Filter index database.execSQL("CREATE INDEX IF NOT EXISTS IDX_ID_QUERY_SORT ON \"" + tableName + "\" (\"QUERY\", \"SORT_KEY\");"); // Standard index database.execSQL("CREATE INDEX IF NOT EXISTS IDX_ID_SORT ON \"" + tableName + "\" (\"SORT_KEY\");"); } } @Override public void updateOrAdd(ListEngineRecord valueContainer) { checkTable(); Object[] args = new Object[]{valueContainer.getKey(), valueContainer.getQuery() != null ? valueContainer.getQuery().toLowerCase() : null, valueContainer.getOrder(), valueContainer.getData()}; database.execSQL("REPLACE INTO \"" + tableName + "\" (\"ID\",\"QUERY\",\"SORT_KEY\",\"BYTES\") VALUES (?,?,?,?)", args); } @Override public void updateOrAdd(List<ListEngineRecord> items) { checkTable(); database.beginTransaction(); try { for (ListEngineRecord record : items) { Object[] args = new Object[]{record.getKey(), record.getQuery() != null ? record.getQuery().toLowerCase() : null, record.getOrder(), record.getData()}; database.execSQL("REPLACE INTO \"" + tableName + "\" (\"ID\",\"QUERY\",\"SORT_KEY\",\"BYTES\") VALUES (?,?,?,?)", args); } database.setTransactionSuccessful(); } finally { database.endTransaction(); } } @Override public void delete(long key) { checkTable(); Object[] args = new Object[]{key}; database.execSQL("DELETE FROM \"" + tableName + "\" WHERE \"ID\"=?", args); } @Override public void delete(long[] keys) { checkTable(); database.beginTransaction(); try { for (long key : keys) { Object[] args = new Object[]{key}; database.execSQL("DELETE FROM \"" + tableName + "\" WHERE \"ID\"=?", args); } database.setTransactionSuccessful(); } finally { database.endTransaction(); } } @Override public void clear() { checkTable(); database.execSQL("DELETE FROM \"" + tableName + "\""); } @Override public ListEngineRecord loadItem(long key) { checkTable(); Cursor cursor = database.query("\"" + tableName + "\"", new String[]{"\"ID\"", "\"SORT_KEY\"", "\"QUERY\"", "\"BYTES\""}, "\"ID\"=?", new String[]{String.valueOf(key)}, null, null, null); if (cursor == null) { return null; } try { if (cursor.moveToFirst()) { return new ListEngineRecord(key, cursor.getLong(cursor.getColumnIndex("SORT_KEY")), cursor.getString(cursor.getColumnIndex("QUERY")), cursor.getBlob(cursor.getColumnIndex("BYTES"))); } } finally { cursor.close(); } return null; } @Override public List<ListEngineRecord> loadAllItems() { return null; } public ListEngineRecord loadItemBySortKey(long key) { checkTable(); Cursor cursor = database.query("\"" + tableName + "\"", new String[]{"\"ID\"", "\"SORT_KEY\"", "\"QUERY\"", "\"BYTES\""}, "\"SORT_KEY\"=?", new String[]{String.valueOf(key)}, null, null, null); if (cursor == null) { return null; } try { if (cursor.moveToFirst()) { return new ListEngineRecord(key, cursor.getLong(cursor.getColumnIndex("SORT_KEY")), cursor.getString(cursor.getColumnIndex("QUERY")), cursor.getBlob(cursor.getColumnIndex("BYTES"))); } } finally { cursor.close(); } return null; } @Override public boolean isEmpty() { Cursor cursor = database.rawQuery("EXISTS (SELECT * FROM \"" + tableName + "\");", null); if (cursor != null) { try { return cursor.getInt(0) > 0; } finally { cursor.close(); } } return false; } @Override public List<ListEngineRecord> loadForward(Long sortingKey, int limit) { checkTable(); Cursor cursor; if (sortingKey == null) { cursor = database.query("\"" + tableName + "\"", new String[]{"\"LIST_ID\"", "\"ID\"", "\"SORT_KEY\"", "\"QUERY\"", "\"BYTES\""}, null, null, null, null, "\"SORT_KEY\" DESC", String.valueOf(limit)); } else { cursor = database.query("\"" + tableName + "\"", new String[]{"\"ID\"", "\"SORT_KEY\"", "\"QUERY\"", "\"BYTES\""}, "\"SORT_KEY\" < ?", new String[]{ String.valueOf(sortingKey) }, null, null, "\"SORT_KEY\" DESC", String.valueOf(limit)); } return loadSlice(cursor); } @Override public List<ListEngineRecord> loadBackward(Long sortingKey, int limit) { checkTable(); Cursor cursor; if (sortingKey == null) { cursor = database.query("\"" + tableName + "\"", new String[]{"\"LIST_ID\"", "\"ID\"", "\"SORT_KEY\"", "\"QUERY\"", "\"BYTES\""}, null, null, null, null, "\"SORT_KEY\" ASC", String.valueOf(limit)); } else { cursor = database.query("\"" + tableName + "\"", new String[]{"\"ID\"", "\"SORT_KEY\"", "\"QUERY\"", "\"BYTES\""}, "\"SORT_KEY\" > ?", new String[]{ String.valueOf(sortingKey) }, null, null, "\"SORT_KEY\" ASC", String.valueOf(limit)); } return loadSlice(cursor); } @Override public List<ListEngineRecord> loadForward(String query, Long sortingKey, int limit) { checkTable(); Cursor cursor; if (sortingKey == null) { cursor = database.query("\"" + tableName + "\"", new String[]{"\"LIST_ID\"", "\"ID\"", "\"SORT_KEY\"", "\"QUERY\"", "\"BYTES\""}, "\"QUERY\" LIKE ? OR \"QUERY\" LIKE ?", new String[]{ query + "%", "% " + query + "%" }, null, null, "SORT_KEY DESC", String.valueOf(limit)); } else { cursor = database.query("\"" + tableName + "\"", new String[]{"\"ID\"", "\"SORT_KEY\"", "\"QUERY\"", "\"BYTES\""}, "(\"QUERY\" LIKE ? OR \"QUERY\" LIKE ?) AND \"SORT_KEY\" < ?", new String[]{ query + "%", "% " + query + "%", String.valueOf(sortingKey) }, null, null, "\"SORT_KEY\" DESC", String.valueOf(limit)); } return loadSlice(cursor); } @Override public List<ListEngineRecord> loadCenter(Long centerSortKey, int limit) { checkTable(); Cursor cursor; if (centerSortKey == null) { cursor = database.query("\"" + tableName + "\"", new String[]{"\"LIST_ID\"", "\"ID\"", "\"SORT_KEY\"", "\"QUERY\"", "\"BYTES\""}, null, null, null, null, "\"SORT_KEY\" DESC", String.valueOf(limit)); return loadSlice(cursor); } else { ListEngineRecord centerItem = loadItemBySortKey(centerSortKey); ArrayList<ListEngineRecord> ret = new ArrayList<ListEngineRecord>(); ret.addAll(loadBackward(centerSortKey, limit)); if (centerItem != null) ret.add(centerItem); ret.addAll(loadForward(centerSortKey, limit)); return ret; } } @Override public List<ListEngineRecord> loadBackward(String query, Long sortingKey, int limit) { checkTable(); Cursor cursor; if (sortingKey == null) { cursor = database.query("\"" + tableName + "\"", new String[]{"\"LIST_ID\"", "\"ID\"", "\"SORT_KEY\"", "\"QUERY\"", "\"BYTES\""}, "\"QUERY\" LIKE ? OR \"QUERY\" LIKE ?", new String[]{ query + "%", "% " + query + "%" }, null, null, "SORT_KEY ASC", String.valueOf(limit)); } else { cursor = database.query("\"" + tableName + "\"", new String[]{"\"ID\"", "\"SORT_KEY\"", "\"QUERY\"", "\"BYTES\""}, "(\"QUERY\" LIKE ? OR \"QUERY\" LIKE ?) AND \"SORT_KEY\" > ?", new String[]{ query + "%", "% " + query + "%", String.valueOf(sortingKey) }, null, null, "\"SORT_KEY\" ASC", String.valueOf(limit)); } return loadSlice(cursor); } @Override public int getCount() { checkTable(); Cursor mCount = null; try { mCount = database.rawQuery("SELECT COUNT(*) FROM \"" + tableName + "\"", null); if (mCount.moveToFirst()) { return mCount.getInt(0); } } finally { if (mCount != null) { mCount.close(); } } return 0; } private ArrayList<ListEngineRecord> loadSlice(Cursor cursor) { // int queryColumn = enableSearch ? cursor.getColumnIndex("QUERY") : -1; ArrayList<ListEngineRecord> res = new ArrayList<ListEngineRecord>(); if (cursor != null) { int idColumn = cursor.getColumnIndex("ID"); int sortColumn = cursor.getColumnIndex("SORT_KEY"); int bytesColumn = cursor.getColumnIndex("BYTES"); int queryColumn = cursor.getColumnIndex("QUERY"); try { if (cursor.moveToFirst()) { do { res.add(new ListEngineRecord(cursor.getLong(idColumn), cursor.getLong(sortColumn), cursor.getString(queryColumn), cursor.getBlob(bytesColumn))); } while (cursor.moveToNext()); } } finally { cursor.close(); } } return res; } }