diff options
author | Konstantin Käfer <mail@kkaefer.com> | 2017-02-14 14:50:11 +0100 |
---|---|---|
committer | Konstantin Käfer <mail@kkaefer.com> | 2017-02-14 14:50:11 +0100 |
commit | 685052a32054f5e822106f056c21bf3823ad0d3f (patch) | |
tree | 811dad50985663aafc9da6c4551cf03f71516879 | |
parent | b9be66f287ade7275ecc18f21a456998e4163318 (diff) | |
download | qtlocation-mapboxgl-685052a32054f5e822106f056c21bf3823ad0d3f.tar.gz |
WIP: Android SQLite bindingsupstream/8048-android-sqlite
-rw-r--r-- | platform/android/config.cmake | 9 | ||||
-rw-r--r-- | platform/android/src/android/database/sqlite/sqlite_database.cpp | 38 | ||||
-rw-r--r-- | platform/android/src/android/database/sqlite/sqlite_database.hpp | 41 | ||||
-rw-r--r-- | platform/android/src/android/database/sqlite/sqlite_exception.cpp | 28 | ||||
-rw-r--r-- | platform/android/src/android/database/sqlite/sqlite_exception.hpp | 39 | ||||
-rw-r--r-- | platform/android/src/java/lang/throwable.cpp | 23 | ||||
-rw-r--r-- | platform/android/src/java/lang/throwable.hpp | 25 | ||||
-rw-r--r-- | platform/android/src/sqlite3.cpp | 121 |
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 |