All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.futureplatforms.kirin.android.db.AndroidDatabase Maven / Gradle / Ivy

The newest version!
package com.futureplatforms.kirin.android.db;

import java.util.List;
import java.util.Map;

import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;

import android.content.Context;
import android.database.AbstractWindowedCursor;
import android.database.Cursor;
import android.database.CursorWindow;
import android.database.sqlite.SQLiteCursor;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
import android.database.sqlite.SQLiteStatement;
import android.os.Build;

import com.futureplatforms.kirin.android.json.AndroidJSONArray;
import com.futureplatforms.kirin.dependencies.db.Database;
import com.futureplatforms.kirin.dependencies.db.DatabaseDelegate;
import com.futureplatforms.kirin.dependencies.db.Transaction.InsertStatement;
import com.futureplatforms.kirin.dependencies.db.Transaction.RowSet;
import com.futureplatforms.kirin.dependencies.db.Transaction.Statement;
import com.futureplatforms.kirin.dependencies.db.Transaction.StatementWithJSONReturn;
import com.futureplatforms.kirin.dependencies.db.Transaction.StatementWithRowsReturn;
import com.futureplatforms.kirin.dependencies.db.Transaction.StatementWithTokenReturn;
import com.futureplatforms.kirin.dependencies.db.Transaction.TxElementType;
import com.futureplatforms.kirin.dependencies.db.Transaction.TxJSONCB;
import com.futureplatforms.kirin.dependencies.db.Transaction.TxRowsCB;
import com.futureplatforms.kirin.dependencies.db.Transaction.TxTokenCB;
import com.futureplatforms.kirin.dependencies.db.Transaction.UpdateStatement;
import com.futureplatforms.kirin.dependencies.internal.TransactionBackend;
import com.futureplatforms.kirin.dependencies.internal.TransactionBundle;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;

public class AndroidDatabase implements DatabaseDelegate {

	private class Helper extends SQLiteOpenHelper {
		public Helper(String name) {
			super(_Context, name, null, 1);
		}

		@Override
		public void onCreate(SQLiteDatabase db) {}

		@Override
		public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {}
	}

	private final Context _Context;

	public AndroidDatabase(Context context) {
		this._Context = context;
	}

	private static class AndroidDatabaseImpl extends Database {

		private SQLiteDatabase db;
		private final CursorCoercer _Coercer;

		public AndroidDatabaseImpl(SQLiteDatabase db) {
			this.db = db;
			if (Build.VERSION.SDK_INT >= 5) {
				_Coercer = new CursorCoercer5();
			} else {
				_Coercer = new CursorCoercer4();
			}
		}

		private String[] columnNames(Cursor cursor) {
			String[] cols = cursor.getColumnNames();

			for (int i = 0, count = cols.length; i < count; i++) {
				String columnName = cols[i];

				int dot = columnName.indexOf('.');
				if (dot >= 0) {
					// assume that the column name will never end in dot.
					columnName = columnName.substring(dot + 1);
				}
				cols[i] = columnName;

			}
			return cols;
		}

		private JSONObject coerceToJSONObject(Cursor cursor) {
			String[] cols = columnNames(cursor);
			if (cursor instanceof AbstractWindowedCursor) {
				return _Coercer.coerceToJSONObject(cols, (AbstractWindowedCursor) cursor);
			} else {
				JSONObject obj = new JSONObject();
				for (int i = 0; i < cols.length; i++) {
					try {
						if (!cursor.isNull(i)) {
							obj.put(cols[i], cursor.getString(i));
						}
					} catch (JSONException e) {}

				}
				return obj;
			}

		}

		private JSONArray coerceToJSONArray(Cursor cursor) {
			JSONArray array = new JSONArray();

			while (cursor.moveToNext()) {
				array.put(coerceToJSONObject(cursor));
			}
			return array;
		}

		@Override
		protected void performTransaction(TransactionCallback cb) {
			cb.onSuccess(new TransactionBackend() {

				@Override
				public void pullTrigger(TransactionBundle bundle) {
					db.beginTransaction();
					try {
						int statementCount = 0, batchCount = 0;
						for (TxElementType type : bundle._Types) {
							if (type == TxElementType.Statement) {
								Statement st = bundle._Statements.get(statementCount);
								statementCount++;

								// Execute the statement
								if (st instanceof InsertStatement) executeInsert((InsertStatement) st);
								else if (st instanceof UpdateStatement) executeUpdate((UpdateStatement) st);
								else if (st instanceof StatementWithRowsReturn) executeQueryWithRowsReturn(st);
								else if (st instanceof StatementWithJSONReturn) executeQueryWithJsonReturn(st);
								else executeQueryWithTokenReturn(st);
							} else {
								// execute batch
								String[] batch = bundle._Batches.get(batchCount);
								for (String sql : batch) {
									db.execSQL(sql);
								}
								batchCount++;
							}
						}
						db.setTransactionSuccessful();
						db.endTransaction();
						bundle._ClosedCallback.onComplete();
					} catch (Exception e) {
						e.printStackTrace();
						db.endTransaction();
						bundle._ClosedCallback.onError();
					}
				}

			});

		}

