package com.applang.provider; import com.applang.Util.Job; import com.applang.provider.PlantInfo.Pictures; import com.applang.provider.PlantInfo.Plants; import android.content.ContentProvider; import android.content.ContentUris; import android.content.ContentValues; import android.content.Context; import android.content.UriMatcher; import android.database.Cursor; import android.database.SQLException; import android.database.sqlite.SQLiteDatabase; import android.database.sqlite.SQLiteOpenHelper; import android.database.sqlite.SQLiteQueryBuilder; import android.net.Uri; import android.util.Log; import java.util.ArrayList; import java.util.HashMap; import static com.applang.Util.*; import static com.applang.Util1.*; /** * Provides access to a database of plants. */ public class PlantInfoProvider extends ContentProvider { private static final String TAG = "PlantInfoProvider"; public static final String DATABASE_NAME = "plant_info.db"; public static final int DATABASE_VERSION = 2; public static final String PLANTS_TABLE_NAME = "plants"; public static final String PICTURES_TABLE_NAME = "pictures"; public static final String PLANT_PICS_TABLE_NAME = "plant_pics"; public static final String[] DATABASE_TABLES = {PLANTS_TABLE_NAME, PICTURES_TABLE_NAME, PLANT_PICS_TABLE_NAME}; public static String tableName(int index) { if (index > -1 && index < DATABASE_TABLES.length) return DATABASE_TABLES[index]; else return ""; } public static int tableIndex(String name) { return asList(DATABASE_TABLES).indexOf(name); } public static Uri contentUri(int index) { return contentUri(tableName(index)); } public static Uri contentUri(String name) { return com.applang.Util1.contentUri(PlantInfo.AUTHORITY, name); } public static int tableIndex(int defaultValue, Uri uri) { if (uri == null) return defaultValue; int segments = uri.getPathSegments().size(); if (segments < 1) return defaultValue; else { int index = tableIndex(uri.getPathSegments().get(0)); return index < 0 ? defaultValue : index; } } private static final int PLANTS = 1; private static final int PLANT_ID = 2; private static final int RAW = 3; private static final int PICTURES = 4; private static final int PICTURE_ID = 5; private static final UriMatcher sUriMatcher; public static ContentValues contentValues(int tableIndex, Object... args) { ContentValues values = new ContentValues(); switch (tableIndex) { case 0: if (args.length > 0) values.put(Plants._ID, (Long)args[0]); if (args.length > 1) values.put(Plants.NAME, (String)args[1]); if (args.length > 2) values.put(Plants.FAMILY, (String)args[2]); if (args.length > 3) values.put(Plants.BOTNAME, (String)args[3]); if (args.length > 4) values.put(Plants.BOTFAMILY, (String)args[4]); if (args.length > 5) values.put(Plants.GROUP, (String)args[5]); break; case 1: if (args.length > 0) values.put(Pictures._ID, (Long)args[0]); if (args.length > 1) values.put(Pictures.NAME, (String)args[1]); if (args.length > 2) values.put(Pictures.TYPE, (String)args[2]); if (args.length > 5) values.put(Pictures.BLOB, (String)args[3]); break; } return values; } public static String[] FULL_PROJECTION = strings( Plants._ID, // 0 Plants.NAME, // 1 Plants.FAMILY, // 2 Plants.BOTNAME, // 3 Plants.BOTFAMILY, // 4 Plants.GROUP // 5 ); /** * This class helps open, create, and upgrade the database file. */ public static class DatabaseHelper extends SQLiteOpenHelper { public DatabaseHelper(Context context, String dbName) { super(context, dbName, null, DATABASE_VERSION); } @Override public void onCreate(SQLiteDatabase db) { turnForeignKeys(db, true); for (int i = 0; i < DATABASE_TABLES.length; i++) { createTable(i, db); } } @Override public void onUpgrade(final SQLiteDatabase db, int oldVersion, int newVersion) { Log.w(TAG, "Upgrading database from version " + oldVersion + " to " + newVersion + ", which will try to loose as few as possible of the old data"); turnForeignKeys(db, false); for (int i = 0; i < DATABASE_TABLES.length; i++) table_upgrade(db, DATABASE_TABLES[i], new Job<Void>() { public void perform(Void t, Object[] parms) throws Exception { createTable(param_Integer(null, 0, parms), db); } }, i); turnForeignKeys(db, true); } } public static void createTable(int index, SQLiteDatabase db) { String sql = "CREATE TABLE IF NOT EXISTS " + DATABASE_TABLES[index] + " ("; switch (index) { case 0: sql += Plants._ID + " INTEGER PRIMARY KEY," + Plants.NAME + " TEXT," + Plants.FAMILY + " TEXT," + Plants.BOTNAME + " TEXT," + Plants.BOTFAMILY + " TEXT," + Plants.GROUP + " TEXT"; break; case 1: sql += Pictures._ID + " INTEGER PRIMARY KEY," + Pictures.NAME + " TEXT," + Pictures.TYPE + " INTEGER," + Pictures.BLOB + " BLOB"; break; case 2: sql += "Plant_ID INTEGER NOT NULL," + "Picture_ID INTEGER NOT NULL," + "unique(Plant_ID, Picture_ID), " + "foreign key(Plant_ID) references " + DATABASE_TABLES[0] + "(" + Plants._ID + "), " + "foreign key(Picture_ID) references " + DATABASE_TABLES[1] + "(" + Pictures._ID + ")"; break; } sql += ");"; db.execSQL(sql); switch (index) { case 2: db.execSQL("CREATE TRIGGER on_delete_plant" + " BEFORE DELETE ON " + DATABASE_TABLES[0] + " FOR EACH ROW BEGIN" + " DELETE FROM " + DATABASE_TABLES[2] + " WHERE " + DATABASE_TABLES[2] + ".Plant_ID = old." + Plants._ID + ";" + " END;"); db.execSQL("CREATE TRIGGER on_delete_picture" + " BEFORE DELETE ON " + DATABASE_TABLES[1] + " FOR EACH ROW BEGIN" + " DELETE FROM " + DATABASE_TABLES[2] + " WHERE " + DATABASE_TABLES[2] + ".Picture_ID = old." + Pictures._ID + ";" + " END;"); break; } } private DatabaseHelper mOpenHelper; public SQLiteOpenHelper openHelper() { return mOpenHelper; } @Override public boolean onCreate() { mOpenHelper = new DatabaseHelper(getContext(), DATABASE_NAME); return true; } @Override public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) { SQLiteQueryBuilder qb = new SQLiteQueryBuilder(); switch (sUriMatcher.match(uri)) { case RAW: return mOpenHelper.getReadableDatabase().rawQuery(selection, selectionArgs); case PLANTS: qb.setTables(PLANTS_TABLE_NAME); qb.setProjectionMap(projectionMaps.get(0)); break; case PLANT_ID: qb.setTables(PLANTS_TABLE_NAME); qb.setProjectionMap(projectionMaps.get(0)); qb.appendWhere(Plants._ID + "=" + uri.getPathSegments().get(1)); break; case PICTURES: qb.setTables(PICTURES_TABLE_NAME); qb.setProjectionMap(projectionMaps.get(1)); break; case PICTURE_ID: qb.setTables(PICTURES_TABLE_NAME); qb.setProjectionMap(projectionMaps.get(1)); qb.appendWhere(Pictures._ID + "=" + uri.getPathSegments().get(1)); break; default: throw new IllegalArgumentException("Unknown URI " + uri); } String orderBy; if (notNullOrEmpty(sortOrder)) { orderBy = sortOrder; } else { orderBy = Plants.DEFAULT_SORT_ORDER; } SQLiteDatabase db = mOpenHelper.getReadableDatabase(); Cursor c = qb.query(db, projection, selection, selectionArgs, null, null, orderBy); c.setNotificationUri(getContext().getContentResolver(), uri); return c; } private void notifyChange(Uri uri) { getContext().getContentResolver().notifyChange(uri, null); } @Override public String getType(Uri uri) { switch (sUriMatcher.match(uri)) { case RAW: case PLANTS: return Plants.CONTENT_TYPE; case PLANT_ID: return Plants.CONTENT_ITEM_TYPE; case PICTURES: return Pictures.CONTENT_TYPE; case PICTURE_ID: return Pictures.CONTENT_ITEM_TYPE; default: throw new IllegalArgumentException("Unknown URI " + uri); } } @Override public Uri insert(Uri uri, ContentValues initialValues) { int match = sUriMatcher.match(uri); if (match != PLANTS && match != PICTURES && match != RAW) { throw new IllegalArgumentException("Unknown URI " + uri); } ContentValues values; if (initialValues != null) { values = new ContentValues(initialValues); } else { values = new ContentValues(); } String tableName = dbTableName(uri); String name = null; switch (match) { case PLANTS: name = Plants.NAME; break; case PICTURES: name = Pictures.NAME; break; case RAW: tableName = PLANT_PICS_TABLE_NAME; if (!values.containsKey("PLANT_ID") && !values.containsKey("PICTURE_ID")) { throw new IllegalArgumentException("Unclear plant/picture relation : " + uri); } } SQLiteDatabase db = mOpenHelper.getWritableDatabase(); if (!values.containsKey(name)) { values.put(name, ""); } long rowId = db.insert(tableName, name, values); if (rowId > 0) { Uri plantUri = ContentUris.withAppendedId(Plants.CONTENT_URI, rowId); notifyChange(plantUri); return plantUri; } throw new SQLException("Failed to insert row into " + uri); } @Override public int delete(Uri uri, String where, String[] whereArgs) { SQLiteDatabase db = mOpenHelper.getWritableDatabase(); int count; switch (sUriMatcher.match(uri)) { case PLANTS: count = db.delete(PLANTS_TABLE_NAME, where, whereArgs); break; case PLANT_ID: String plantId = uri.getPathSegments().get(1); count = db.delete(PLANTS_TABLE_NAME, Plants._ID + "=" + plantId + (notNullOrEmpty(where) ? " AND (" + where + ')' : ""), whereArgs); break; case PICTURES: count = db.delete(PICTURES_TABLE_NAME, where, whereArgs); break; case PICTURE_ID: String pictureId = uri.getPathSegments().get(1); count = db.delete(PICTURES_TABLE_NAME, Pictures._ID + "=" + pictureId + (notNullOrEmpty(where) ? " AND (" + where + ')' : ""), whereArgs); break; case RAW: if (!where.contains("PLANT_ID") && !where.contains("PICTURE_ID")) { throw new IllegalArgumentException("Unclear plant/picture relation : " + uri); } count = db.delete(PLANT_PICS_TABLE_NAME, where, whereArgs); break; default: throw new IllegalArgumentException("Unknown URI " + uri); } notifyChange(uri); return count; } @Override public int update(Uri uri, ContentValues values, String where, String[] whereArgs) { SQLiteDatabase db = mOpenHelper.getWritableDatabase(); int count; switch (sUriMatcher.match(uri)) { case PLANTS: count = db.update(PLANTS_TABLE_NAME, values, where, whereArgs); break; case PLANT_ID: String plantId = uri.getPathSegments().get(1); count = db.update(PLANTS_TABLE_NAME, values, Plants._ID + "=" + plantId + (notNullOrEmpty(where) ? " AND (" + where + ')' : ""), whereArgs); break; case PICTURES: count = db.update(PICTURES_TABLE_NAME, values, where, whereArgs); break; case PICTURE_ID: String pictureId = uri.getPathSegments().get(1); count = db.update(PICTURES_TABLE_NAME, values, Pictures._ID + "=" + pictureId + (notNullOrEmpty(where) ? " AND (" + where + ')' : ""), whereArgs); break; default: throw new IllegalArgumentException("Unknown URI " + uri); } notifyChange(uri); return count; } private static ArrayList<HashMap<String, String>> projectionMaps = alist(); static { sUriMatcher = new UriMatcher(UriMatcher.NO_MATCH); sUriMatcher.addURI(PlantInfo.AUTHORITY, null, RAW); sUriMatcher.addURI(PlantInfo.AUTHORITY, "plants", PLANTS); sUriMatcher.addURI(PlantInfo.AUTHORITY, "plants/#", PLANT_ID); sUriMatcher.addURI(PlantInfo.AUTHORITY, "pictures", PICTURES); sUriMatcher.addURI(PlantInfo.AUTHORITY, "pictures/#", PICTURE_ID); projectionMaps.add(new HashMap<String, String>()); projectionMaps.get(0).put(Plants._ID, Plants._ID); projectionMaps.get(0).put(Plants.NAME, Plants.NAME); projectionMaps.get(0).put(Plants.FAMILY, Plants.FAMILY); projectionMaps.get(0).put(Plants.BOTNAME, Plants.BOTNAME); projectionMaps.get(0).put(Plants.BOTFAMILY, Plants.BOTFAMILY); projectionMaps.get(0).put(Plants.GROUP, Plants.GROUP); projectionMaps.add(new HashMap<String, String>()); projectionMaps.get(1).put(Pictures._ID, Pictures._ID); projectionMaps.get(1).put(Pictures.NAME, Pictures.NAME); projectionMaps.get(1).put(Pictures.TYPE, Pictures.TYPE); projectionMaps.get(1).put(Pictures.BLOB, Pictures.BLOB); } }