summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorKonstantin Käfer <mail@kkaefer.com>2017-02-14 14:50:11 +0100
committerKonstantin Käfer <mail@kkaefer.com>2017-02-14 14:50:11 +0100
commit685052a32054f5e822106f056c21bf3823ad0d3f (patch)
tree811dad50985663aafc9da6c4551cf03f71516879
parentb9be66f287ade7275ecc18f21a456998e4163318 (diff)
downloadqtlocation-mapboxgl-upstream/8048-android-sqlite.tar.gz
WIP: Android SQLite bindingsupstream/8048-android-sqlite
-rw-r--r--platform/android/config.cmake9
-rw-r--r--platform/android/src/android/database/sqlite/sqlite_database.cpp38
-rw-r--r--platform/android/src/android/database/sqlite/sqlite_database.hpp41
-rw-r--r--platform/android/src/android/database/sqlite/sqlite_exception.cpp28
-rw-r--r--platform/android/src/android/database/sqlite/sqlite_exception.hpp39
-rw-r--r--platform/android/src/java/lang/throwable.cpp23
-rw-r--r--platform/android/src/java/lang/throwable.hpp25
-rw-r--r--platform/android/src/sqlite3.cpp121
8 files changed, 323 insertions, 1 deletions
diff --git a/platform/android/config.cmake b/platform/android/config.cmake
index 9491071f2a..0cd8a80ec4 100644
--- a/platform/android/config.cmake
+++ b/platform/android/config.cmake
@@ -53,7 +53,11 @@ macro(mbgl_platform_core)
PRIVATE platform/default/mbgl/storage/offline_database.hpp
PRIVATE platform/default/mbgl/storage/offline_download.cpp
PRIVATE platform/default/mbgl/storage/offline_download.hpp
- PRIVATE platform/default/sqlite3.cpp
+ PRIVATE platform/android/src/android/database/sqlite/sqlite_database.cpp
+ PRIVATE platform/android/src/android/database/sqlite/sqlite_database.hpp
+ PRIVATE platform/android/src/android/database/sqlite/sqlite_exception.cpp
+ PRIVATE platform/android/src/android/database/sqlite/sqlite_exception.hpp
+ PRIVATE platform/android/src/sqlite3.cpp
PRIVATE platform/default/sqlite3.hpp
# Misc
@@ -142,6 +146,9 @@ macro(mbgl_platform_core)
platform/android/src/attach_env.hpp
platform/android/src/java_types.cpp
platform/android/src/java_types.hpp
+ platform/android/src/java/lang.hpp
+ platform/android/src/java/lang/throwable.cpp
+ platform/android/src/java/lang/throwable.hpp
# Main entry point
platform/android/src/jni.hpp
diff --git a/platform/android/src/android/database/sqlite/sqlite_database.cpp b/platform/android/src/android/database/sqlite/sqlite_database.cpp
new file mode 100644
index 0000000000..51e686dce6
--- /dev/null
+++ b/platform/android/src/android/database/sqlite/sqlite_database.cpp
@@ -0,0 +1,38 @@
+#include "sqlite_database.hpp"
+
+namespace mbgl {
+namespace android {
+namespace android {
+namespace database {
+namespace sqlite {
+
+jni::Class<SQLiteDatabase> SQLiteDatabase::Class(jni::JNIEnv& env) {
+ static auto clazz = *jni::Class<SQLiteDatabase>::Find(env).NewGlobalRef(env).release();
+ return clazz;
+}
+
+jni::Object<SQLiteDatabase>
+SQLiteDatabase::OpenDatabase(jni::JNIEnv& env, jni::String path, jni::jint flags) {
+ using Signature =
+ jni::Object<SQLiteDatabase>(jni::String, jni::Object<CursorFactory>, jni::jint);
+ static auto method = Class(env).GetStaticMethod<Signature>(env, "openDatabase");
+ return Class(env).Call(env, method, path, jni::Object<CursorFactory>(nullptr), flags);
+}
+
+void SQLiteDatabase::ExecSQL(jni::JNIEnv& env, jni::Object<SQLiteDatabase> db, jni::String sql) {
+ using Signature = void(jni::String);
+ static auto method = Class(env).GetMethod<Signature>(env, "execSQL");
+ return db.Call(env, method, sql);
+}
+
+void SQLiteDatabase::Close(jni::JNIEnv& env, jni::Object<SQLiteDatabase> db) {
+ using Signature = void(void);
+ static auto method = Class(env).GetMethod<Signature>(env, "close");
+ return db.Call(env, method);
+}
+
+} // namespace sqlite
+} // namespace database
+} // namespace android
+} // namespace android
+} // namespace mbgl
diff --git a/platform/android/src/android/database/sqlite/sqlite_database.hpp b/platform/android/src/android/database/sqlite/sqlite_database.hpp
new file mode 100644
index 0000000000..d553a95ef0
--- /dev/null
+++ b/platform/android/src/android/database/sqlite/sqlite_database.hpp
@@ -0,0 +1,41 @@
+#pragma once
+
+#include <jni/jni.hpp>
+
+
+namespace mbgl {
+namespace android {
+namespace android {
+namespace database {
+namespace sqlite {
+
+class SQLiteDatabase : public jni::jobject {
+public:
+ static constexpr auto Name() {
+ return "android/database/sqlite/SQLiteDatabase";
+ }
+ static jni::Class<SQLiteDatabase> Class(jni::JNIEnv&);
+
+ class CursorFactory {
+ public:
+ static constexpr auto Name() {
+ return "android/database/sqlite/SQLiteDatabase$CursorFactory";
+ }
+ };
+
+ static constexpr const int OPEN_READWRITE = 0x00000000;
+ static constexpr const int OPEN_READONLY = 0x00000001;
+ static constexpr const int CREATE_IF_NECESSARY = 0x10000000;
+
+ static jni::Object<SQLiteDatabase>
+ OpenDatabase(jni::JNIEnv&, jni::String path, jni::jint flags);
+
+ static void ExecSQL(jni::JNIEnv&, jni::Object<SQLiteDatabase>, jni::String sql);
+ static void Close(jni::JNIEnv&, jni::Object<SQLiteDatabase>);
+};
+
+} // namespace sqlite
+} // namespace database
+} // namespace android
+} // namespace android
+} // namespace mbgl
diff --git a/platform/android/src/android/database/sqlite/sqlite_exception.cpp b/platform/android/src/android/database/sqlite/sqlite_exception.cpp
new file mode 100644
index 0000000000..c63f440cd1
--- /dev/null
+++ b/platform/android/src/android/database/sqlite/sqlite_exception.cpp
@@ -0,0 +1,28 @@
+#include "sqlite_exception.hpp"
+
+namespace mbgl {
+namespace android {
+namespace android {
+namespace database {
+namespace sqlite {
+
+jni::Class<SQLiteException> SQLiteException::Class(jni::JNIEnv& env) {
+ static auto clazz = *jni::Class<SQLiteException>::Find(env).NewGlobalRef(env).release();
+ return clazz;
+}
+
+jni::Class<SQLiteCantOpenDatabaseException> SQLiteCantOpenDatabaseException::Class(jni::JNIEnv& env) {
+ static auto clazz = *jni::Class<SQLiteCantOpenDatabaseException>::Find(env).NewGlobalRef(env).release();
+ return clazz;
+}
+
+jni::Class<SQLiteDatabaseCorruptException> SQLiteDatabaseCorruptException::Class(jni::JNIEnv& env) {
+ static auto clazz = *jni::Class<SQLiteDatabaseCorruptException>::Find(env).NewGlobalRef(env).release();
+ return clazz;
+}
+
+} // namespace sqlite
+} // namespace database
+} // namespace android
+} // namespace android
+} // namespace mbgl
diff --git a/platform/android/src/android/database/sqlite/sqlite_exception.hpp b/platform/android/src/android/database/sqlite/sqlite_exception.hpp
new file mode 100644
index 0000000000..b496f1cf1c
--- /dev/null
+++ b/platform/android/src/android/database/sqlite/sqlite_exception.hpp
@@ -0,0 +1,39 @@
+#pragma once
+
+#include "../../../java/lang/throwable.hpp"
+
+namespace mbgl {
+namespace android {
+namespace android {
+namespace database {
+namespace sqlite {
+
+class SQLiteException : public java::lang::Throwable {
+public:
+ static constexpr auto Name() {
+ return "android/database/sqlite/SQLiteException";
+ }
+ static jni::Class<SQLiteException> Class(jni::JNIEnv&);
+};
+
+class SQLiteCantOpenDatabaseException : public SQLiteException {
+public:
+ static constexpr auto Name() {
+ return "android/database/sqlite/SQLiteCantOpenDatabaseException";
+ }
+ static jni::Class<SQLiteCantOpenDatabaseException> Class(jni::JNIEnv&);
+};
+
+class SQLiteDatabaseCorruptException : public SQLiteException {
+public:
+ static constexpr auto Name() {
+ return "android/database/sqlite/SQLiteDatabaseCorruptException";
+ }
+ static jni::Class<SQLiteDatabaseCorruptException> Class(jni::JNIEnv&);
+};
+
+} // namespace sqlite
+} // namespace database
+} // namespace android
+} // namespace android
+} // namespace mbgl
diff --git a/platform/android/src/java/lang/throwable.cpp b/platform/android/src/java/lang/throwable.cpp
new file mode 100644
index 0000000000..12df5da27e
--- /dev/null
+++ b/platform/android/src/java/lang/throwable.cpp
@@ -0,0 +1,23 @@
+#include "throwable.hpp"
+
+namespace mbgl {
+namespace android {
+namespace java {
+namespace lang {
+
+jni::Class<Throwable> Throwable::Class(jni::JNIEnv& env) {
+ static auto clazz = *jni::Class<Throwable>::Find(env).NewGlobalRef(env).release();
+ return clazz;
+}
+
+jni::String
+Throwable::GetMessage(jni::JNIEnv& env, jni::Object<Throwable> throwable) {
+ using Signature = jni::String(void);
+ static auto method = Class(env).GetMethod<Signature>(env, "getMessage");
+ return throwable.Call(env, method);
+}
+
+} // namespace lang
+} // namespace java
+} // namespace android
+} // namespace mbgl
diff --git a/platform/android/src/java/lang/throwable.hpp b/platform/android/src/java/lang/throwable.hpp
new file mode 100644
index 0000000000..e27c841bae
--- /dev/null
+++ b/platform/android/src/java/lang/throwable.hpp
@@ -0,0 +1,25 @@
+#pragma once
+
+#include <jni/jni.hpp>
+
+namespace mbgl {
+namespace android {
+namespace java {
+namespace lang {
+
+class Throwable {
+public:
+ static constexpr auto Name() {
+ return "java/lang/Throwable";
+ }
+
+ static jni::String GetMessage(jni::JNIEnv&, jni::Object<Throwable>);
+
+private:
+ static jni::Class<Throwable> Class(jni::JNIEnv&);
+};
+
+} // namespace lang
+} // namespace java
+} // namespace android
+} // namespace mbgl
diff --git a/platform/android/src/sqlite3.cpp b/platform/android/src/sqlite3.cpp
new file mode 100644
index 0000000000..e316ea9890
--- /dev/null
+++ b/platform/android/src/sqlite3.cpp
@@ -0,0 +1,121 @@
+#include "../default/sqlite3.hpp"
+
+#include "android/database/sqlite/sqlite_database.hpp"
+#include "android/database/sqlite/sqlite_exception.hpp"
+#include "java/lang/throwable.hpp"
+
+#include "attach_env.hpp"
+
+using namespace mbgl::android;
+
+using android::database::sqlite::SQLiteDatabase;
+using android::database::sqlite::SQLiteCantOpenDatabaseException;
+using android::database::sqlite::SQLiteDatabaseCorruptException;
+using java::lang::Throwable;
+
+namespace mapbox {
+namespace sqlite {
+
+template <typename Lambda>
+void checkException(jni::JNIEnv& env, Lambda&& lambda) {
+ try {
+ lambda();
+ } catch (const jni::PendingJavaException&) {
+ auto throwable = jni::Object<Throwable>(jni::ExceptionOccurred(env));
+ jni::ExceptionClear(env);
+ const std::string message =
+ jni::Make<std::string>(env, Throwable::GetMessage(env, throwable));
+ if (throwable.IsInstanceOf(env, SQLiteCantOpenDatabaseException::Class(env))) {
+ throw Exception{ Exception::Code::CANTOPEN, message };
+ } else if (throwable.IsInstanceOf(env, SQLiteDatabaseCorruptException::Class(env))) {
+ throw Exception{ Exception::Code::NOTADB, message };
+ } else {
+ throw Exception{ 1 /* SQLITE_ERROR */, message };
+ }
+ }
+}
+
+class DatabaseImpl {
+public:
+ DatabaseImpl(const std::string& filename, OpenFlag openFlags) {
+ int flags = 0;
+ if (openFlags & OpenFlag::ReadOnly) {
+ flags |= SQLiteDatabase::OPEN_READONLY;
+ }
+ if (openFlags & OpenFlag::ReadWrite) {
+ flags |= SQLiteDatabase::OPEN_READWRITE;
+ }
+ if (openFlags & OpenFlag::Create) {
+ flags |= SQLiteDatabase::CREATE_IF_NECESSARY;
+ }
+
+ checkException(*env, [&] {
+ db = SQLiteDatabase::OpenDatabase(*env, jni::Make<jni::String>(*env, filename), flags).NewGlobalRef(*env);
+ });
+ }
+
+ UniqueEnv env{ AttachEnv() };
+ jni::UniqueObject<android::database::sqlite::SQLiteDatabase> db;
+};
+
+Database::Database(const std::string& filename, int flags)
+ : impl(std::make_unique<DatabaseImpl>(filename, OpenFlag(flags))) {
+}
+
+Database::Database(Database&& other) : impl(std::move(other.impl)) {
+}
+
+Database& Database::operator=(Database&& other) {
+ std::swap(impl, other.impl);
+ return *this;
+}
+
+Database::~Database() {
+}
+
+Database::operator bool() const {
+ return impl.operator bool();
+}
+
+void Database::setBusyTimeout(std::chrono::milliseconds timeout) {
+ // Java bindings don't support setting a direct busy_timeout.
+ exec(std::string{ "PRAGMA busy_timeout = " } +
+ std::to_string(std::min<std::chrono::milliseconds::rep>(timeout.count(),
+ std::numeric_limits<int>::max())));
+}
+
+void Database::exec(const std::string &sql) {
+ assert(impl);
+ checkException(*impl->env, [&] {
+ SQLiteDatabase::ExecSQL(*impl->env, *impl->db, jni::Make<jni::String>(*impl->env, sql));
+ });
+}
+
+Statement Database::prepare(const char *query) {
+ assert(impl);
+ return Statement(this, query);
+}
+
+Statement::Statement(Database *db, const char *sql)
+ : impl(std::make_unique<StatementImpl>(db->impl->db, sql))
+{
+}
+
+Statement::Statement(Statement &&other) {
+ *this = std::move(other);
+}
+
+Statement &Statement::operator=(Statement &&other) {
+ std::swap(impl, other.impl);
+ return *this;
+}
+
+Statement::~Statement() {
+}
+
+Statement::operator bool() const {
+ return impl.operator bool();
+}
+
+} // namespace sqlite
+} // namespace mapbox