/* * Copyright 2012 The Stanford MobiSocial Laboratory * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package mobisocial.musubi.model.helpers; import java.io.FileDescriptor; import mobisocial.musubi.model.DbLikeCache; import mobisocial.musubi.model.MDevice; import mobisocial.musubi.model.MIdentity; import mobisocial.musubi.model.MObject; import mobisocial.musubi.util.Util; import android.database.Cursor; import android.database.sqlite.SQLiteDatabase; import android.database.sqlite.SQLiteDoneException; import android.database.sqlite.SQLiteOpenHelper; import android.database.sqlite.SQLiteStatement; import android.os.Build; import android.os.ParcelFileDescriptor; public class ObjectManager extends ManagerBase { private SQLiteStatement mSqlInsertObj; private SQLiteStatement mSqlUpdateObj; private SQLiteStatement mSqlGetObjIdByHash; private SQLiteStatement mSqlObjectCount; private SQLiteStatement mSqlGetLatestRenderableId; private String mSqlQueryObjectForId; private String mSqlQueryObjectWithoutRawForId; private String mSqlGetObjectsToEncode; private SQLiteStatement mSqlLikeCount; private SQLiteStatement mSqlUpdateObjPipelineMetadata; private SQLiteStatement mSqlUpdateObjEncodeMetadata; static String[] STANDARD_FIELDS = new String[] { MObject.COL_ID, MObject.COL_FEED_ID, MObject.COL_IDENTITY_ID, MObject.COL_DEVICE_ID, MObject.COL_PARENT_ID, MObject.COL_APP_ID, MObject.COL_TIMESTAMP, MObject.COL_UNIVERSAL_HASH, MObject.COL_SHORT_UNIVERSAL_HASH, MObject.COL_TYPE, MObject.COL_JSON, MObject.COL_RAW, MObject.COL_INT_KEY, MObject.COL_STRING_KEY, MObject.COL_LAST_MODIFIED_TIMESTAMP, MObject.COL_ENCODED_ID, MObject.COL_DELETED, MObject.COL_RENDERABLE, MObject.COL_PROCESSED }; final int _id = 0; final int feedId = 1; final int identityId = 2; final int deviceId = 3; final int parentId = 4; final int appId = 5; final int timestamp = 6; final int universalHash = 7; final int shortHash = 8; final int type = 9; final int json = 10; final int raw = 11; final int intKey = 12; final int stringKey = 13; final int lastModified = 14; final int encodedId = 15; final int deleted = 16; final int renderable = 17; final int processed = 18; public ObjectManager(SQLiteOpenHelper databaseSource) { super(databaseSource); } public void insertObject(MObject obj) { SQLiteDatabase db = initializeDatabase(); if (mSqlInsertObj == null) { synchronized (this) { if(mSqlInsertObj == null) { String sql = new StringBuilder() .append("INSERT INTO ").append(MObject.TABLE).append("(") .append(MObject.COL_FEED_ID).append(",") .append(MObject.COL_IDENTITY_ID).append(",") .append(MObject.COL_DEVICE_ID).append(",") .append(MObject.COL_PARENT_ID).append(",") .append(MObject.COL_APP_ID).append(",") .append(MObject.COL_TIMESTAMP).append(",") .append(MObject.COL_UNIVERSAL_HASH).append(",") .append(MObject.COL_SHORT_UNIVERSAL_HASH).append(",") .append(MObject.COL_TYPE).append(",") .append(MObject.COL_JSON).append(",") .append(MObject.COL_RAW).append(",") .append(MObject.COL_INT_KEY).append(",") .append(MObject.COL_STRING_KEY).append(",") .append(MObject.COL_LAST_MODIFIED_TIMESTAMP).append(",") .append(MObject.COL_ENCODED_ID).append(",") .append(MObject.COL_DELETED).append(",") .append(MObject.COL_RENDERABLE).append(",") .append(MObject.COL_PROCESSED) .append(") VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)").toString(); mSqlInsertObj = db.compileStatement(sql); } } } synchronized (mSqlInsertObj) { bindStandardFields(mSqlInsertObj, obj); obj.id_ = mSqlInsertObj.executeInsert(); } } public void updateObject(MObject obj) { SQLiteDatabase db = initializeDatabase(); if (mSqlUpdateObj == null) { synchronized (this) { if(mSqlUpdateObj == null) { String sql = new StringBuilder() .append("UPDATE ").append(MObject.TABLE).append(" SET ") .append(MObject.COL_FEED_ID).append("=?,") .append(MObject.COL_IDENTITY_ID).append("=?,") .append(MObject.COL_DEVICE_ID).append("=?,") .append(MObject.COL_PARENT_ID).append("=?,") .append(MObject.COL_APP_ID).append("=?,") .append(MObject.COL_TIMESTAMP).append("=?,") .append(MObject.COL_UNIVERSAL_HASH).append("=?,") .append(MObject.COL_SHORT_UNIVERSAL_HASH).append("=?,") .append(MObject.COL_TYPE).append("=?,") .append(MObject.COL_JSON).append("=?,") .append(MObject.COL_RAW).append("=?,") .append(MObject.COL_INT_KEY).append("=?,") .append(MObject.COL_STRING_KEY).append("=?,") .append(MObject.COL_LAST_MODIFIED_TIMESTAMP).append("=?,") .append(MObject.COL_ENCODED_ID).append("=?,") .append(MObject.COL_DELETED).append("=?,") .append(MObject.COL_RENDERABLE).append("=?,") .append(MObject.COL_PROCESSED).append("=?") .append(" WHERE ").append(MObject.COL_ID).append("=?").toString(); mSqlUpdateObj = db.compileStatement(sql); } } } synchronized (mSqlUpdateObj) { bindStandardFieldsThenId(mSqlUpdateObj, obj); mSqlUpdateObj.execute(); } } public void updateObjectPipelineMetadata(MObject obj) { SQLiteDatabase db = initializeDatabase(); if (mSqlUpdateObjPipelineMetadata == null) { synchronized (this) { if(mSqlUpdateObjPipelineMetadata == null) { String sql = new StringBuilder() .append("UPDATE ").append(MObject.TABLE).append(" SET ") .append(MObject.COL_PARENT_ID).append("=?,") .append(MObject.COL_RENDERABLE).append("=?,") .append(MObject.COL_PROCESSED).append("=?") .append(" WHERE ").append(MObject.COL_ID).append("=?").toString(); mSqlUpdateObjPipelineMetadata = db.compileStatement(sql); } } } synchronized (mSqlUpdateObjPipelineMetadata) { if (obj.parentId_ == null) { mSqlUpdateObjPipelineMetadata.bindNull(1); } else { mSqlUpdateObjPipelineMetadata.bindLong(1, obj.parentId_); } mSqlUpdateObjPipelineMetadata.bindLong(2, obj.renderable_ ? 1 : 0); mSqlUpdateObjPipelineMetadata.bindLong(3, obj.processed_ ? 1 : 0); mSqlUpdateObjPipelineMetadata.bindLong(4, obj.id_); mSqlUpdateObjPipelineMetadata.execute(); } } public void updateObjectEncodedMetadata(MObject obj) { SQLiteDatabase db = initializeDatabase(); if (mSqlUpdateObjEncodeMetadata == null) { synchronized (this) { if(mSqlUpdateObjEncodeMetadata == null) { String sql = new StringBuilder() .append("UPDATE ").append(MObject.TABLE).append(" SET ") .append(MObject.COL_ENCODED_ID).append("=?,") .append(MObject.COL_UNIVERSAL_HASH).append("=?,") .append(MObject.COL_SHORT_UNIVERSAL_HASH).append("=?") .append(" WHERE ").append(MObject.COL_ID).append("=?").toString(); mSqlUpdateObjEncodeMetadata = db.compileStatement(sql); } } } synchronized (mSqlUpdateObjEncodeMetadata) { if (obj.encodedId_ == null) { mSqlUpdateObjEncodeMetadata.bindNull(1); } else { mSqlUpdateObjEncodeMetadata.bindLong(1, obj.encodedId_); } if (obj.universalHash_ == null) { mSqlUpdateObjEncodeMetadata.bindNull(2); } else { mSqlUpdateObjEncodeMetadata.bindBlob(2, obj.universalHash_); } if (obj.shortUniversalHash_ == null) { mSqlUpdateObjEncodeMetadata.bindNull(3); } else { mSqlUpdateObjEncodeMetadata.bindLong(3, obj.shortUniversalHash_); } mSqlUpdateObjEncodeMetadata.bindLong(4, obj.id_); mSqlUpdateObjEncodeMetadata.execute(); } } public MObject getObjectForId(long id) { SQLiteDatabase db = initializeDatabase(); if (mSqlQueryObjectForId == null) { synchronized (this) { StringBuilder sql = new StringBuilder(100).append("SELECT "); for (String c : STANDARD_FIELDS) { sql.append(c).append(","); } sql.setLength(sql.length() - 1); sql.append(" FROM ").append(MObject.TABLE) .append(" WHERE ").append(MObject.COL_ID).append("=?"); mSqlQueryObjectForId = sql.toString(); } } String[] selectionArgs = new String[] { String.valueOf(id) }; Cursor c = db.rawQuery(mSqlQueryObjectForId, selectionArgs); try { if (c.moveToNext()) { return fillInStandardFields(c); } return null; } finally { c.close(); } } String mSqlQueryForRawById; public byte[] getRawForId(long id) { SQLiteDatabase db = initializeDatabase(); if (mSqlQueryForRawById == null) { synchronized (this) { StringBuilder sql = new StringBuilder(100).append("SELECT ") .append(MObject.COL_RAW) .append(" FROM ").append(MObject.TABLE) .append(" WHERE ").append(MObject.COL_ID).append("=?"); mSqlQueryForRawById = sql.toString(); } } String[] selectionArgs = new String[] { Long.toString(id) }; Cursor c = db.rawQuery(mSqlQueryForRawById, selectionArgs); try { if (c.moveToFirst()) { if (!c.isNull(0)) return c.getBlob(0); } return null; } finally { c.close(); } } SQLiteStatement mSqlGetFdForRaw; public FileDescriptor getFileDescriptorForRaw(long objId) { if (Build.VERSION.SDK_INT < Build.VERSION_CODES.HONEYCOMB) { return null; } SQLiteDatabase db = initializeDatabase(); if (mSqlGetFdForRaw == null) { synchronized (this) { if (mSqlGetFdForRaw == null) { StringBuilder sql = new StringBuilder(100) .append("SELECT ").append(MObject.COL_RAW) .append(" FROM ").append(MObject.TABLE) .append(" WHERE ").append(MObject.COL_ID).append("=?"); mSqlGetFdForRaw = db.compileStatement(sql.toString()); } } } synchronized (mSqlGetFdForRaw) { mSqlGetFdForRaw.bindLong(1, objId); try { ParcelFileDescriptor pfd = mSqlGetFdForRaw.simpleQueryForBlobFileDescriptor(); if (pfd == null) { return null; } return pfd.getFileDescriptor(); } catch (SQLiteDoneException e) { return null; } } } public MObject getObjectWithoutRawForId(long id) { SQLiteDatabase db = initializeDatabase(); if (mSqlQueryObjectWithoutRawForId == null) { synchronized (this) { StringBuilder sql = new StringBuilder(100).append("SELECT "); for (String c : STANDARD_FIELDS) { if (c.equals(MObject.COL_RAW)) { sql.append("NULL,"); } else { sql.append(c).append(","); } } sql.setLength(sql.length() - 1); sql.append(" FROM ").append(MObject.TABLE) .append(" WHERE ").append(MObject.COL_ID).append("=?"); mSqlQueryObjectWithoutRawForId = sql.toString(); } } String[] selectionArgs = new String[] { String.valueOf(id) }; Cursor c = db.rawQuery(mSqlQueryObjectWithoutRawForId, selectionArgs); try { if (c.moveToNext()) { return fillInStandardFields(c); } return null; } finally { c.close(); } } public boolean delete(long id) { SQLiteDatabase db = initializeDatabase(); String whereClause = MObject.COL_ID + " = ?"; String[] whereArgs = new String[] { Long.toString(id) }; return db.delete(MObject.TABLE, whereClause, whereArgs) > 0; } private MObject fillInStandardFields(Cursor c) { MObject obj = new MObject(); obj.id_ = c.getLong(_id); obj.feedId_ = c.getLong(feedId); obj.identityId_ = c.getLong(identityId); obj.deviceId_ = c.getLong(deviceId); obj.parentId_ = c.isNull(parentId) ? null : c.getLong(parentId); obj.appId_ = c.getLong(appId); obj.timestamp_ = c.getLong(timestamp); obj.universalHash_ = c.getBlob(universalHash); obj.shortUniversalHash_ = c.isNull(shortHash) ? null : c.getLong(shortHash); obj.type_ = c.getString(type); obj.json_ = c.getString(json); obj.raw_ = c.getBlob(raw); obj.intKey_ = c.isNull(intKey) ? null : c.getInt(intKey); obj.stringKey_ = c.getString(stringKey); obj.lastModifiedTimestamp_ = c.isNull(lastModified) ? null : c.getLong(lastModified); obj.encodedId_ = c.isNull(encodedId) ? null : c.getLong(encodedId); obj.deleted_ = c.getInt(deleted) == 1; obj.renderable_ = c.getInt(renderable) == 1; obj.processed_ = c.getInt(processed) == 1; return obj; } private void bindStandardFields(SQLiteStatement statement, MObject obj) { assert(obj.type_ != null); statement.bindLong(feedId, obj.feedId_); statement.bindLong(identityId, obj.identityId_); statement.bindLong(deviceId, obj.deviceId_); if (obj.parentId_ == null) statement.bindNull(parentId); else statement.bindLong(parentId, obj.parentId_); statement.bindLong(appId, obj.appId_); statement.bindLong(timestamp, obj.timestamp_); if (obj.universalHash_ == null) statement.bindNull(universalHash); else statement.bindBlob(universalHash, obj.universalHash_); if (obj.shortUniversalHash_ == null) statement.bindNull(shortHash); else statement.bindLong(shortHash, obj.shortUniversalHash_); statement.bindString(type, obj.type_); if (obj.json_ == null) statement.bindNull(json); else statement.bindString(json, obj.json_); if (obj.raw_ == null) statement.bindNull(raw); else statement.bindBlob(raw, obj.raw_); if (obj.intKey_ == null) statement.bindNull(intKey); else statement.bindLong(intKey, obj.intKey_); if (obj.stringKey_ == null) statement.bindNull(stringKey); else statement.bindString(stringKey, obj.stringKey_); statement.bindLong(lastModified, obj.lastModifiedTimestamp_); if (obj.encodedId_ == null) statement.bindNull(encodedId); else statement.bindLong(encodedId, obj.encodedId_); statement.bindLong(deleted, obj.deleted_ ? 1 : 0); statement.bindLong(renderable, obj.renderable_ ? 1 : 0); statement.bindLong(processed, obj.processed_ ? 1 : 0); } private void bindStandardFieldsThenId(SQLiteStatement statement, MObject obj) { bindStandardFields(statement, obj); statement.bindLong(processed + 1, obj.id_); } public long getObjectIdForHash(byte[] hash) { SQLiteDatabase db = initializeDatabase(); if (mSqlGetObjIdByHash == null) { synchronized (this) { if(mSqlGetObjIdByHash == null) { String sql = new StringBuilder() .append("SELECT ").append(MObject.COL_ID).append(" FROM ").append(MObject.TABLE).append(" WHERE ") .append(MObject.COL_SHORT_UNIVERSAL_HASH).append("=?").append(" AND ") .append(MObject.COL_UNIVERSAL_HASH).append("=?").toString(); mSqlGetObjIdByHash = db.compileStatement(sql); } } } synchronized (mSqlGetObjIdByHash) { mSqlGetObjIdByHash.bindLong(1, Util.shortHash(hash)); mSqlGetObjIdByHash.bindBlob(2, hash); try { return mSqlGetObjIdByHash.simpleQueryForLong(); } catch(SQLiteDoneException e) { return -1; } } } public long getTotalCountOfObjects() { SQLiteDatabase db = initializeDatabase(); if (mSqlObjectCount == null) { synchronized (this) { if(mSqlObjectCount == null) { String sql = new StringBuilder() .append("SELECT COUNT(*) FROM ").append(MObject.TABLE).toString(); mSqlObjectCount = db.compileStatement(sql); } } } synchronized (mSqlObjectCount) { return mSqlObjectCount.simpleQueryForLong(); } } public boolean isObjectFromLocalDevice(long objectId) { MObject object = this.getObjectForId(objectId); DeviceManager deviceMan = new DeviceManager(mDatabase); MDevice device = deviceMan.getDeviceForId(object.deviceId_); long myDeviceName = deviceMan.getLocalDeviceName(); if (device.deviceName_ != myDeviceName) { return false; } IdentitiesManager idMan = new IdentitiesManager(mDatabase); MIdentity senderId = idMan.getIdentityForId(device.identityId_); return senderId != null && senderId.owned_; } public Long getLatestFeedRenderable(long feed_id) { SQLiteDatabase db = initializeDatabase(); if (mSqlGetLatestRenderableId == null) { synchronized (this) { if(mSqlGetLatestRenderableId == null) { String sql = new StringBuilder() .append("SELECT ").append(MObject.COL_ID).append(" FROM ").append(MObject.TABLE).append(" WHERE ") .append(MObject.COL_RENDERABLE).append("=1 AND ").append(MObject.COL_FEED_ID).append("=? ORDER BY ") .append(MObject.COL_LAST_MODIFIED_TIMESTAMP).append(" DESC LIMIT 1") .toString(); mSqlGetLatestRenderableId = db.compileStatement(sql); } } } synchronized (mSqlGetLatestRenderableId) { mSqlGetLatestRenderableId.bindLong(1, feed_id); try { return mSqlGetLatestRenderableId.simpleQueryForLong(); } catch(SQLiteDoneException e) { return null; } } } public Cursor getIdCursorForFeed(long feedId) { SQLiteDatabase db = initializeDatabase(); StringBuilder sql = new StringBuilder(100).append("SELECT ").append(MObject.COL_ID) .append(" FROM ").append(MObject.TABLE) .append(" WHERE ").append(MObject.COL_FEED_ID).append("=?"); String query = sql.toString(); String[] selectionArgs = new String[] { String.valueOf(feedId) }; return db.rawQuery(query, selectionArgs); } public Cursor getTypedIdCursorForFeed(String type, long feedId) { SQLiteDatabase db = initializeDatabase(); StringBuilder sql = new StringBuilder(100).append("SELECT ").append(MObject.COL_ID) .append(" FROM ").append(MObject.TABLE) .append(" WHERE ").append(MObject.COL_FEED_ID).append("=? AND ").append(MObject.COL_TYPE).append("=?") .append(" ORDER BY ").append(MObject.COL_LAST_MODIFIED_TIMESTAMP).append(" ASC"); String query = sql.toString(); String[] selectionArgs = new String[] { String.valueOf(feedId), type }; return db.rawQuery(query, selectionArgs); } public int getLikeCount(long objId) { SQLiteDatabase db = initializeDatabase(); if (mSqlLikeCount == null) { synchronized(this) { if (mSqlLikeCount == null) { StringBuilder sql = new StringBuilder(80) .append("SELECT ").append(DbLikeCache.COUNT) .append(" FROM ").append(DbLikeCache.TABLE) .append(" WHERE ").append(DbLikeCache.PARENT_OBJ).append("=?"); mSqlLikeCount = db.compileStatement(sql.toString()); } } } synchronized(mSqlLikeCount) { mSqlLikeCount.bindLong(1, objId); try { return (int)mSqlLikeCount.simpleQueryForLong(); } catch (SQLiteDoneException e) { return 0; } } } public long[] objectsToEncode() { if (mSqlGetObjectsToEncode == null) { mSqlGetObjectsToEncode = new StringBuilder(80) .append("SELECT ").append(MObject.COL_ID) .append(" FROM ").append(MObject.TABLE) .append(" WHERE ").append(MObject.COL_ENCODED_ID).append(" is null").toString(); } Cursor c = initializeDatabase().rawQuery(mSqlGetObjectsToEncode, null); int i = 0; long[] ids = new long[c.getCount()]; while (c.moveToNext()) { ids[i++] = c.getLong(0); } try { return ids; } finally { c.close(); } } @Override public synchronized void close() { if (mSqlInsertObj != null) { mSqlInsertObj.close(); mSqlInsertObj = null; } if (mSqlUpdateObj != null) { mSqlUpdateObj.close(); mSqlUpdateObj = null; } if (mSqlGetObjIdByHash != null) { mSqlGetObjIdByHash.close(); mSqlGetObjIdByHash = null; } if (mSqlObjectCount != null) { mSqlObjectCount.close(); mSqlObjectCount = null; } if (mSqlGetLatestRenderableId != null) { mSqlGetLatestRenderableId.close(); mSqlGetLatestRenderableId = null; } if (mSqlLikeCount != null) { mSqlLikeCount.close(); mSqlLikeCount = null; } if (mSqlUpdateObjPipelineMetadata != null) { mSqlUpdateObjPipelineMetadata.close(); mSqlUpdateObjPipelineMetadata = null; } if (mSqlUpdateObjEncodeMetadata != null) { mSqlUpdateObjEncodeMetadata.close(); mSqlUpdateObjEncodeMetadata = null; } } }