package by.istin.android.xcore.gson; import android.content.ContentValues; import android.database.Cursor; import android.provider.BaseColumns; import com.google.gson.JsonArray; import com.google.gson.JsonDeserializationContext; import com.google.gson.JsonObject; import java.lang.reflect.Type; import by.istin.android.xcore.ContextHolder; import by.istin.android.xcore.annotations.dbEntities; import by.istin.android.xcore.db.IDBConnection; import by.istin.android.xcore.db.IDBConnector; import by.istin.android.xcore.db.entity.IBeforeArrayUpdate; import by.istin.android.xcore.db.entity.IGenerateID; import by.istin.android.xcore.db.impl.DBHelper; import by.istin.android.xcore.provider.IDBContentProviderSupport; import by.istin.android.xcore.source.DataSourceRequest; import by.istin.android.xcore.utils.ReflectUtils; public class DBContentValuesAdapter extends ContentValuesAdapter { private final IDBConnection dbConnection; private ITransactionCreationController transactionCreationController; private final DBHelper dbHelper; private final DataSourceRequest dataSourceRequest; private int count = 0; private final IBeforeArrayUpdate beforeListUpdate; private IGenerateID generateID; private String foreignKey; public static class WritableConnectionWrapper implements IDBConnection { private IDBConnection connection; private final IDBConnector connector; WritableConnectionWrapper(IDBConnector connector, IDBConnection connection) { this.connection = connection; this.connector = connector; } @Override public void execSQL(String sql) { connection.execSQL(sql); } @Override public Cursor query(String table, String[] projection, String selection, String[] selectionArgs, String groupBy, String having, String sortOrder, String limit) { return connection.query(table, projection, selection, selectionArgs, groupBy, having, sortOrder, limit); } @Override public boolean isExists(String tableName) { return connection.isExists(tableName); } @Override public Cursor rawQuery(String sql, String[] selectionArgs) { return connection.rawQuery(sql, selectionArgs); } @Override public int delete(String tableName, String where, String[] whereArgs) { return connection.delete(tableName, where, whereArgs); } @Override public long insert(String tableName, ContentValues contentValues) { return connection.insert(tableName, contentValues); } @Override public void beginTransaction() { connection.beginTransaction(); } @Override public void setTransactionSuccessful() { connection.setTransactionSuccessful(); } @Override public void endTransaction() { connection.endTransaction(); } @Override public int update(String tableName, ContentValues contentValues, String selection, String[] selectionArgs) { return connection.update(tableName, contentValues, selection, selectionArgs); } protected void doneAndCreateNewTransaction() { setTransactionSuccessful(); endTransaction(); connection = connector.getWritableConnection(); connection.beginTransaction(); } } public static interface ITransactionCreationController { boolean isCreateNewTransaction(int count); void onTransactionRecreated(); } public DBContentValuesAdapter(Class<?> contentValuesClass, DataSourceRequest dataSourceRequest, IDBContentProviderSupport dbContentProvider) { this(contentValuesClass, dataSourceRequest, dbContentProvider, null); } public DBContentValuesAdapter(Class<?> contentValuesClass, DataSourceRequest dataSourceRequest, IDBContentProviderSupport dbContentProvider, ITransactionCreationController contentValuesAdapterTransactionListener) { super(contentValuesClass); IDBConnector connector = dbContentProvider.getDbSupport().createConnector(ContextHolder.getInstance().getContext()); IDBConnection writableConnection = connector.getWritableConnection(); this.transactionCreationController = contentValuesAdapterTransactionListener; this.dbConnection = new WritableConnectionWrapper(connector, writableConnection); this.dbHelper = dbContentProvider.getDbSupport().getOrCreateDBHelper(ContextHolder.getInstance().getContext()); this.dataSourceRequest = dataSourceRequest; this.beforeListUpdate = ReflectUtils.getInstanceInterface(contentValuesClass, IBeforeArrayUpdate.class); this.generateID = ReflectUtils.getInstanceInterface(getContentValuesEntityClazz(), IGenerateID.class); this.foreignKey = DBHelper.getForeignKey(getContentValuesEntityClazz()); } public DBContentValuesAdapter(Class<?> contentValuesClass, DataSourceRequest dataSourceRequest, IDBConnection dbConnection, DBHelper dbHelper) { super(contentValuesClass); this.dbConnection = dbConnection; this.dbHelper = dbHelper; this.dataSourceRequest = dataSourceRequest; this.beforeListUpdate = ReflectUtils.getInstanceInterface(contentValuesClass, IBeforeArrayUpdate.class); this.generateID = ReflectUtils.getInstanceInterface(getContentValuesEntityClazz(), IGenerateID.class); this.foreignKey = DBHelper.getForeignKey(getContentValuesEntityClazz()); } public IDBConnection getDbConnection() { return this.dbConnection; } @Override protected void proceedSubEntities(Type type, JsonDeserializationContext jsonDeserializationContext, ContentValues contentValues, ReflectUtils.XField field, String fieldValue, JsonArray jsonArray) { dbEntities entity = ReflectUtils.getAnnotation(field, dbEntities.class); Class<?> clazz = entity.clazz(); IBeforeArrayUpdate beforeListUpdate = ReflectUtils.getInstanceInterface(clazz, IBeforeArrayUpdate.class); Long id = getParentId(contentValues); Class<? extends IGsonEntitiesConverter> jsonConverter = entity.jsonConverter(); IGsonEntitiesConverter gsonEntityConverter = ReflectUtils.getInstanceInterface(jsonConverter, IGsonEntitiesConverter.class); gsonEntityConverter.convert(new IGsonEntitiesConverter.Params( beforeListUpdate, type, jsonDeserializationContext, contentValues, clazz, field, dataSourceRequest, dbConnection, dbHelper, fieldValue, jsonArray, foreignKey, id, entity, count) ); } @Override protected void proceedSubEntity(Type type, JsonDeserializationContext jsonDeserializationContext, ContentValues contentValues, ReflectUtils.XField field, String fieldValue, Class<?> clazz, JsonObject subEntityJsonObject) { DBContentValuesAdapter contentValuesAdapter = new DBContentValuesAdapter(clazz, dataSourceRequest, dbConnection, dbHelper); ContentValues values = contentValuesAdapter.deserializeContentValues(contentValues, UNKNOWN_POSITION, subEntityJsonObject, type, jsonDeserializationContext); Long id = getParentId(contentValues); values.put(foreignKey, id); dbHelper.updateOrInsert(dataSourceRequest, dbConnection, clazz, values); } private Long getParentId(ContentValues contentValues) { Long id = contentValues.getAsLong(BaseColumns._ID); if (id == null) { if (generateID == null) { throw new IllegalStateException("can not put sub entity without parent id, use IGenerateID.class for generate ID for "+ getContentValuesEntityClazz()); } id = generateID.generateId(dbHelper, dbConnection, dataSourceRequest, contentValues); contentValues.put(BaseColumns._ID, id); } return id; } @Override protected ContentValues proceed(ContentValues parent, int position, ContentValues contentValues) { if (parent == null) { if (contentValues == null) { return null; } if (beforeListUpdate != null) { beforeListUpdate.onBeforeListUpdate(dbHelper, dbConnection, dataSourceRequest, count, contentValues); } dbHelper.updateOrInsert(dataSourceRequest, dbConnection, getContentValuesEntityClazz(), contentValues); if (transactionCreationController != null) { if (transactionCreationController.isCreateNewTransaction(count)) { if (dbConnection instanceof WritableConnectionWrapper) { ((WritableConnectionWrapper) dbConnection).doneAndCreateNewTransaction(); transactionCreationController.onTransactionRecreated(); } } } count++; } return contentValues; } }