package com.eleybourn.bookcatalogue.booklist; import static com.eleybourn.bookcatalogue.booklist.DatabaseDefinitions.DOM_BOOK; import static com.eleybourn.bookcatalogue.booklist.DatabaseDefinitions.DOM_ID; import static com.eleybourn.bookcatalogue.booklist.DatabaseDefinitions.TBL_ROW_NAVIGATOR_FLATTENED_DEFN; import android.database.sqlite.SQLiteDoneException; import com.eleybourn.bookcatalogue.database.DbSync.SynchronizedDb; import com.eleybourn.bookcatalogue.database.DbSync.SynchronizedStatement; import com.eleybourn.bookcatalogue.database.DbUtils.TableDefinition; import com.eleybourn.bookcatalogue.database.DbUtils.TableDefinition.TableTypes; import com.eleybourn.bookcatalogue.database.SqlStatementManager; /** * Class to provide a simple interface into a temporary table containing a list of book IDs on * the same order as an underlying book list. * * @author pjw */ public class FlattenedBooklist { /** Underlying temporary table definition */ private TableDefinition mTable; /** Connection to db; we need this to keep the table alive */ private SynchronizedDb mDb; /** Default position (before start) */ private long mPosition = -1; /** Book ID from the currently selected row */ private Long mBookId = null; /** Collection of statements compiled for this object */ private SqlStatementManager mStatements; /** * Constructor * * @param db Database connection * @param table Table definition */ public FlattenedBooklist(SynchronizedDb db, TableDefinition table) { init(db, table.clone()); } /** * Constructor * * @param db Database connection * @param tableName Name of underlying table */ public FlattenedBooklist(SynchronizedDb db, String tableName) { TableDefinition flat = TBL_ROW_NAVIGATOR_FLATTENED_DEFN.clone(); flat.setName(tableName); flat.setType(TableTypes.Temporary); //RELEASE Make sure is TEMPORARY init(db, flat); } /** * Shared constructor utility routine.Save the passed values. * * @param db Database connection * @param table Table definition */ private void init(SynchronizedDb db, TableDefinition table) { mDb = db; mTable = table; mStatements = new SqlStatementManager(mDb); } /** * Accessor * * @return */ public TableDefinition getTable() { return mTable; } /** * Accessor * * @return */ public Long getBookId() { return mBookId; } /** * Release resource-consuming stuff */ public void close() { mStatements.close(); } /** * Cleanup the underlying table */ public void deleteData() { mTable.drop(mDb); mTable.close(); } /** * Passed a statement update the 'current' row details based on the columns returned * @param stmt * @return */ private boolean updateDetailsFromStatement(SynchronizedStatement stmt) { // Get a pair of ID's separated by a '/' String info; try { info = stmt.simpleQueryForString(); } catch (SQLiteDoneException e) { return false; } if (info == null) return false; final String[] data = info.split("/"); mPosition = Long.parseLong(data[0]); mBookId = Long.parseLong(data[1]); return true; } /** * Check that the referenced table exists. This is important for resumed activities * where th underlying database connection may have closed and the table been deleted * as a result. * * @return */ public boolean exists() { return mTable.exists(mDb); } /** * Name for the 'next' statement */ private static final String NEXT_STMT_NAME = "next"; /** * Move to the next book row * * @return true if successful */ public boolean moveNext() { SynchronizedStatement stmt = mStatements.get(NEXT_STMT_NAME); if (stmt == null) { String sql = "Select " + mTable.dot(DOM_ID) + "|| '/' || " + mTable.dot(DOM_BOOK) + " From " + mTable.ref() + " Where " + mTable.dot(DOM_ID) + " > ? and " + mTable.dot(DOM_BOOK) + " <> Coalesce(?,-1)" + " Order by " + mTable.dot(DOM_ID) + " Asc Limit 1"; stmt = mStatements.add(NEXT_STMT_NAME, sql); } stmt.bindLong(1, mPosition); if (mBookId != null) { stmt.bindLong(2, mBookId); } else { stmt.bindNull(2); } return updateDetailsFromStatement(stmt); } /** * Name for the 'prev' statement */ private static final String PREV_STMT_NAME = "prev"; /** * Move to the previous book row * * @return true if successful */ public boolean movePrev() { SynchronizedStatement stmt = mStatements.get(PREV_STMT_NAME); if (stmt == null) { String sql = "Select " + mTable.dot(DOM_ID) + "|| '/' || " + mTable.dot(DOM_BOOK) + " From " + mTable.ref() + " Where " + mTable.dot(DOM_ID) + " < ? and " + mTable.dot(DOM_BOOK) + " <> Coalesce(?,-1)" + " Order by " + mTable.dot(DOM_ID) + " Desc Limit 1"; stmt = mStatements.add(PREV_STMT_NAME, sql); } stmt.bindLong(1, mPosition); if (mBookId != null) { stmt.bindLong(2, mBookId); } else { stmt.bindNull(2); } return updateDetailsFromStatement(stmt); } /** * Name for the 'move-to' statement */ private static final String MOVE_STMT_NAME = "move"; /** * Move to the specified book row, based on the row ID, not the book ID or row number. * The row ID should be the row number in the table, including header-related rows. * * @return true if successful */ public boolean moveTo(Integer pos) { SynchronizedStatement stmt = mStatements.get(MOVE_STMT_NAME); if (stmt == null) { String sql = "Select " + mTable.dot(DOM_ID) + "|| '/' || " + mTable.dot(DOM_BOOK) + " From " + mTable.ref() + " Where " + mTable.dot(DOM_ID) + " = ?"; stmt = mStatements.add(MOVE_STMT_NAME, sql); } stmt.bindLong(1, pos); if ( updateDetailsFromStatement(stmt) ) { return true; } else { long posSav = mPosition; mPosition = pos; if (moveNext() || movePrev()) { return true; } else { mPosition = posSav; return false; } } } /** * Move to the first row * * @return true if successful */ public boolean moveFirst() { mPosition = -1; mBookId = null; return moveNext(); } /** * Move to the last row * * @return true if successful */ public boolean moveLast() { mPosition = Long.MAX_VALUE; mBookId = null; return movePrev(); } /** * Get the underlying row position (row ID) * @return */ public long getPosition() { return mPosition; } /** * Name for the 'count' statement */ private static final String COUNT_STMT_NAME = "count"; /** * Get the total row count * * @return number of rows */ public long getCount() { SynchronizedStatement stmt = mStatements.get(COUNT_STMT_NAME); if (stmt == null) { String sql = "Select Count(*) From " + mTable.ref(); stmt = mStatements.add(COUNT_STMT_NAME, sql); } return stmt.simpleQueryForLong(); } /** * Name for the 'absolute-position' statement */ private static final String POSITION_STMT_NAME = "position"; /** * Get the position of the current record in the table * * @return position */ public long getAbsolutePosition() { SynchronizedStatement stmt = mStatements.get(POSITION_STMT_NAME); if (stmt == null) { String sql = "Select Count(*) From " + mTable.ref() + " where " + mTable.dot(DOM_ID) + " <= ?"; stmt = mStatements.add(POSITION_STMT_NAME, sql); } stmt.bindLong(1, mPosition); return stmt.simpleQueryForLong(); } /** * Cleanup the statements */ public void finalize() { close(); } }