package org.kroz.activerecord; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import org.kroz.activerecord.utils.ARConst; import org.kroz.activerecord.utils.Logg; import android.content.ContentValues; import android.content.Context; import android.database.Cursor; import android.database.sqlite.SQLiteDatabase; /** * Represents a database to be used by Android Active Record entities. * * @author JEREMYOT * @author Vladimir Kroz * * <p>This project based on and inspired by 'androidactiverecord' project written by JEREMYOT</p> */ public class Database { static final String CNAME = Database.class.getSimpleName(); static Map<String, DatabaseBuilder> _builders = new HashMap<String, DatabaseBuilder>(); private SQLiteDatabase _database; private DatabaseOpenHelper _dbHelper; private String _path; @SuppressWarnings("unused") private Context _context; /** * Creates a new DatabaseWrapper object * * @param dbName * The file name to use for the SQLite database. * @param dbVersion * Database version * @param context * The context used for database creation, its package name will * be used to place the database on external storage if any is * present, otherwise the context's application data directory. */ Database(Context context, String dbName, int dbVersion, DatabaseBuilder builder) { _context = context; // String dbPath = (Environment.getExternalStorageState().equals( // Environment.MEDIA_MOUNTED) ? appendFilePath(Environment // .getExternalStorageDirectory().getAbsolutePath(), String // .format("android%1$sdata%1$s%2$s%1$s", File.separator, _context // .getPackageName())) : _context.getDir( // _context.getPackageName(), 0).getAbsolutePath()); // new File(dbPath).mkdirs(); _path = dbName; // temporary workaround - DB is created only on device, // not SDcard // _path = appendFilePath(dbPath, dbName); _dbHelper = new DatabaseOpenHelper(context, _path, dbVersion, builder); _context = context; } /** * Creates new DB instance. Returned DB instances is not initially opened. * Calling application must explicitly open it by calling open() method * * @param ctx * @param dbName * @param dbVersion * @return * @throws ActiveRecordException */ public static Database createInstance(Context ctx, String dbName, int dbVersion) throws ActiveRecordException { DatabaseBuilder builder = getBuilder(dbName); if (null == builder) throw new ActiveRecordException( "Schema wasn't initialized. Call Database.setBuilder() first"); return new Database(ctx, dbName, dbVersion, builder); } /** * Creates and opens new DB instances. DB instance returned to calling * application already opened. * * @param ctx * @param dbName * @param dbVersion * @return * @throws ActiveRecordException */ public static Database open(Context ctx, String dbName, int dbVersion) throws ActiveRecordException { Database db = Database.createInstance(ctx, dbName, dbVersion); db.open(); return db; } /** * Returns DatabaseBuilder object assosicted with Database * * @param dbName * database name * @return DatabaseBuilder object assosicted with Database */ public static DatabaseBuilder getBuilder(String dbName) { return _builders.get(dbName); } /** * Initializes Database framework. This method must be called for each used * database only once before using database. This is required for proper * setup static attributes of the Database * * @param builder * @return */ static public void setBuilder(DatabaseBuilder builder) { _builders.put(builder.getDatabaseName(), builder); } public static Database createInstance(Context ctx, String dbName, int dbVersion, DatabaseBuilder builder) { return new Database(ctx, dbName, dbVersion, builder); } /** * Opens or creates the database file. Uses external storage if present, * otherwise uses local storage. */ public void open() { if (_database != null && _database.isOpen()) { _database.close(); _database = null; } _database = _dbHelper.getReadableDatabase(); Logg.d(ARConst.TAG, "(%t) %s.open(): new db obj %s", CNAME, _database.toString()); } public void close() { String d = _database.toString(); if (_database != null) _database.close(); _database = null; Logg.d(ARConst.TAG, "(%t) %s.close(): db obj %s set to null", CNAME, d); } public boolean isOpen() { if (null != _database && _database.isOpen()) return true; else return false; } /** * Execute some raw SQL. * * @param sql * Standard SQLite compatible SQL. */ public void execute(String sql) { _database.execSQL(sql); } /** * Insert into a table in the database. * * @param table * The table to insert into. * @param parameters * The data. * @return the row ID of the newly inserted row, or -1 if an error occurred. */ public long insert(String table, ContentValues parameters) { return _database.insert(table, null, parameters); } /** * Update a table in the database. * * @param table * The table to update. * @param values * The new values. * @param whereClause * The condition to match (Don't include "where"). * @param whereArgs * The arguments to replace "?" with. * @return The number of rows affected. */ public int update(String table, ContentValues values, String whereClause, String[] whereArgs) { return _database.update(table, values, whereClause, whereArgs); } /** * Delete from a table in the database * * @param table * The table to delete from. * @param whereClause * The condition to match (Don't include WHERE). * @param whereArgs * The arguments to replace "?" with. * @return The number of rows affected. */ public int delete(String table, String whereClause, String[] whereArgs) { return _database.delete(table, whereClause, whereArgs); } /** * Execute a raw SQL query. * * @param sql * Standard SQLite compatible SQL. * @return A cursor over the data returned. */ public Cursor rawQuery(String sql) { return rawQuery(sql, null); } /** * Execute a raw SQL query. * * @param sql * Standard SQLite compatible SQL. * @param params * The values to replace "?" with. * @return A cursor over the data returned. */ public Cursor rawQuery(String sql, String[] params) { return _database.rawQuery(sql, params); } /** * Execute a query. * * @param table * The table to query. * @param selectColumns * The columns to select. * @param where * The condition to match (Don't include "where"). * @param whereArgs * The arguments to replace "?" with. * @return A cursor over the data returned. * @throws ActiveRecordException is database is null or closed */ public Cursor query(String table, String[] selectColumns, String where, String[] whereArgs) throws ActiveRecordException { return query(false, table, selectColumns, where, whereArgs, null, null, null, null); } /** * Execute a query. * * @param distinct * @param table * The table to query. * @param selectColumns * The columns to select. * @param where * The condition to match (Don't include "where"). * @param whereArgs * The arguments to replace "?" with. * @param groupBy * @param having * @param orderBy * @param limit * @return A cursor over the data returned. * @throws ActiveRecordException is database is null or closed */ public Cursor query(boolean distinct, String table, String[] selectColumns, String where, String[] whereArgs, String groupBy, String having, String orderBy, String limit) throws ActiveRecordException { if (null == _database || !_database.isOpen()) { Logg.e(ARConst.TAG, "(%t) %s.query(): ERROR - db object is null or closed", CNAME); throw new ActiveRecordException(ErrMsg.ERR_DB_IS_NOT_OPEN); } return _database.query(distinct, table, selectColumns, where, whereArgs, groupBy, having, orderBy, limit); } /** * Returns array of database tables names * * @throws ActiveRecordException */ public String[] getTables() throws ActiveRecordException { if (null == _database || !_database.isOpen()) { Logg.e(ARConst.TAG, "(%t) %s.getTables(): ERROR - db object is null or closed", CNAME); throw new ActiveRecordException(ErrMsg.ERR_DB_IS_NOT_OPEN); } Cursor c = query("sqlite_master", new String[] { "name" }, "type = ?", new String[] { "table" }); List<String> tables = new ArrayList<String>(); try { while (c.moveToNext()) { tables.add(c.getString(0)); } } finally { c.close(); } return tables.toArray(new String[0]); } public String[] getColumnsForTable(String table) { Cursor c = rawQuery(String.format("PRAGMA table_info(%s)", table)); List<String> columns = new ArrayList<String>(); try { while (c.moveToNext()) { columns.add(c.getString(c.getColumnIndex("name"))); } } finally { c.close(); } return columns.toArray(new String[0]); } public int getVersion() throws ActiveRecordException { if (null == _database || !_database.isOpen()) { Logg.e(ARConst.TAG, "(%t) %s.getVersion(): ERROR - db object is null or closed", CNAME); throw new ActiveRecordException(ErrMsg.ERR_DB_IS_NOT_OPEN); } return _database.getVersion(); } public void setVersion(int version) throws ActiveRecordException { if (null == _database || !_database.isOpen()) { Logg.e(ARConst.TAG, "(%t) %s.setVersion(): ERROR - db object is null or closed", CNAME); throw new ActiveRecordException(ErrMsg.ERR_DB_IS_NOT_OPEN); } _database.setVersion(version); } public void beginTransaction() { _database.beginTransaction(); } public void endTransaction() { _database.endTransaction(); } /** * Get the SQLite type for an input class. * * @param c * The class to convert. * @return A string representing the SQLite type that would be used to store * that class. */ protected static String getSQLiteTypeString(Class<?> c) { String name = c.getName(); if (name.equals("java.lang.String")) return "text"; if (name.equals("short")) return "int"; if (name.equals("int")) return "int"; if (name.equals("long")) return "int"; if (name.equals("long")) return "int"; if (name.equals("java.sql.Timestamp")) return "int"; if (name.equals("double")) return "real"; if (name.equals("float")) return "real"; if (name.equals("[B")) return "blob"; if (name.equals("boolean")) return "bool"; if (c.getSuperclass() == ActiveRecordBase.class) return "int"; throw new IllegalArgumentException( "Class cannot be stored in Sqlite3 database."); } // /** // * Append to a file path, takes extra or missing separator characters into // * account. // * // * @param path // * The root path. // * @param append // * What to add. // * @return The new path. // */ // private static String appendFilePath(String path, String append) { // return path.concat(path.endsWith(File.separator) ? (append // .startsWith(File.separator) ? append.substring(1) : append) // : File.separator // .concat((append.startsWith(File.separator) ? append // .substring(1) : append))); // } // -------------------------------------------------------------------------// }