		private void executeInsert(InsertStatement st) {
			SQLiteStatement sqLiteStatement = compileAndBindStatement(st);
			sqLiteStatement.executeInsert();
		}

		private void executeUpdate(UpdateStatement st) {
			SQLiteStatement sqLiteStatement = compileAndBindStatement(st);
			sqLiteStatement.execute();
		}

		private SQLiteStatement compileAndBindStatement(Statement st) {
			SQLiteStatement sqLiteStatement = db.compileStatement(st._SQL);
			String[] bindArgs = st._Params;
			if (bindArgs != null) {
				// loop copied from
				// SQLiteProgram.bindAllArgsAsStrings()
				// SQLite indexes are 1 to N, not 0 to N-1
				for (int i = bindArgs.length; i != 0; i--) {
					String arg = bindArgs[i - 1];
					if (arg == null) sqLiteStatement.bindNull(i);
					else sqLiteStatement.bindString(i, arg);
				}
			}
			return sqLiteStatement;
		}

		private void executeQueryWithRowsReturn(Statement st) throws Exception {
			Cursor cursor = db.rawQuery(st._SQL, st._Params);
			// rows return
			// construct RowSet from cursor
			ImmutableList columnNames = ImmutableList.copyOf(cursor.getColumnNames());
			RowSet rowset = new RowSet(columnNames);
			int colCount = cursor.getColumnCount();
			while (cursor.moveToNext()) {
				List values = Lists.newArrayList();
				for (int i = 0; i < colCount; i++) {
					// Everything has to be a
					// string...
					int entryType = getType(cursor, i);
					switch (entryType) {
						case FIELD_TYPE_BLOB: {
							values.add(new String(cursor.getBlob(i)));
						}
							break;

						case FIELD_TYPE_FLOAT: {
							values.add(String.valueOf(cursor.getDouble(i)));
						}
							break;

						case FIELD_TYPE_INTEGER: {
							values.add(String.valueOf(cursor.getLong(i)));
						}
							break;

						case FIELD_TYPE_NULL: {
							values.add(null);
						}
							break;

						case FIELD_TYPE_STRING: {
							values.add(cursor.getString(i));
						}
							break;
					}
				}
				rowset.addRow(values);
			}
			cursor.close();
			TxRowsCB c = ((StatementWithRowsReturn) st)._Callback;
			if (c != null) {
				c.onSuccess(rowset);
			}
		}

		private void executeQueryWithTokenReturn(Statement st) {
			Cursor cursor = db.rawQuery(st._SQL, st._Params);
			// token return -- stick it on the
			// dropbox!!
			String token = AndroidDbDropbox.getInstance().putCursor(cursor);
			TxTokenCB c = ((StatementWithTokenReturn) st)._Callback;
			if (c != null) {
				c.onSuccess(token);
			}
		}

		private void executeQueryWithJsonReturn(Statement st) {
			Cursor cursor = db.rawQuery(st._SQL, st._Params);
			JSONArray arr = coerceToJSONArray(cursor);
			cursor.close();
			TxJSONCB c = ((StatementWithJSONReturn) st)._Callback;
			if (c != null) {
				c.onSuccess(new AndroidJSONArray(arr));
			}
		}
	}

	Map dbHelperMap = Maps.newHashMap();

	@Override
	public void open(String filename, DatabaseOpenedCallback cb) {
		SQLiteOpenHelper helper = dbHelperMap.get(filename);
		if (helper == null) {
			helper = new Helper(filename);
			dbHelperMap.put(filename, helper);
		}
		SQLiteDatabase _db = null;
		try {
			_db = helper.getWritableDatabase();
		} catch (Exception e) {}
		if (_db != null) cb.onOpened(new AndroidDatabaseImpl(_db));
		else cb.onError();
	}

	protected static final int FIELD_TYPE_BLOB = 4;
	protected static final int FIELD_TYPE_FLOAT = 2;
	protected static final int FIELD_TYPE_INTEGER = 1;
	protected static final int FIELD_TYPE_NULL = 0;
	protected static final int FIELD_TYPE_STRING = 3;

	@SuppressWarnings("deprecation")
	static int getType(Cursor cursor, int i) throws Exception {
		SQLiteCursor sqLiteCursor = (SQLiteCursor) cursor;
		CursorWindow cursorWindow = sqLiteCursor.getWindow();
		int pos = cursor.getPosition();
		int type = -1;
		if (cursorWindow.isNull(pos, i)) {
			type = FIELD_TYPE_NULL;
		} else if (cursorWindow.isLong(pos, i)) {
			type = FIELD_TYPE_INTEGER;
		} else if (cursorWindow.isFloat(pos, i)) {
			type = FIELD_TYPE_FLOAT;
		} else if (cursorWindow.isString(pos, i)) {
			type = FIELD_TYPE_STRING;
		} else if (cursorWindow.isBlob(pos, i)) {
			type = FIELD_TYPE_BLOB;
		}

		return type;
	}
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy