package tech.linjiang.pandora.database; import android.database.Cursor; import android.database.sqlite.SQLiteDatabase; import android.database.sqlite.SQLiteException; import android.database.sqlite.SQLiteStatement; import java.io.File; import java.util.ArrayList; import java.util.Collections; import java.util.HashSet; import java.util.List; import java.util.Set; import tech.linjiang.pandora.database.protocol.IDriver; import tech.linjiang.pandora.database.protocol.IProvider; public class DatabaseDriver implements IDriver<DatabaseDescriptor> { private static final String[] EXTRA_FILE_SUFFIXES = new String[]{"-journal", "-shm", "-uid", "-wal"}; private final IProvider mProvider; public DatabaseDriver(IProvider mProvider) { this.mProvider = mProvider; } @Override public List<DatabaseDescriptor> getDatabaseNames() { ArrayList<DatabaseDescriptor> databases = new ArrayList<>(); List<File> potentialDatabaseFiles = mProvider.getDatabaseFiles(); Collections.sort(potentialDatabaseFiles); Iterable<File> tidiedList = tidyDatabaseList(potentialDatabaseFiles); for (File database : tidiedList) { databases.add(new DatabaseDescriptor(database)); } return databases; } private static List<File> tidyDatabaseList(List<File> databaseFiles) { Set<File> originalAsSet = new HashSet<>(databaseFiles); List<File> tidiedList = new ArrayList<>(); for (File databaseFile : databaseFiles) { String databaseFilename = databaseFile.getPath(); String sansSuffix = removeSuffix(databaseFilename, EXTRA_FILE_SUFFIXES); if (sansSuffix.equals(databaseFilename) || !originalAsSet.contains(new File(sansSuffix))) { tidiedList.add(databaseFile); } } return tidiedList; } private static String removeSuffix(String str, String[] suffixesToRemove) { for (String suffix : suffixesToRemove) { if (str.endsWith(suffix)) { return str.substring(0, str.length() - suffix.length()); } } return str; } @Override public List<String> getTableNames(DatabaseDescriptor databaseDesc) throws SQLiteException { SQLiteDatabase database = openDatabase(databaseDesc); try { Cursor cursor = database.rawQuery("SELECT name FROM sqlite_master WHERE type IN (?/*, ?*/)", new String[]{"table"/*, "view"*/}); try { List<String> tableNames = new ArrayList<>(); while (cursor.moveToNext()) { tableNames.add(cursor.getString(0)); } return tableNames; } finally { cursor.close(); } } finally { database.close(); } } @Override public void executeSQL(DatabaseDescriptor databaseDesc, String query, DatabaseResult result) throws SQLiteException { SQLiteDatabase database = openDatabase(databaseDesc); try { String firstWordUpperCase = getFirstWord(query).toUpperCase(); switch (firstWordUpperCase) { case "UPDATE": case "DELETE": executeUpdateDelete(database, query, result); break; case "INSERT": executeInsert(database, query, result); break; case "SELECT": case "PRAGMA": case "EXPLAIN": executeSelect(database, query, result); break; default: executeRawQuery(database, query, result); break; } } finally { database.close(); } } private static String getFirstWord(String s) { s = s.trim(); int firstSpace = s.indexOf(' '); return firstSpace >= 0 ? s.substring(0, firstSpace) : s; } private void executeUpdateDelete(SQLiteDatabase database, String query, DatabaseResult result) { SQLiteStatement statement = database.compileStatement(query); int count = statement.executeUpdateDelete(); result.transformUpdateDelete(count); } private void executeInsert(SQLiteDatabase database, String query, DatabaseResult result) { SQLiteStatement statement = database.compileStatement(query); long count = statement.executeInsert(); result.transformInsert(count); } private void executeSelect(SQLiteDatabase database, String query, DatabaseResult result) { Cursor cursor = database.rawQuery(query, null); try { result.transformSelect(cursor); } finally { cursor.close(); } } private void executeRawQuery(SQLiteDatabase database, String query, DatabaseResult result) { database.execSQL(query); result.transformRawQuery(); } private SQLiteDatabase openDatabase(DatabaseDescriptor databaseDesc) throws SQLiteException { return mProvider.openDatabase(databaseDesc.file); } }