diff --git a/android-kiwix-lib-publisher/.gitignore b/android-kiwix-lib-publisher/.gitignore new file mode 100644 index 0000000..2b75303 --- /dev/null +++ b/android-kiwix-lib-publisher/.gitignore @@ -0,0 +1,13 @@ +*.iml +.gradle +/local.properties +/.idea/caches +/.idea/libraries +/.idea/modules.xml +/.idea/workspace.xml +/.idea/navEditor.xml +/.idea/assetWizardSettings.xml +.DS_Store +/build +/captures +.externalNativeBuild diff --git a/android-kiwix-lib-publisher/build.gradle b/android-kiwix-lib-publisher/build.gradle new file mode 100644 index 0000000..1f6ee06 --- /dev/null +++ b/android-kiwix-lib-publisher/build.gradle @@ -0,0 +1,25 @@ +// Top-level build file where you can add configuration options common to all sub-projects/modules. + +buildscript { + repositories { + google() + jcenter() + + } + dependencies { + classpath 'com.android.tools.build:gradle:3.4.1' + // NOTE: Do not place your application dependencies here; they belong + // in the individual module build.gradle files + } +} + +allprojects { + repositories { + google() + jcenter() + } +} + +task clean(type: Delete) { + delete rootProject.buildDir +} diff --git a/android-kiwix-lib-publisher/gradle.properties b/android-kiwix-lib-publisher/gradle.properties new file mode 100644 index 0000000..85be9ea --- /dev/null +++ b/android-kiwix-lib-publisher/gradle.properties @@ -0,0 +1,15 @@ +# Project-wide Gradle settings. +# IDE (e.g. Android Studio) users: +# Gradle settings configured through the IDE *will override* +# any settings specified in this file. +# For more details on how to configure your build environment visit +# http://www.gradle.org/docs/current/userguide/build_environment.html +# Specifies the JVM arguments used for the daemon process. +# The setting is particularly useful for tweaking memory settings. +org.gradle.jvmargs=-Xmx1536m +# When configured, Gradle will run in incubating parallel mode. +# This option should only be used with decoupled projects. More details, visit +# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects +# org.gradle.parallel=true +# Kotlin code style for this project: "official" or "obsolete": +kotlin.code.style=official diff --git a/android-kiwix-lib-publisher/gradle/wrapper/gradle-wrapper.jar b/android-kiwix-lib-publisher/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index 0000000..f6b961f Binary files /dev/null and b/android-kiwix-lib-publisher/gradle/wrapper/gradle-wrapper.jar differ diff --git a/android-kiwix-lib-publisher/gradle/wrapper/gradle-wrapper.properties b/android-kiwix-lib-publisher/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 0000000..b2a63df --- /dev/null +++ b/android-kiwix-lib-publisher/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,6 @@ +#Wed Jun 19 15:28:39 BST 2019 +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-5.1.1-all.zip diff --git a/android-kiwix-lib-publisher/gradlew b/android-kiwix-lib-publisher/gradlew new file mode 100755 index 0000000..cccdd3d --- /dev/null +++ b/android-kiwix-lib-publisher/gradlew @@ -0,0 +1,172 @@ +#!/usr/bin/env sh + +############################################################################## +## +## Gradle start up script for UN*X +## +############################################################################## + +# Attempt to set APP_HOME +# Resolve links: $0 may be a link +PRG="$0" +# Need this for relative symlinks. +while [ -h "$PRG" ] ; do + ls=`ls -ld "$PRG"` + link=`expr "$ls" : '.*-> \(.*\)$'` + if expr "$link" : '/.*' > /dev/null; then + PRG="$link" + else + PRG=`dirname "$PRG"`"/$link" + fi +done +SAVED="`pwd`" +cd "`dirname \"$PRG\"`/" >/dev/null +APP_HOME="`pwd -P`" +cd "$SAVED" >/dev/null + +APP_NAME="Gradle" +APP_BASE_NAME=`basename "$0"` + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS="" + +# Use the maximum available, or set MAX_FD != -1 to use that value. +MAX_FD="maximum" + +warn () { + echo "$*" +} + +die () { + echo + echo "$*" + echo + exit 1 +} + +# OS specific support (must be 'true' or 'false'). +cygwin=false +msys=false +darwin=false +nonstop=false +case "`uname`" in + CYGWIN* ) + cygwin=true + ;; + Darwin* ) + darwin=true + ;; + MINGW* ) + msys=true + ;; + NONSTOP* ) + nonstop=true + ;; +esac + +CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + +# Determine the Java command to use to start the JVM. +if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD="$JAVA_HOME/jre/sh/java" + else + JAVACMD="$JAVA_HOME/bin/java" + fi + if [ ! -x "$JAVACMD" ] ; then + die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +else + JAVACMD="java" + which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." +fi + +# Increase the maximum file descriptors if we can. +if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then + MAX_FD_LIMIT=`ulimit -H -n` + if [ $? -eq 0 ] ; then + if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then + MAX_FD="$MAX_FD_LIMIT" + fi + ulimit -n $MAX_FD + if [ $? -ne 0 ] ; then + warn "Could not set maximum file descriptor limit: $MAX_FD" + fi + else + warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" + fi +fi + +# For Darwin, add options to specify how the application appears in the dock +if $darwin; then + GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" +fi + +# For Cygwin, switch paths to Windows format before running java +if $cygwin ; then + APP_HOME=`cygpath --path --mixed "$APP_HOME"` + CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` + JAVACMD=`cygpath --unix "$JAVACMD"` + + # We build the pattern for arguments to be converted via cygpath + ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` + SEP="" + for dir in $ROOTDIRSRAW ; do + ROOTDIRS="$ROOTDIRS$SEP$dir" + SEP="|" + done + OURCYGPATTERN="(^($ROOTDIRS))" + # Add a user-defined pattern to the cygpath arguments + if [ "$GRADLE_CYGPATTERN" != "" ] ; then + OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" + fi + # Now convert the arguments - kludge to limit ourselves to /bin/sh + i=0 + for arg in "$@" ; do + CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` + CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option + + if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition + eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` + else + eval `echo args$i`="\"$arg\"" + fi + i=$((i+1)) + done + case $i in + (0) set -- ;; + (1) set -- "$args0" ;; + (2) set -- "$args0" "$args1" ;; + (3) set -- "$args0" "$args1" "$args2" ;; + (4) set -- "$args0" "$args1" "$args2" "$args3" ;; + (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; + (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; + (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; + (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; + (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; + esac +fi + +# Escape application args +save () { + for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done + echo " " +} +APP_ARGS=$(save "$@") + +# Collect all arguments for the java command, following the shell quoting and substitution rules +eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" + +# by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong +if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then + cd "$(dirname "$0")" +fi + +exec "$JAVACMD" "$@" diff --git a/android-kiwix-lib-publisher/gradlew.bat b/android-kiwix-lib-publisher/gradlew.bat new file mode 100755 index 0000000..f955316 --- /dev/null +++ b/android-kiwix-lib-publisher/gradlew.bat @@ -0,0 +1,84 @@ +@if "%DEBUG%" == "" @echo off +@rem ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +set DIRNAME=%~dp0 +if "%DIRNAME%" == "" set DIRNAME=. +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS= + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if "%ERRORLEVEL%" == "0" goto init + +echo. +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto init + +echo. +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:init +@rem Get command-line arguments, handling Windows variants + +if not "%OS%" == "Windows_NT" goto win9xME_args + +:win9xME_args +@rem Slurp the command line arguments. +set CMD_LINE_ARGS= +set _SKIP=2 + +:win9xME_args_slurp +if "x%~1" == "x" goto execute + +set CMD_LINE_ARGS=%* + +:execute +@rem Setup the command line + +set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + +@rem Execute Gradle +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% + +:end +@rem End local scope for the variables with windows NT shell +if "%ERRORLEVEL%"=="0" goto mainEnd + +:fail +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of +rem the _cmd.exe /c_ return code! +if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 +exit /b 1 + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega diff --git a/android-kiwix-lib-publisher/kiwixLibAndroid/.gitignore b/android-kiwix-lib-publisher/kiwixLibAndroid/.gitignore new file mode 100644 index 0000000..796b96d --- /dev/null +++ b/android-kiwix-lib-publisher/kiwixLibAndroid/.gitignore @@ -0,0 +1 @@ +/build diff --git a/android-kiwix-lib-publisher/kiwixLibAndroid/build.gradle b/android-kiwix-lib-publisher/kiwixLibAndroid/build.gradle new file mode 100644 index 0000000..25cc67f --- /dev/null +++ b/android-kiwix-lib-publisher/kiwixLibAndroid/build.gradle @@ -0,0 +1,64 @@ +apply plugin: 'com.android.library' +apply plugin: 'maven' + +android { + compileSdkVersion 28 + defaultConfig { + minSdkVersion 15 + targetSdkVersion 28 + versionCode 1 + versionName "1.0" + } + buildTypes { + release { + minifyEnabled false + proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' + } + } +} + +dependencies { + implementation 'com.getkeepsafe.relinker:relinker:1.3.1' +} + +task writePom { + pom { + project { + groupId 'org.kiwix.kiwixlib' + artifactId 'kiwixlib' + version '10.1.1' + (System.env.KIWIXLIB_BUILDVERSION == null ? '' : '-'+System.env.KIWIXLIB_BUILDVERSION) + packaging 'aar' + name 'kiwixlib' + url 'https://github.com/kiwix/libkiwix' + licenses { + license { + name 'GPLv3' + url 'https://www.gnu.org/licenses/gpl-3.0.en.html' + } + } + developers { + developer { + id 'kiwix' + name 'kiwix' + email 'contact@kiwix.org' + } + } + scm { + connection 'https://github.com/kiwix/libkiwix.git' + developerConnection 'https://github.com/kiwix/libkiwix.git' + url 'https://github.com/kiwix/libkiwix' + } + } + }.withXml { + def dependenciesNode = asNode().appendNode('dependencies') + + //Iterate over the implementation dependencies, adding a node for each + configurations.implementation.allDependencies.each { + def dependencyNode = dependenciesNode.appendNode('dependency') + dependencyNode.appendNode('groupId', it.group) + dependencyNode.appendNode('artifactId', it.name) + dependencyNode.appendNode('version', it.version) + } + }.writeTo("$buildDir/pom.xml") +} + diff --git a/android-kiwix-lib-publisher/kiwixLibAndroid/proguard-rules.pro b/android-kiwix-lib-publisher/kiwixLibAndroid/proguard-rules.pro new file mode 100644 index 0000000..f1b4245 --- /dev/null +++ b/android-kiwix-lib-publisher/kiwixLibAndroid/proguard-rules.pro @@ -0,0 +1,21 @@ +# Add project specific ProGuard rules here. +# You can control the set of applied configuration files using the +# proguardFiles setting in build.gradle. +# +# For more details, see +# http://developer.android.com/guide/developing/tools/proguard.html + +# If your project uses WebView with JS, uncomment the following +# and specify the fully qualified class name to the JavaScript interface +# class: +#-keepclassmembers class fqcn.of.javascript.interface.for.webview { +# public *; +#} + +# Uncomment this to preserve the line number information for +# debugging stack traces. +#-keepattributes SourceFile,LineNumberTable + +# If you keep the line number information, uncomment this to +# hide the original source file name. +#-renamesourcefileattribute SourceFile diff --git a/android-kiwix-lib-publisher/kiwixLibAndroid/src/main/AndroidManifest.xml b/android-kiwix-lib-publisher/kiwixLibAndroid/src/main/AndroidManifest.xml new file mode 100644 index 0000000..e9b809a --- /dev/null +++ b/android-kiwix-lib-publisher/kiwixLibAndroid/src/main/AndroidManifest.xml @@ -0,0 +1,10 @@ + + + + + + + diff --git a/android-kiwix-lib-publisher/settings.gradle b/android-kiwix-lib-publisher/settings.gradle new file mode 100644 index 0000000..232b235 --- /dev/null +++ b/android-kiwix-lib-publisher/settings.gradle @@ -0,0 +1 @@ +include ':kiwixLibAndroid' diff --git a/src/wrapper/java/AndroidManifest.xml b/src/wrapper/java/AndroidManifest.xml new file mode 100644 index 0000000..1181dc1 --- /dev/null +++ b/src/wrapper/java/AndroidManifest.xml @@ -0,0 +1,13 @@ + + + + + + + diff --git a/src/wrapper/java/book.cpp b/src/wrapper/java/book.cpp new file mode 100644 index 0000000..157cc52 --- /dev/null +++ b/src/wrapper/java/book.cpp @@ -0,0 +1,88 @@ +/* + * Copyright (C) 2020 Matthieu Gautier + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301, USA. + */ + + +#include +#include "org_kiwix_kiwixlib_Book.h" + +#include "utils.h" +#include "book.h" + +JNIEXPORT void JNICALL +Java_org_kiwix_kiwixlib_Book_allocate( + JNIEnv* env, jobject thisObj) +{ + allocate(env, thisObj); +} + +JNIEXPORT void JNICALL +Java_org_kiwix_kiwixlib_Book_dispose(JNIEnv* env, jobject thisObj) +{ + dispose(env, thisObj); +} + +#define BOOK (getPtr(env, thisObj)) + +METHOD(void, Book, update__Lorg_kiwix_kiwixlib_Book_2, jobject otherBook) +{ + BOOK->update(*getPtr(env, otherBook)); +} + +METHOD(void, Book, update__Lorg_kiwix_kiwixlib_JNIKiwixReader_2, jobject reader) +{ + BOOK->update(**Handle::getHandle(env, reader)); +} + +#define GETTER(retType, name) JNIEXPORT retType JNICALL \ +Java_org_kiwix_kiwixlib_Book_##name (JNIEnv* env, jobject thisObj) \ +{ \ + auto cRet = BOOK->name(); \ + retType ret = c2jni(cRet, env); \ + return ret; \ +} + +GETTER(jstring, getId) +GETTER(jstring, getPath) +GETTER(jboolean, isPathValid) +GETTER(jstring, getTitle) +GETTER(jstring, getDescription) +GETTER(jstring, getLanguage) +GETTER(jstring, getCreator) +GETTER(jstring, getPublisher) +GETTER(jstring, getDate) +GETTER(jstring, getUrl) +GETTER(jstring, getName) +GETTER(jstring, getFlavour) +GETTER(jstring, getCategory) +GETTER(jstring, getTags) +GETTER(jlong, getArticleCount) +GETTER(jlong, getMediaCount) +GETTER(jlong, getSize) +GETTER(jstring, getFavicon) +GETTER(jstring, getFaviconUrl) +GETTER(jstring, getFaviconMimeType) + +METHOD(jstring, Book, getTagStr, jstring tagName) try { + auto cRet = BOOK->getTagStr(jni2c(tagName, env)); + return c2jni(cRet, env); +} catch(...) { + return c2jni("", env); +} + +#undef GETTER diff --git a/src/wrapper/java/filter.cpp b/src/wrapper/java/filter.cpp new file mode 100644 index 0000000..09c0734 --- /dev/null +++ b/src/wrapper/java/filter.cpp @@ -0,0 +1,63 @@ +/* + * Copyright (C) 2019-2020 Matthieu Gautier + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301, USA. + */ + + +#include +#include "org_kiwix_kiwixlib_Filter.h" + +#include "library.h" +#include "utils.h" + +/* Kiwix Reader JNI functions */ +METHOD0(void, Filter, allocate) { + allocate(env, thisObj); +} + +METHOD0(void, Filter, dispose) { + dispose(env, thisObj); +} + +#define FILTER (getPtr(env, thisObj)) + +#define FORWARD(name, args_type) \ +METHOD(jobject, Filter, name, args_type value) { \ + FILTER->name(jni2c(value, env)); \ + return thisObj; \ +} + +#define FORWARDA(name, args_type) \ +METHOD(jobject, Filter, name, jobjectArray value) { \ + FILTER->name(jni2c(value, env)); \ + return thisObj; \ +} + + + +FORWARD(local, jboolean) +FORWARD(remote, jboolean) +FORWARD(valid, jboolean) +FORWARDA(acceptTags, jstring) +FORWARDA(rejectTags, jstring) +FORWARD(lang, jstring) +FORWARD(publisher, jstring) +FORWARD(creator, jstring) +FORWARD(maxSize, jlong) +FORWARD(query, jstring) + + diff --git a/src/wrapper/java/kiwixicu.cpp b/src/wrapper/java/kiwixicu.cpp new file mode 100644 index 0000000..a10b578 --- /dev/null +++ b/src/wrapper/java/kiwixicu.cpp @@ -0,0 +1,44 @@ +/* + * Copyright (C) 2013 Emmanuel Engelhart + * Copyright (C) 2017 Matthieu Gautier + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301, USA. + */ + +#include +#include "org_kiwix_kiwixlib_JNIICU.h" + +#include +#include + +#include "unicode/putil.h" + +#include "utils.h" + +std::mutex globalLock; + +JNIEXPORT void JNICALL Java_org_kiwix_kiwixlib_JNIICU_setDataDirectory( + JNIEnv* env, jclass kclass, jstring dirStr) +{ + std::string cPath = jni2c(dirStr, env); + + Lock l; + try { + u_setDataDirectory(cPath.c_str()); + } catch (...) { + std::cerr << "Unable to set data directory " << cPath << std::endl; + } +} diff --git a/src/wrapper/java/kiwixreader.cpp b/src/wrapper/java/kiwixreader.cpp new file mode 100644 index 0000000..cc3c277 --- /dev/null +++ b/src/wrapper/java/kiwixreader.cpp @@ -0,0 +1,561 @@ +/* + * Copyright (C) 2013 Emmanuel Engelhart + * Copyright (C) 2017 Matthieu Gautier + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301, USA. + */ + + +#include +#include +#include "org_kiwix_kiwixlib_JNIKiwixReader.h" + +#include "tools/base64.h" +#include "reader.h" +#include "utils.h" + +/* Kiwix Reader JNI functions */ +JNIEXPORT jlong JNICALL Java_org_kiwix_kiwixlib_JNIKiwixReader_getNativeReader( + JNIEnv* env, jobject obj, jstring filename) +{ + std::string cPath = jni2c(filename, env); + + LOG("Attempting to create reader with: %s", cPath.c_str()); + Lock l; + try { + kiwix::Reader* reader = new kiwix::Reader(cPath); + return reinterpret_cast(new Handle(reader)); + } catch (std::exception& e) { + LOG("Error opening ZIM file"); + LOG(e.what()); + return 0; + } +} + +namespace +{ + +int jni2fd(const jobject& fdObj, JNIEnv* env) +{ + jclass class_fdesc = env->FindClass("java/io/FileDescriptor"); + jfieldID field_fd = env->GetFieldID(class_fdesc, "fd", "I"); + if ( field_fd == NULL ) + { + env->ExceptionClear(); + // Under Android the (private) 'fd' field of java.io.FileDescriptor has been + // renamed to 'descriptor'. See, for example, + // https://android.googlesource.com/platform/libcore/+/refs/tags/android-8.1.0_r1/ojluni/src/main/java/java/io/FileDescriptor.java#55 + field_fd = env->GetFieldID(class_fdesc, "descriptor", "I"); + } + return env->GetIntField(fdObj, field_fd); +} + +} // unnamed namespace + +JNIEXPORT jlong JNICALL Java_org_kiwix_kiwixlib_JNIKiwixReader_getNativeReaderByFD( + JNIEnv* env, jobject obj, jobject fdObj) +{ +#ifndef _WIN32 + int fd = jni2fd(fdObj, env); + + LOG("Attempting to create reader with fd: %d", fd); + Lock l; + try { + kiwix::Reader* reader = new kiwix::Reader(fd); + return reinterpret_cast(new Handle(reader)); + } catch (std::exception& e) { + LOG("Error opening ZIM file"); + LOG(e.what()); + return 0; + } +#else + jclass exception = env->FindClass("java/lang/UnsupportedOperationException"); + env->ThrowNew(exception, "org.kiwix.kiwixlib.JNIKiwixReader.getNativeReaderByFD() is not supported under Windows"); + return 0; +#endif +} + +JNIEXPORT jlong JNICALL Java_org_kiwix_kiwixlib_JNIKiwixReader_getNativeReaderEmbedded( + JNIEnv* env, jobject obj, jobject fdObj, jlong offset, jlong size) +{ +#ifndef _WIN32 + int fd = jni2fd(fdObj, env); + + LOG("Attempting to create reader with fd: %d", fd); + Lock l; + try { + kiwix::Reader* reader = new kiwix::Reader(fd, offset, size); + return reinterpret_cast(new Handle(reader)); + } catch (std::exception& e) { + LOG("Error opening ZIM file"); + LOG(e.what()); + return 0; + } +#else + jclass exception = env->FindClass("java/lang/UnsupportedOperationException"); + env->ThrowNew(exception, "org.kiwix.kiwixlib.JNIKiwixReader.getNativeReaderEmbedded() is not supported under Windows"); + return 0; +#endif +} + +JNIEXPORT void JNICALL +Java_org_kiwix_kiwixlib_JNIKiwixReader_dispose(JNIEnv* env, jobject obj) +{ + Handle::dispose(env, obj); +} + +#define READER (Handle::getHandle(env, obj)) + +/* Kiwix library functions */ +JNIEXPORT jstring JNICALL +Java_org_kiwix_kiwixlib_JNIKiwixReader_getMainPage(JNIEnv* env, jobject obj) +{ + jstring url; + + try { + std::string cUrl = READER->getMainPage().getPath(); + url = c2jni(cUrl, env); + } catch (std::exception& e) { + LOG("Unable to get ZIM main page"); + LOG(e.what()); + url = NULL; + } + return url; +} + +JNIEXPORT jstring JNICALL +Java_org_kiwix_kiwixlib_JNIKiwixReader_getId(JNIEnv* env, jobject obj) +{ + jstring id; + + try { + std::string cId = READER->getId(); + id = c2jni(cId, env); + } catch (std::exception& e) { + LOG("Unable to get ZIM id"); + LOG(e.what()); + id = NULL; + } + + return id; +} + +JNIEXPORT jint JNICALL +Java_org_kiwix_kiwixlib_JNIKiwixReader_getFileSize(JNIEnv* env, jobject obj) +{ + jint size = 0; + + try { + int cSize = READER->getFileSize(); + size = c2jni(cSize, env); + } catch (std::exception& e) { + LOG("Unable to get ZIM file size"); + LOG(e.what()); + } + + return size; +} + +JNIEXPORT jstring JNICALL +Java_org_kiwix_kiwixlib_JNIKiwixReader_getCreator(JNIEnv* env, jobject obj) +{ + jstring creator; + + try { + std::string cCreator = READER->getCreator(); + creator = c2jni(cCreator, env); + } catch (std::exception& e) { + LOG("Unable to get ZIM creator"); + LOG(e.what()); + creator = NULL; + } + + return creator; +} + +JNIEXPORT jstring JNICALL +Java_org_kiwix_kiwixlib_JNIKiwixReader_getPublisher(JNIEnv* env, jobject obj) +{ + jstring publisher; + + try { + std::string cPublisher = READER->getPublisher(); + publisher = c2jni(cPublisher, env); + } catch (std::exception& e) { + LOG("Unable to get ZIM publish"); + LOG(e.what()); + publisher = NULL; + } + return publisher; +} + +JNIEXPORT jstring JNICALL +Java_org_kiwix_kiwixlib_JNIKiwixReader_getName(JNIEnv* env, jobject obj) +{ + jstring name; + + try { + std::string cName = READER->getName(); + name = c2jni(cName, env); + } catch (std::exception& e) { + LOG("Unable to get ZIM name"); + LOG(e.what()); + name = NULL; + } + return name; +} + +JNIEXPORT jstring JNICALL +Java_org_kiwix_kiwixlib_JNIKiwixReader_getFavicon(JNIEnv* env, jobject obj) +{ + jstring favicon; + + try { + std::string cContent; + std::string cMime; + READER->getFavicon(cContent, cMime); + favicon = c2jni( + base64_encode(cContent), + env); + } catch (std::exception& e) { + LOG("Unable to get ZIM favicon"); + LOG(e.what()); + favicon = NULL; + } + return favicon; +} + +JNIEXPORT jstring JNICALL +Java_org_kiwix_kiwixlib_JNIKiwixReader_getDate(JNIEnv* env, jobject obj) +{ + jstring date; + + try { + std::string cDate = READER->getDate(); + date = c2jni(cDate, env); + } catch (std::exception& e) { + LOG("Unable to get ZIM date"); + LOG(e.what()); + date = NULL; + } + return date; +} + +JNIEXPORT jstring JNICALL +Java_org_kiwix_kiwixlib_JNIKiwixReader_getLanguage(JNIEnv* env, jobject obj) +{ + jstring language; + + try { + std::string cLanguage = READER->getLanguage(); + language = c2jni(cLanguage, env); + } catch (std::exception& e) { + LOG("Unable to get ZIM language"); + LOG(e.what()); + language = NULL; + } + + return language; +} + +JNIEXPORT jstring JNICALL Java_org_kiwix_kiwixlib_JNIKiwixReader_getMimeType( + JNIEnv* env, jobject obj, jstring url) +{ + jstring mimeType; + + std::string cUrl = jni2c(url, env); + try { + auto entry = READER->getEntryFromEncodedPath(cUrl); + auto cMimeType = entry.getMimetype(); + mimeType = c2jni(cMimeType, env); + } catch (std::exception& e) { + LOG("Unable to get mime-type for url: %s", cUrl.c_str()); + LOG(e.what()); + mimeType = NULL; + } + return mimeType; +} + +JNIEXPORT jstring JNICALL Java_org_kiwix_kiwixlib_JNIKiwixReader_checkUrl( + JNIEnv* env, jobject obj, jstring url) +{ + jstring finalUrl; + std::string cUrl = jni2c(url, env); + try { + auto entry = READER->getEntryFromEncodedPath(cUrl); + entry = entry.getFinalEntry(); + finalUrl = c2jni(entry.getPath(), env); + } catch (std::exception& e) { + finalUrl = c2jni(std::string(), env); + } + return finalUrl; +} + +JNIEXPORT jbyteArray JNICALL Java_org_kiwix_kiwixlib_JNIKiwixReader_getContent( + JNIEnv* env, jobject obj, jobject url, jobject titleObj, jobject mimeTypeObj, jobject sizeObj) +{ + /* Default values */ + setStringObjValue("", titleObj, env); + setStringObjValue("", mimeTypeObj, env); + setIntObjValue(0, sizeObj, env); + jbyteArray data = env->NewByteArray(0); + + /* Retrieve the content */ + std::string cUrl = getStringObjValue(url, env); + unsigned int cSize = 0; + + try { + auto entry = READER->getEntryFromEncodedPath(cUrl); + bool isRedirect = entry.isRedirect(); + entry = entry.getFinalEntry(); + cSize = entry.getSize(); + setIntObjValue(cSize, sizeObj, env); + setStringObjValue(entry.getMimetype(), mimeTypeObj, env); + setStringObjValue(entry.getTitle(), titleObj, env); + if (isRedirect) { + setStringObjValue(entry.getPath(), url, env); + } else { + data = env->NewByteArray(cSize); + env->SetByteArrayRegion( + data, 0, cSize, reinterpret_cast(entry.getBlob().data())); + } + } catch (std::exception& e) { + LOG("Unable to get content for url: %s", cUrl.c_str()); + LOG(e.what()); + } + + return data; +} + +JNIEXPORT jbyteArray JNICALL Java_org_kiwix_kiwixlib_JNIKiwixReader_getContentPart( + JNIEnv* env, jobject obj, jstring url, jint offset, jint len, jobject sizeObj) +{ + jbyteArray data = env->NewByteArray(0); + setIntObjValue(0, sizeObj, env); + + /* Default values */ + /* Retrieve the content */ + std::string cUrl = jni2c(url, env); + unsigned int cOffset = jni2c(offset, env); + unsigned int cLen = jni2c(len, env); + try { + auto entry = READER->getEntryFromEncodedPath(cUrl); + entry = entry.getFinalEntry(); + + if (cLen == 0) { + setIntObjValue(entry.getSize(), sizeObj, env); + } else if (cOffset+cLen < entry.getSize()) { + auto blob = entry.getBlob(cOffset, cLen); + data = env->NewByteArray(cLen); + env->SetByteArrayRegion( + data, 0, cLen, reinterpret_cast(blob.data())); + setIntObjValue(cLen, sizeObj, env); + } + } catch (std::exception& e) { + LOG("Unable to get partial content for url: %s (%u : %u)", cUrl.c_str(), cOffset, cLen); + LOG(e.what()); + } + return data; +} + +JNIEXPORT jlong JNICALL +Java_org_kiwix_kiwixlib_JNIKiwixReader_getArticleSize( + JNIEnv* env, jobject obj, jstring url) +{ + std::string cUrl = jni2c(url, env); + try { + auto entry = READER->getEntryFromEncodedPath(cUrl); + entry = entry.getFinalEntry(); + return c2jni(entry.getSize(), env); + } catch(std::exception& e) { + LOG("Unable to get size for url : %s", cUrl.c_str()); + LOG(e.what()); + } + return c2jni(0, env); +} + +JNIEXPORT jobject JNICALL +Java_org_kiwix_kiwixlib_JNIKiwixReader_getDirectAccessInformation( + JNIEnv* env, jobject obj, jstring url) +{ + jclass daiClass = env->FindClass("org/kiwix/kiwixlib/DirectAccessInfo"); + jmethodID daiInitMethod = env->GetMethodID(daiClass, "", "()V"); + jobject dai = env->NewObject(daiClass, daiInitMethod); + setDaiObjValue("", 0, dai, env); + + std::string cUrl = jni2c(url, env); + try { + auto entry = READER->getEntryFromEncodedPath(cUrl); + entry = entry.getFinalEntry(); + auto part_info = entry.getDirectAccessInfo(); + setDaiObjValue(part_info.first, part_info.second, dai, env); + } catch (std::exception& e) { + LOG("Unable to get direct access info for url: %s", cUrl.c_str()); + LOG(e.what()); + } + return dai; +} + +JNIEXPORT jboolean JNICALL +Java_org_kiwix_kiwixlib_JNIKiwixReader_searchSuggestions(JNIEnv* env, + jobject obj, + jstring prefix, + jint count) +{ + jboolean retVal = JNI_FALSE; + std::string cPrefix = jni2c(prefix, env); + unsigned int cCount = jni2c(count, env); + + try { +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wdeprecated-declarations" + if (READER->searchSuggestionsSmart(cPrefix, cCount)) { + retVal = JNI_TRUE; + } +#pragma GCC diagnostic pop + } catch (std::exception& e) { + LOG("Unable to get search results for pattern: %s", cPrefix.c_str()); + LOG(e.what()); + } + + return retVal; +} + +JNIEXPORT jboolean JNICALL +Java_org_kiwix_kiwixlib_JNIKiwixReader_getNextSuggestion(JNIEnv* env, + jobject obj, + jobject titleObj, + jobject urlObj) +{ + jboolean retVal = JNI_FALSE; + std::string cTitle; + std::string cUrl; + + try { +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wdeprecated-declarations" + if (READER->getNextSuggestion(cTitle, cUrl)) { + setStringObjValue(cTitle, titleObj, env); + setStringObjValue(cUrl, urlObj, env); + retVal = JNI_TRUE; + } +#pragma GCC diagnostic pop + } catch (std::exception& e) { + LOG("Unable to get next suggestion"); + LOG(e.what()); + } + + return retVal; +} + +JNIEXPORT jboolean JNICALL +Java_org_kiwix_kiwixlib_JNIKiwixReader_getPageUrlFromTitle(JNIEnv* env, + jobject obj, + jstring title, + jobject urlObj) +{ + std::string cTitle = jni2c(title, env); + + try { + auto entry = READER->getEntryFromTitle(cTitle); + entry = entry.getFinalEntry(); + setStringObjValue(entry.getPath(), urlObj, env); + return JNI_TRUE; + } catch (std::exception& e) { + LOG("Unable to get url for title %s: ", cTitle.c_str()); + LOG(e.what()); + } + + return JNI_FALSE; +} + +JNIEXPORT jstring JNICALL Java_org_kiwix_kiwixlib_JNIKiwixReader_getTitle( + JNIEnv* env, jobject obj) +{ + jstring title; + + try { + std::string cTitle = READER->getTitle(); + title = c2jni(cTitle, env); + } catch (std::exception& e) { + LOG("Unable to get zim title"); + LOG(e.what()); + title = NULL; + } + return title; +} + +JNIEXPORT jstring JNICALL +Java_org_kiwix_kiwixlib_JNIKiwixReader_getDescription(JNIEnv* env, jobject obj) +{ + jstring description; + + try { + std::string cDescription = READER->getDescription(); + description = c2jni(cDescription, env); + } catch (std::exception& e) { + LOG("Unable to get zim description"); + LOG(e.what()); + description = NULL; + } + return description; +} + +JNIEXPORT jint JNICALL +Java_org_kiwix_kiwixlib_JNIKiwixReader_getArticleCount(JNIEnv* env, jobject obj) +{ + jint articleCount = 0; + try { + auto cArticleCount = READER->getArticleCount(); + articleCount = c2jni(cArticleCount, env); + } catch (std::exception& e) { + LOG("Unable to get article count."); + LOG(e.what()); + } + return articleCount; +} + +JNIEXPORT jint JNICALL +Java_org_kiwix_kiwixlib_JNIKiwixReader_getMediaCount(JNIEnv* env, jobject obj) +{ + jint mediaCount = 0; + try { + auto cMediaCount = READER->getMediaCount(); + mediaCount = c2jni(cMediaCount, env); + } catch (std::exception& e) { + LOG("Unable to get media count."); + LOG(e.what()); + } + return mediaCount; +} + + +JNIEXPORT jboolean JNICALL Java_org_kiwix_kiwixlib_JNIKiwixReader_getRandomPage( + JNIEnv* env, jobject obj, jobject urlObj) +{ + jboolean retVal = JNI_FALSE; + std::string cUrl; + + try { + std::string cUrl = READER->getRandomPage().getPath(); + setStringObjValue(cUrl, urlObj, env); + retVal = JNI_TRUE; + } catch (std::exception& e) { + LOG("Unable to get random page"); + LOG(e.what()); + } + return retVal; +} diff --git a/src/wrapper/java/kiwixsearcher.cpp b/src/wrapper/java/kiwixsearcher.cpp new file mode 100644 index 0000000..68241b3 --- /dev/null +++ b/src/wrapper/java/kiwixsearcher.cpp @@ -0,0 +1,123 @@ +/* + * Copyright (C) 2013 Emmanuel Engelhart + * Copyright (C) 2017 Matthieu Gautier + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301, USA. + */ + + +#include "org_kiwix_kiwixlib_JNIKiwixSearcher.h" +#include "org_kiwix_kiwixlib_JNIKiwixSearcher_Result.h" + +#include "reader.h" +#include "searcher.h" +#include "utils.h" + +#define SEARCHER (Handle::getHandle(env, obj)) +#define RESULT (Handle::getHandle(env, obj)) + + +JNIEXPORT void JNICALL +Java_org_kiwix_kiwixlib_JNIKiwixSearcher_dispose(JNIEnv* env, jobject obj) +{ + Handle::dispose(env, obj); +} + +/* Kiwix Reader JNI functions */ +JNIEXPORT jlong JNICALL +Java_org_kiwix_kiwixlib_JNIKiwixSearcher_getNativeHandle(JNIEnv* env, + jobject obj) +{ + kiwix::Searcher* searcher = new kiwix::Searcher(); + return reinterpret_cast(new Handle(searcher)); +} + +/* Kiwix library functions */ +JNIEXPORT void JNICALL Java_org_kiwix_kiwixlib_JNIKiwixSearcher_addReader( + JNIEnv* env, jobject obj, jobject reader) +{ + auto searcher = SEARCHER; + + searcher->add_reader(*(Handle::getHandle(env, reader))); +} + +JNIEXPORT void JNICALL Java_org_kiwix_kiwixlib_JNIKiwixSearcher_search( + JNIEnv* env, jobject obj, jstring query, jint count) +{ + std::string cquery = jni2c(query, env); + unsigned int ccount = jni2c(count, env); + + SEARCHER->search(cquery, 0, ccount); +} + +JNIEXPORT jobject JNICALL +Java_org_kiwix_kiwixlib_JNIKiwixSearcher_getNextResult(JNIEnv* env, + jobject obj) +{ + jobject result = nullptr; + + kiwix::Result* cresult = SEARCHER->getNextResult(); + if (cresult != nullptr) { + jclass resultclass + = env->FindClass("org/kiwix/kiwixlib/JNIKiwixSearcher$Result"); + jmethodID ctor = env->GetMethodID( + resultclass, "", "(Lorg/kiwix/kiwixlib/JNIKiwixSearcher;JLorg/kiwix/kiwixlib/JNIKiwixSearcher;)V"); + result = env->NewObject(resultclass, ctor, obj, reinterpret_cast(new Handle(cresult)), obj); + } + return result; +} + +JNIEXPORT void JNICALL Java_org_kiwix_kiwixlib_JNIKiwixSearcher_00024Result_dispose( + JNIEnv* env, jobject obj) +{ + Handle::dispose(env, obj); +} + +JNIEXPORT jstring JNICALL +Java_org_kiwix_kiwixlib_JNIKiwixSearcher_00024Result_getUrl(JNIEnv* env, + jobject obj) +{ + try { + return c2jni(RESULT->get_url(), env); + } catch (...) { + return nullptr; + } +} + +JNIEXPORT jstring JNICALL +Java_org_kiwix_kiwixlib_JNIKiwixSearcher_00024Result_getTitle(JNIEnv* env, + jobject obj) +{ + try { + return c2jni(RESULT->get_title(), env); + } catch (...) { + return nullptr; + } +} + +JNIEXPORT jstring JNICALL +Java_org_kiwix_kiwixlib_JNIKiwixSearcher_00024Result_getSnippet(JNIEnv* env, + jobject obj) +{ + return c2jni(RESULT->get_snippet(), env); +} + +JNIEXPORT jstring JNICALL +Java_org_kiwix_kiwixlib_JNIKiwixSearcher_00024Result_getContent(JNIEnv* env, + jobject obj) +{ + return c2jni(RESULT->get_content(), env); +} diff --git a/src/wrapper/java/kiwixserver.cpp b/src/wrapper/java/kiwixserver.cpp new file mode 100644 index 0000000..830fdc6 --- /dev/null +++ b/src/wrapper/java/kiwixserver.cpp @@ -0,0 +1,103 @@ +/* + * Copyright (C) 2013 Emmanuel Engelhart + * Copyright (C) 2017 Matthieu Gautier + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301, USA. + */ + + +#include +#include "org_kiwix_kiwixlib_JNIKiwixServer.h" + +#include "tools/base64.h" +#include "server.h" +#include "utils.h" + +/* Kiwix Reader JNI functions */ +JNIEXPORT jlong JNICALL Java_org_kiwix_kiwixlib_JNIKiwixServer_getNativeServer( + JNIEnv* env, jobject obj, jobject jLibrary) +{ + LOG("Attempting to create server"); + Lock l; + try { + auto library = getPtr(env, jLibrary); + kiwix::Server* server = new kiwix::Server(library); + return reinterpret_cast(new Handle(server)); + } catch (std::exception& e) { + LOG("Error creating the server"); + LOG(e.what()); + return 0; + } +} + +JNIEXPORT void JNICALL +Java_org_kiwix_kiwixlib_JNIKiwixServer_dispose(JNIEnv* env, jobject obj) +{ + Handle::dispose(env, obj); +} + +#define SERVER (Handle::getHandle(env, obj)) + +/* Kiwix library functions */ +JNIEXPORT void JNICALL +Java_org_kiwix_kiwixlib_JNIKiwixServer_setRoot(JNIEnv* env, jobject obj, jstring jRoot) +{ + std::string root = jni2c(jRoot, env); + SERVER->setRoot(root); +} + +JNIEXPORT void JNICALL +Java_org_kiwix_kiwixlib_JNIKiwixServer_setAddress(JNIEnv* env, jobject obj, jstring jAddress) +{ + std::string address = jni2c(jAddress, env); + SERVER->setAddress(address); +} + +JNIEXPORT void JNICALL +Java_org_kiwix_kiwixlib_JNIKiwixServer_setPort(JNIEnv* env, jobject obj, int port) +{ + SERVER->setPort(port); +} + +JNIEXPORT void JNICALL +Java_org_kiwix_kiwixlib_JNIKiwixServer_setNbThreads(JNIEnv* env, jobject obj, int threads) +{ + SERVER->setNbThreads(threads); +} + +JNIEXPORT void JNICALL +Java_org_kiwix_kiwixlib_JNIKiwixServer_setTaskbar(JNIEnv* env, jobject obj, jboolean withTaskbar, jboolean withLibraryButton) +{ + SERVER->setTaskbar(withTaskbar, withLibraryButton); +} + +JNIEXPORT void JNICALL +Java_org_kiwix_kiwixlib_JNIKiwixServer_setBlockExternalLinks(JNIEnv* env, jobject obj, jboolean blockExternalLinks) +{ + SERVER->setBlockExternalLinks(blockExternalLinks); +} + +JNIEXPORT jboolean JNICALL +Java_org_kiwix_kiwixlib_JNIKiwixServer_start(JNIEnv* env, jobject obj) +{ + return SERVER->start(); +} + +JNIEXPORT void JNICALL +Java_org_kiwix_kiwixlib_JNIKiwixServer_stop(JNIEnv* env, jobject obj) +{ + SERVER->stop(); +} diff --git a/src/wrapper/java/library.cpp b/src/wrapper/java/library.cpp new file mode 100644 index 0000000..f2a0846 --- /dev/null +++ b/src/wrapper/java/library.cpp @@ -0,0 +1,96 @@ +/* + * Copyright (C) 2019-2020 Matthieu Gautier + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301, USA. + */ + + +#include +#include "org_kiwix_kiwixlib_Library.h" + +#include "library.h" +#include "reader.h" +#include "utils.h" + +/* Kiwix Reader JNI functions */ +JNIEXPORT void JNICALL +Java_org_kiwix_kiwixlib_Library_allocate( + JNIEnv* env, jobject thisObj) +{ + allocate(env, thisObj); +} + +JNIEXPORT void JNICALL +Java_org_kiwix_kiwixlib_Library_dispose(JNIEnv* env, jobject thisObj) +{ + dispose(env, thisObj); +} + +#define LIBRARY (getPtr(env, thisObj)) + +/* Kiwix library functions */ +JNIEXPORT jboolean JNICALL +Java_org_kiwix_kiwixlib_Library_addBook( + JNIEnv* env, jobject thisObj, jstring path) +{ + auto cPath = jni2c(path, env); + + try { + kiwix::Reader reader(cPath); + kiwix::Book book; + book.update(reader); + return LIBRARY->addBook(book); + } catch (std::exception& e) { + LOG("Unable to add the book"); + LOG(e.what()); } + return false; +} + +METHOD(jobject, Library, getBookById, jstring id) { + auto cId = jni2c(id, env); + auto cBook = new kiwix::Book(LIBRARY->getBookById(cId)); + jclass cls = env->FindClass("org/kiwix/kiwixlib/Book"); + jmethodID constructorId = env->GetMethodID(cls, "", "()V"); + jobject book = env->NewObject(cls, constructorId); + setPtr(env, book, cBook); + return book; +} + +METHOD(jint, Library, getBookCount, jboolean localBooks, jboolean remoteBooks) { + return LIBRARY->getBookCount(localBooks, remoteBooks); +} + +METHOD0(jobjectArray, Library, getBooksIds) { + return c2jni(LIBRARY->getBooksIds(), env); +} + +METHOD(jobjectArray, Library, filter, jobject filterObj) { + auto filter = getPtr(env, filterObj); + return c2jni(LIBRARY->filter(*filter), env); +} + +METHOD0(jobjectArray, Library, getBooksLanguages) { + return c2jni(LIBRARY->getBooksLanguages(), env); +} + +METHOD0(jobjectArray, Library, getBooksCreators) { + return c2jni(LIBRARY->getBooksCreators(), env); +} + +METHOD0(jobjectArray, Library, getBooksPublisher) { + return c2jni(LIBRARY->getBooksPublishers(), env); +} + diff --git a/src/wrapper/java/manager.cpp b/src/wrapper/java/manager.cpp new file mode 100644 index 0000000..4395609 --- /dev/null +++ b/src/wrapper/java/manager.cpp @@ -0,0 +1,131 @@ +/* + * Copyright (C) 2020 Matthieu Gautier + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301, USA. + */ + + +#include +#include "org_kiwix_kiwixlib_Manager.h" + +#include "manager.h" +#include "utils.h" + + +JNIEXPORT void JNICALL +Java_org_kiwix_kiwixlib_Manager_allocate( + JNIEnv* env, jobject thisObj, jobject libraryObj) +{ + auto lib = getPtr(env, libraryObj); + allocate(env, thisObj, lib); +} + +JNIEXPORT void JNICALL +Java_org_kiwix_kiwixlib_Manager_dispose(JNIEnv* env, jobject thisObj) +{ + dispose(env, thisObj); +} + +#define MANAGER (getPtr(env, thisObj)) + +/* Kiwix manager functions */ +JNIEXPORT jboolean JNICALL +Java_org_kiwix_kiwixlib_Manager_readFile( + JNIEnv* env, jobject thisObj, jstring path) +{ + auto cPath = jni2c(path, env); + + try { + return MANAGER->readFile(cPath); + } catch (std::exception& e) { + LOG("Unable to get readFile"); + LOG(e.what()); + } + return false; +} + +JNIEXPORT jboolean JNICALL +Java_org_kiwix_kiwixlib_Manager_readXml( + JNIEnv* env, jobject thisObj, jstring content, jstring libraryPath) +{ + auto cContent = jni2c(content, env); + auto cPath = jni2c(libraryPath, env); + + try { + return MANAGER->readXml(cContent, false, cPath); + } catch (std::exception& e) { + LOG("Unable to get ZIM id"); + LOG(e.what()); + } + + return false; +} + +JNIEXPORT jboolean JNICALL +Java_org_kiwix_kiwixlib_Manager_readOpds( + JNIEnv* env, jobject thisObj, jstring content, jstring urlHost) +{ + auto cContent = jni2c(content, env); + auto cUrl = jni2c(urlHost, env); + + try { + return MANAGER->readOpds(cContent, cUrl); + } catch (std::exception& e) { + LOG("Unable to get ZIM id"); + LOG(e.what()); + } + + return false; +} + +JNIEXPORT jboolean JNICALL +Java_org_kiwix_kiwixlib_Manager_readBookmarkFile( + JNIEnv* env, jobject thisObj, jstring path) +{ + auto cPath = jni2c(path, env); + + try { + return MANAGER->readBookmarkFile(cPath); + } catch (std::exception& e) { + LOG("Unable to get ZIM id"); + LOG(e.what()); + } + + return false; +} + +JNIEXPORT jstring JNICALL +Java_org_kiwix_kiwixlib_Manager_addBookFromPath( + JNIEnv* env, jobject thisObj, + jstring pathToOpen, jstring pathToSave, jstring url, jboolean checkMetaData) +{ + auto cPathToOpen = jni2c(pathToOpen, env); + auto cPathToSave = jni2c(pathToSave, env); + auto cUrl = jni2c(url, env); + jstring id = NULL; + + try { + auto cId = MANAGER->addBookFromPathAndGetId(cPathToOpen, cPathToSave, cUrl, checkMetaData); + if ( !cId.empty() ) { + id = c2jni(cId, env); + } + } catch (std::exception& e) { + LOG("Unable to get ZIM file size"); + LOG(e.what()); + } + + return id; +} diff --git a/src/wrapper/java/meson.build b/src/wrapper/java/meson.build new file mode 100644 index 0000000..de72e49 --- /dev/null +++ b/src/wrapper/java/meson.build @@ -0,0 +1,55 @@ + +java_sources = files([ + 'org/kiwix/kiwixlib/JNIICU.java', + 'org/kiwix/kiwixlib/Book.java', + 'org/kiwix/kiwixlib/JNIKiwixReader.java', + 'org/kiwix/kiwixlib/Library.java', + 'org/kiwix/kiwixlib/Manager.java', + 'org/kiwix/kiwixlib/Filter.java', + 'org/kiwix/kiwixlib/JNIKiwixSearcher.java', + 'org/kiwix/kiwixlib/JNIKiwixServer.java', + 'org/kiwix/kiwixlib/JNIKiwixInt.java', + 'org/kiwix/kiwixlib/JNIKiwixString.java', + 'org/kiwix/kiwixlib/JNIKiwixBool.java', + 'org/kiwix/kiwixlib/JNIKiwixException.java', + 'org/kiwix/kiwixlib/DirectAccessInfo.java' +]) + +kiwix_jni = custom_target('jni', + input: java_sources, + output: ['org_kiwix_kiwixlib_JNIKiwix.h', + 'org_kiwix_kiwixlib_Book.h', + 'org_kiwix_kiwixlib_JNIKiwixReader.h', + 'org_kiwix_kiwixlib_Library.h', + 'org_kiwix_kiwixlib_Manager.h', + 'org_kiwix_kiwixlib_Filter.h', + 'org_kiwix_kiwixlib_JNIKiwixServer.h', + 'org_kiwix_kiwixlib_JNIKiwixSearcher.h', + 'org_kiwix_kiwixlib_JNIKiwixSearcher_Result.h'], + command:['javac', '-d', '@OUTDIR@', '-h', '@OUTDIR@', '@INPUT@'] +) + +jni_sources = files([ + 'kiwixicu.cpp', + 'book.cpp', + 'kiwixreader.cpp', + 'library.cpp', + 'manager.cpp', + 'filter.cpp', + 'kiwixsearcher.cpp', + 'kiwixserver.cpp', +]) + +kiwix_sources += jni_sources + [kiwix_jni] + +if 'java' in wrapper + kiwix_jar = jar('kiwixlib', java_sources) + #junit_jar = files('org/kiwix/testing/junit-4.13.jar') + #test_jar = jar('testing', 'org/kiwix/testing/test.java', + # link_with: [kiwix_jar, junit_jar]) + #test('javatest', test_jar) +endif + +install_subdir('org', install_dir: 'kiwix-lib/java', exclude_directories: ['kiwix/testing']) +install_subdir('res', install_dir: 'kiwix-lib') +install_data('AndroidManifest.xml', install_dir: 'kiwix-lib') diff --git a/src/wrapper/java/org/kiwix/kiwixlib/Book.java b/src/wrapper/java/org/kiwix/kiwixlib/Book.java new file mode 100644 index 0000000..693e833 --- /dev/null +++ b/src/wrapper/java/org/kiwix/kiwixlib/Book.java @@ -0,0 +1,48 @@ + +package org.kiwix.kiwixlib; + +public class Book +{ + public Book() { allocate(); } + + + public native void update(Book book); + public native void update(JNIKiwixReader reader); + + @Override + protected void finalize() { dispose(); } + + public native String getId(); + public native String getPath(); + public native boolean isPathValid(); + public native String getTitle(); + public native String getDescription(); + public native String getLanguage(); + public native String getCreator(); + public native String getPublisher(); + public native String getDate(); + public native String getUrl(); + public native String getName(); + public native String getFlavour(); + public native String getCategory(); + public native String getTags(); + /** + * Return the value associated to the tag tagName + * + * @param tagName the tag name to search for. + * @return The value of the tag. If the tag is not found, return empty string. + */ + public native String getTagStr(String tagName); + + public native long getArticleCount(); + public native long getMediaCount(); + public native long getSize(); + + public native String getFavicon(); + public native String getFaviconUrl(); + public native String getFaviconMimeType(); + + private native void allocate(); + private native void dispose(); + private long nativeHandle; +} diff --git a/src/wrapper/java/org/kiwix/kiwixlib/DirectAccessInfo.java b/src/wrapper/java/org/kiwix/kiwixlib/DirectAccessInfo.java new file mode 100644 index 0000000..4ba137d --- /dev/null +++ b/src/wrapper/java/org/kiwix/kiwixlib/DirectAccessInfo.java @@ -0,0 +1,26 @@ +/* + * Copyright (C) 2017 Matthieu Gautier + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301, USA. + */ + +package org.kiwix.kiwixlib; + +public class DirectAccessInfo +{ + public String filename; + public long offset; +} diff --git a/src/wrapper/java/org/kiwix/kiwixlib/Filter.java b/src/wrapper/java/org/kiwix/kiwixlib/Filter.java new file mode 100644 index 0000000..bf9e719 --- /dev/null +++ b/src/wrapper/java/org/kiwix/kiwixlib/Filter.java @@ -0,0 +1,44 @@ +/* + * Copyright (C) 2019-2020 Matthieu Gautier + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301, USA. + */ + +package org.kiwix.kiwixlib; + +public class Filter +{ + + public native Filter local(boolean accept); + public native Filter remote(boolean accept); + public native Filter valid(boolean accept); + public native Filter acceptTags(String[] tags); + public native Filter rejectTags(String[] tags); + public native Filter lang(String lang); + public native Filter publisher(String publisher); + public native Filter creator(String creator); + public native Filter maxSize(long size); + public native Filter query(String query); + + + public Filter() { allocate(); } + + @Override + protected void finalize() { dispose(); } + private native void allocate(); + private native void dispose(); + private long nativeHandle; +} diff --git a/src/wrapper/java/org/kiwix/kiwixlib/JNIICU.java b/src/wrapper/java/org/kiwix/kiwixlib/JNIICU.java new file mode 100644 index 0000000..db8ba6e --- /dev/null +++ b/src/wrapper/java/org/kiwix/kiwixlib/JNIICU.java @@ -0,0 +1,26 @@ +/* + * Copyright (C) 2013 Emmanuel Engelhart + * Copyright (C) 2017 Matthieu Gautier + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301, USA. + */ + +package org.kiwix.kiwixlib; + +public class JNIICU +{ + static public native void setDataDirectory(String icuDataDir); +} diff --git a/src/wrapper/java/org/kiwix/kiwixlib/JNIKiwix.java b/src/wrapper/java/org/kiwix/kiwixlib/JNIKiwix.java new file mode 100644 index 0000000..57e148b --- /dev/null +++ b/src/wrapper/java/org/kiwix/kiwixlib/JNIKiwix.java @@ -0,0 +1,36 @@ +/* + * Copyright (C) 2013 Emmanuel Engelhart + * Copyright (C) 2017 Matthieu Gautier + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301, USA. + */ + +package org.kiwix.kiwixlib; + +import android.content.Context; +import com.getkeepsafe.relinker.ReLinker; +import org.kiwix.kiwixlib.JNIICU; + +public class JNIKiwix +{ + public JNIKiwix(final Context context){ + ReLinker.loadLibrary(context, "kiwix"); + } + + public void setDataDirectory(String icuDataDir) { + JNIICU.setDataDirectory(icuDataDir); + } +} diff --git a/src/wrapper/java/org/kiwix/kiwixlib/JNIKiwixBool.java b/src/wrapper/java/org/kiwix/kiwixlib/JNIKiwixBool.java new file mode 100644 index 0000000..74563d3 --- /dev/null +++ b/src/wrapper/java/org/kiwix/kiwixlib/JNIKiwixBool.java @@ -0,0 +1,25 @@ +/* + * Copyright (C) 2013 Emmanuel Engelhart + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301, USA. + */ + +package org.kiwix.kiwixlib; + +public class JNIKiwixBool +{ + public boolean value; +} diff --git a/src/wrapper/java/org/kiwix/kiwixlib/JNIKiwixException.java b/src/wrapper/java/org/kiwix/kiwixlib/JNIKiwixException.java new file mode 100644 index 0000000..dd0e214 --- /dev/null +++ b/src/wrapper/java/org/kiwix/kiwixlib/JNIKiwixException.java @@ -0,0 +1,27 @@ +/* + * Copyright (C) 2017 Matthieu Gautier + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301, USA. + */ + +package org.kiwix.kiwixlib; + +public class JNIKiwixException extends Exception +{ + public JNIKiwixException(String message) { + super(message); + } +} diff --git a/src/wrapper/java/org/kiwix/kiwixlib/JNIKiwixInt.java b/src/wrapper/java/org/kiwix/kiwixlib/JNIKiwixInt.java new file mode 100644 index 0000000..a66c937 --- /dev/null +++ b/src/wrapper/java/org/kiwix/kiwixlib/JNIKiwixInt.java @@ -0,0 +1,25 @@ +/* + * Copyright (C) 2013 Emmanuel Engelhart + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301, USA. + */ + +package org.kiwix.kiwixlib; + +public class JNIKiwixInt +{ + public int value; +} diff --git a/src/wrapper/java/org/kiwix/kiwixlib/JNIKiwixReader.java b/src/wrapper/java/org/kiwix/kiwixlib/JNIKiwixReader.java new file mode 100644 index 0000000..e11d721 --- /dev/null +++ b/src/wrapper/java/org/kiwix/kiwixlib/JNIKiwixReader.java @@ -0,0 +1,182 @@ +/* + * Copyright (C) 2013 Emmanuel Engelhart + * Copyright (C) 2017 Matthieu Gautier + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301, USA. + */ + +package org.kiwix.kiwixlib; + +import org.kiwix.kiwixlib.JNIKiwixException; +import org.kiwix.kiwixlib.JNIKiwixString; +import org.kiwix.kiwixlib.JNIKiwixInt; +import org.kiwix.kiwixlib.JNIKiwixSearcher; +import org.kiwix.kiwixlib.DirectAccessInfo; +import java.io.FileDescriptor; + +public class JNIKiwixReader +{ + public native String getMainPage(); + + public native String getTitle(); + + public native String getId(); + + public native String getLanguage(); + + public native String getMimeType(String url); + + /** + * Check if a url exists and is a redirect or not. + * + * Return an empty string if the url doesn't exist in the reader. + * Return the url of the "final" entry. + * - equal to the input url if the entry is not a redirection. + * - different if the url is a redirection (and the webview should redirect to it). + */ + public native String checkUrl(String url); + + /** + * Get the content of a article. + * + * Return a byte array of the content of the article. + * Set the title, mimeType to the title and mimeType of the article. + * Set the size to the size of the returned array. + * + * If the entry doesn't exist : + * - return a empty byte array + * - set all arguments (except url) to empty/0. + * If the entry exist but is a redirection : + * - return an empty byte array + * - set all arguments (including url) to information of the targeted article. + */ + public native byte[] getContent(JNIKiwixString url, + JNIKiwixString title, + JNIKiwixString mimeType, + JNIKiwixInt size); + + /** + * getContentPart. + * + * Get only a part of the content of the article. + * Return a byte array of `len` size starting from offset `offset`. + * Set `size` to the number of bytes read + * (`len` if everything is ok, 0 in case of error). + * If `len` == 0, no bytes are read but `size` is set to the total size of the + * article. + */ + public native byte[] getContentPart(String url, + int offest, + int len, + JNIKiwixInt size); + + /** + * + * Get the size of an article. + * + * @param url The url of the article. + * @return The size of the final (redirections are resolved) article (in byte). + * Return 0 if the article is not found. + */ + public native long getArticleSize(String url); + + /** + * getDirectAccessInformation. + * + * Return information giving where the content is located in the zim file. + * + * Some contents (binary content) are stored uncompressed in the zim file. + * Knowing this information, it could be interesting to directly open + * the zim file (or zim part) and directly read the content from it (and so + * bypassing the libzim). + * + * Return a `DirectAccessInfo` (filename, offset) where the content is located. + * + * If the content cannot be directly accessed (content is compressed or zim + * file is cut in the middle of the content), the filename is an empty string + * and offset is zero. + */ + public native DirectAccessInfo getDirectAccessInformation(String url); + + public native boolean searchSuggestions(String prefix, int count); + + public native boolean getNextSuggestion(JNIKiwixString title, JNIKiwixString url); + + public native boolean getPageUrlFromTitle(String title, JNIKiwixString url); + + public native String getDescription(); + + public native String getDate(); + + public native String getFavicon(); + + public native String getCreator(); + + public native String getPublisher(); + + public native String getName(); + + public native int getFileSize(); + + public native int getArticleCount(); + + public native int getMediaCount(); + + public native boolean getRandomPage(JNIKiwixString url); + + public JNIKiwixSearcher search(String query, int count) + { + JNIKiwixSearcher searcher = new JNIKiwixSearcher(); + searcher.addKiwixReader(this); + searcher.search(query, count); + return searcher; + } + + public JNIKiwixReader(String filename) throws JNIKiwixException + { + nativeHandle = getNativeReader(filename); + if (nativeHandle == 0) { + throw new JNIKiwixException("Cannot open zimfile "+filename); + } + } + + public JNIKiwixReader(FileDescriptor fd) throws JNIKiwixException + { + nativeHandle = getNativeReaderByFD(fd); + if (nativeHandle == 0) { + throw new JNIKiwixException("Cannot open zimfile by fd "+fd.toString()); + } + } + + public JNIKiwixReader(FileDescriptor fd, long offset, long size) + throws JNIKiwixException + { + nativeHandle = getNativeReaderEmbedded(fd, offset, size); + if (nativeHandle == 0) { + throw new JNIKiwixException(String.format("Cannot open embedded zimfile (fd=%s, offset=%d, size=%d)", fd, offset, size)); + } + } + + public JNIKiwixReader() { + + } + public native void dispose(); + + private native long getNativeReader(String filename); + private native long getNativeReaderByFD(FileDescriptor fd); + private native long getNativeReaderEmbedded(FileDescriptor fd, long offset, long size); + private long nativeHandle; +} diff --git a/src/wrapper/java/org/kiwix/kiwixlib/JNIKiwixSearcher.java b/src/wrapper/java/org/kiwix/kiwixlib/JNIKiwixSearcher.java new file mode 100644 index 0000000..30d7afb --- /dev/null +++ b/src/wrapper/java/org/kiwix/kiwixlib/JNIKiwixSearcher.java @@ -0,0 +1,67 @@ +/* + * Copyright (C) 2013 Emmanuel Engelhart + * Copyright (C) 2017 Matthieu Gautier + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301, USA. + */ + + +package org.kiwix.kiwixlib; + +import org.kiwix.kiwixlib.JNIKiwixReader; +import java.util.Vector; + +public class JNIKiwixSearcher +{ + public class Result + { + private long nativeHandle; + private JNIKiwixSearcher searcher; + public Result(long handle, JNIKiwixSearcher _searcher) + { + nativeHandle = handle; + searcher = _searcher; + } + public native String getUrl(); + public native String getTitle(); + public native String getContent(); + public native String getSnippet(); + public native void dispose(); + } + + public JNIKiwixSearcher() + { + nativeHandle = getNativeHandle(); + usedReaders = new Vector(); + } + public native void dispose(); + + private native long getNativeHandle(); + private long nativeHandle; + private Vector usedReaders; + + public native void addReader(JNIKiwixReader reader); + public void addKiwixReader(JNIKiwixReader reader) + { + addReader(reader); + usedReaders.addElement(reader); + }; + + public native void search(String query, int count); + + public native Result getNextResult(); + public native boolean hasMoreResult(); +} diff --git a/src/wrapper/java/org/kiwix/kiwixlib/JNIKiwixServer.java b/src/wrapper/java/org/kiwix/kiwixlib/JNIKiwixServer.java new file mode 100644 index 0000000..11c5f0d --- /dev/null +++ b/src/wrapper/java/org/kiwix/kiwixlib/JNIKiwixServer.java @@ -0,0 +1,50 @@ +/* + * Copyright (C) 2019 Matthieu Gautier + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301, USA. + */ + +package org.kiwix.kiwixlib; + +import org.kiwix.kiwixlib.JNIKiwixException; +import org.kiwix.kiwixlib.Library; + +public class JNIKiwixServer +{ + public native void setRoot(String root); + + public native void setAddress(String address); + + public native void setPort(int port); + + public native void setNbThreads(int nbTreads); + + public native void setTaskbar(boolean withTaskBar, boolean witLibraryButton); + + public native void setBlockExternalLinks(boolean blockExternalLinks); + + public native boolean start(); + + public native void stop(); + + public JNIKiwixServer(Library library) + { + nativeHandle = getNativeServer(library); + } + + private native long getNativeServer(Library library); + private long nativeHandle; +} diff --git a/src/wrapper/java/org/kiwix/kiwixlib/JNIKiwixString.java b/src/wrapper/java/org/kiwix/kiwixlib/JNIKiwixString.java new file mode 100644 index 0000000..601fb9d --- /dev/null +++ b/src/wrapper/java/org/kiwix/kiwixlib/JNIKiwixString.java @@ -0,0 +1,37 @@ +/* + * Copyright (C) 2013 Emmanuel Engelhart + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301, USA. + */ + +package org.kiwix.kiwixlib; + +public class JNIKiwixString +{ + public String value; + + public JNIKiwixString(String value) { + this.value = value; + } + + public JNIKiwixString() { + this(""); + } + + public String getValue() { + return value; + } +} diff --git a/src/wrapper/java/org/kiwix/kiwixlib/Library.java b/src/wrapper/java/org/kiwix/kiwixlib/Library.java new file mode 100644 index 0000000..68aa7d2 --- /dev/null +++ b/src/wrapper/java/org/kiwix/kiwixlib/Library.java @@ -0,0 +1,49 @@ +/* + * Copyright (C) 2019-2020 Matthieu Gautier + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301, USA. + */ + +package org.kiwix.kiwixlib; + +import org.kiwix.kiwixlib.Book; +import org.kiwix.kiwixlib.JNIKiwixException; + +public class Library +{ + public native boolean addBook(String path) throws JNIKiwixException; + + public native Book getBookById(String id); + public native int getBookCount(boolean localBooks, boolean remoteBooks); + + public native String[] getBooksIds(); + public native String[] filter(Filter filter); + + public native String[] getBooksLanguages(); + public native String[] getBooksCreators(); + public native String[] getBooksPublishers(); + + public Library() + { + allocate(); + } + + @Override + protected void finalize() { dispose(); } + private native void allocate(); + private native void dispose(); + private long nativeHandle; +} diff --git a/src/wrapper/java/org/kiwix/kiwixlib/Manager.java b/src/wrapper/java/org/kiwix/kiwixlib/Manager.java new file mode 100644 index 0000000..2772ca9 --- /dev/null +++ b/src/wrapper/java/org/kiwix/kiwixlib/Manager.java @@ -0,0 +1,90 @@ +/* + * Copyright (C) 2020 Matthieu Gautier + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301, USA. + */ + +package org.kiwix.kiwixlib; + +import org.kiwix.kiwixlib.Library; + +public class Manager +{ + /** + * Read a `library.xml` file and add books in the library. + * + * @param path The (utf8) path to the `library.xml` file. + * @return True if the file has been properly parsed. + */ + public native boolean readFile(String path); + + /** + * Load a library content stored in a string (at `library.xml` format). + * + * @param content The content corresponding of the library xml. + * @param libraryPath The library path (used to resolve relative paths) + * @return True if the content has been properly parsed. + */ + public native boolean readXml(String content, String libraryPath); + + /** + * Load a library content stored in a string (at OPDS stream format) + * + * @param content the content of the OPDS stream. + * @param urlHost the url of the stream (used to resolve relative url) + * @return True if the content has been properly parsed. + */ + public native boolean readOpds(String content, String urlHost); + + /** + * Load a bookmark file + * + * @param path The path of the file to read. + * @return True if the content has been properly parsed + */ + public native boolean readBookmarkFile(String path); + + /** + * Add a book to the library. + * + * @param pathToOpen The path of the zim file to add. + * @param pathToSave The path to store in the library in place of + * pathToOpen. + * @param url The url of the book to store in the library + * (useful for kiiwix-serve catalog) + * @param checkMetaData Tell if we check metadata before adding a book to the + * library. + * @return The id of te book if the book has been added to the library. + * Empty string if not. + */ + public native String addBookFromPath(String pathToOpen, + String pathToSave, + String url, + boolean checkMetaData); + + public Manager(Library library) { + allocate(library); + _library = library; + } + + private Library _library; + + @Override + protected void finalize() { dispose(); } + private native void allocate(Library library); + private native void dispose(); + private long nativeHandle; +} diff --git a/src/wrapper/java/org/kiwix/testing/catalog.xml b/src/wrapper/java/org/kiwix/testing/catalog.xml new file mode 100644 index 0000000..3e2f6ac --- /dev/null +++ b/src/wrapper/java/org/kiwix/testing/catalog.xml @@ -0,0 +1,19 @@ + + 00000000-0000-0000-0000-000000000000 + + Test ZIM file + urn:uuid:86c91e51-55bf-8882-464e-072aca37a3e8 + /meta?name=favicon&content=small + 2020-11-27:00::00:Z + en + This is a ZIM file used in libzim unit-tests + unit;test + + + Kiwix + + + + + + diff --git a/src/wrapper/java/org/kiwix/testing/compile_and_run_test.sh b/src/wrapper/java/org/kiwix/testing/compile_and_run_test.sh new file mode 100755 index 0000000..41899af --- /dev/null +++ b/src/wrapper/java/org/kiwix/testing/compile_and_run_test.sh @@ -0,0 +1,37 @@ +#!/usr/bin/bash + +# This script compiles and runs the unit test to test the java wrapper. +# This is not integrated in meson because ... this is not so easy. + +die() +{ + echo >&2 "!!! ERROR: $*" + exit 1 +} + + +KIWIX_LIB_JAR=$1 +if [ -z $KIWIX_LIB_JAR ] +then + die "You must give the path to the kiwixlib.jar as first argument" +fi + +KIWIX_LIB_DIR=$2 +if [ -z $KIWIX_LIB_DIR ] +then + die "You must give the path to directory containing libkiwix.so as second argument" +fi + +KIWIX_LIB_JAR=$(readlink -f "$KIWIX_LIB_JAR") +KIWIX_LIB_DIR=$(readlink -f "$KIWIX_LIB_DIR") +TEST_SOURCE_DIR=$(dirname "$(readlink -f $0)") + +cd "$TEST_SOURCE_DIR" + +javac -g -d . -s . -cp "junit-4.13.jar:$KIWIX_LIB_JAR" test.java \ + || die "Compilation failed" + +java -Djava.library.path="$KIWIX_LIB_DIR" \ + -cp "junit-4.13.jar:hamcrest-core-1.3.jar:$KIWIX_LIB_JAR:." \ + org.junit.runner.JUnitCore test \ + || die "Unit test failed" diff --git a/src/wrapper/java/org/kiwix/testing/create_test_zimfiles b/src/wrapper/java/org/kiwix/testing/create_test_zimfiles new file mode 100755 index 0000000..4610b7d --- /dev/null +++ b/src/wrapper/java/org/kiwix/testing/create_test_zimfiles @@ -0,0 +1,28 @@ +#!/usr/bin/env bash + +die() +{ + echo >&2 "!!! ERROR: $*" + exit 1 +} + +cd "$(dirname "$0")" +rm -f small.zim +zimwriterfs --withoutFTIndex \ + -w main.html \ + -f favicon.png \ + -l en \ + -t "Test ZIM file" \ + -d "N/A" \ + -c "N/A" \ + -p "N/A" \ + small_zimfile_data \ + small.zim \ +&& echo 'small.zim was successfully created' \ +|| die 'Failed to create small.zim' + +printf "BEGINZIM" > small.zim.embedded \ +&& cat small.zim >> small.zim.embedded \ +&& printf "ENDZIM" >> small.zim.embedded \ +&& echo 'small.zim.embedded was successfully created' \ +|| die 'Failed to create small.zim.embedded' diff --git a/src/wrapper/java/org/kiwix/testing/hamcrest-core-1.3.jar b/src/wrapper/java/org/kiwix/testing/hamcrest-core-1.3.jar new file mode 100644 index 0000000..9d5fe16 Binary files /dev/null and b/src/wrapper/java/org/kiwix/testing/hamcrest-core-1.3.jar differ diff --git a/src/wrapper/java/org/kiwix/testing/junit-4.13.jar b/src/wrapper/java/org/kiwix/testing/junit-4.13.jar new file mode 100644 index 0000000..acc3c43 Binary files /dev/null and b/src/wrapper/java/org/kiwix/testing/junit-4.13.jar differ diff --git a/src/wrapper/java/org/kiwix/testing/small.zim b/src/wrapper/java/org/kiwix/testing/small.zim new file mode 100644 index 0000000..bce4fc5 Binary files /dev/null and b/src/wrapper/java/org/kiwix/testing/small.zim differ diff --git a/src/wrapper/java/org/kiwix/testing/small.zim.embedded b/src/wrapper/java/org/kiwix/testing/small.zim.embedded new file mode 100644 index 0000000..1bc1da2 Binary files /dev/null and b/src/wrapper/java/org/kiwix/testing/small.zim.embedded differ diff --git a/src/wrapper/java/org/kiwix/testing/small_zimfile_data/favicon.png b/src/wrapper/java/org/kiwix/testing/small_zimfile_data/favicon.png new file mode 100644 index 0000000..8094543 Binary files /dev/null and b/src/wrapper/java/org/kiwix/testing/small_zimfile_data/favicon.png differ diff --git a/src/wrapper/java/org/kiwix/testing/small_zimfile_data/main.html b/src/wrapper/java/org/kiwix/testing/small_zimfile_data/main.html new file mode 100644 index 0000000..c12e573 --- /dev/null +++ b/src/wrapper/java/org/kiwix/testing/small_zimfile_data/main.html @@ -0,0 +1,11 @@ + + + + Test ZIM file + + + + + Test ZIM file + + diff --git a/src/wrapper/java/org/kiwix/testing/test.java b/src/wrapper/java/org/kiwix/testing/test.java new file mode 100644 index 0000000..788729d --- /dev/null +++ b/src/wrapper/java/org/kiwix/testing/test.java @@ -0,0 +1,160 @@ + +import java.io.*; +import java.util.*; +import org.junit.Test; +import static org.junit.Assert.*; +import org.kiwix.kiwixlib.*; + +public class test { +static { + System.loadLibrary("kiwix"); +} + +private static byte[] getFileContent(String path) +throws IOException +{ + File file = new File(path); + DataInputStream in = new DataInputStream( + new BufferedInputStream( + new FileInputStream(file))); + byte[] data = new byte[(int)file.length()]; + in.read(data); + return data; +} + +private static byte[] getFileContentPartial(String path, int offset, int size) +throws IOException +{ + File file = new File(path); + DataInputStream in = new DataInputStream( + new BufferedInputStream( + new FileInputStream(file))); + byte[] data = new byte[size]; + in.skipBytes(offset); + in.read(data, 0, size); + return data; +} + +private static String getTextFileContent(String path) +throws IOException +{ + return new String(getFileContent(path)); +} + +@Test +public void testReader() +throws JNIKiwixException, IOException +{ + JNIKiwixReader reader = new JNIKiwixReader("small.zim"); + assertEquals("Test ZIM file", reader.getTitle()); + assertEquals(45, reader.getFileSize()); // The file size is in KiB + assertEquals("A/main.html", reader.getMainPage()); + String s = getTextFileContent("small_zimfile_data/main.html"); + byte[] c = reader.getContent(new JNIKiwixString("A/main.html"), + new JNIKiwixString(), + new JNIKiwixString(), + new JNIKiwixInt()); + assertEquals(s, new String(c)); + + byte[] faviconData = getFileContent("small_zimfile_data/favicon.png"); + assertEquals(faviconData.length, reader.getArticleSize("I/favicon.png")); + c = reader.getContent(new JNIKiwixString("I/favicon.png"), + new JNIKiwixString(), + new JNIKiwixString(), + new JNIKiwixInt()); + assertTrue(Arrays.equals(faviconData, c)); + + DirectAccessInfo dai = reader.getDirectAccessInformation("I/favicon.png"); + assertNotEquals("", dai.filename); + c = getFileContentPartial(dai.filename, (int)dai.offset, faviconData.length); + assertTrue(Arrays.equals(faviconData, c)); +} + +@Test +public void testReaderByFd() +throws JNIKiwixException, IOException +{ + FileInputStream fis = new FileInputStream("small.zim"); + JNIKiwixReader reader = new JNIKiwixReader(fis.getFD()); + assertEquals("Test ZIM file", reader.getTitle()); + assertEquals(45, reader.getFileSize()); // The file size is in KiB + assertEquals("A/main.html", reader.getMainPage()); + String s = getTextFileContent("small_zimfile_data/main.html"); + byte[] c = reader.getContent(new JNIKiwixString("A/main.html"), + new JNIKiwixString(), + new JNIKiwixString(), + new JNIKiwixInt()); + assertEquals(s, new String(c)); + + byte[] faviconData = getFileContent("small_zimfile_data/favicon.png"); + assertEquals(faviconData.length, reader.getArticleSize("I/favicon.png")); + c = reader.getContent(new JNIKiwixString("I/favicon.png"), + new JNIKiwixString(), + new JNIKiwixString(), + new JNIKiwixInt()); + assertTrue(Arrays.equals(faviconData, c)); + + DirectAccessInfo dai = reader.getDirectAccessInformation("I/favicon.png"); + assertNotEquals("", dai.filename); + c = getFileContentPartial(dai.filename, (int)dai.offset, faviconData.length); + assertTrue(Arrays.equals(faviconData, c)); +} + +@Test +public void testReaderWithAnEmbeddedArchive() +throws JNIKiwixException, IOException +{ + File plainArchive = new File("small.zim"); + FileInputStream fis = new FileInputStream("small.zim.embedded"); + JNIKiwixReader reader = new JNIKiwixReader(fis.getFD(), 8, plainArchive.length()); + assertEquals("Test ZIM file", reader.getTitle()); + assertEquals(45, reader.getFileSize()); // The file size is in KiB + assertEquals("A/main.html", reader.getMainPage()); + String s = getTextFileContent("small_zimfile_data/main.html"); + byte[] c = reader.getContent(new JNIKiwixString("A/main.html"), + new JNIKiwixString(), + new JNIKiwixString(), + new JNIKiwixInt()); + assertEquals(s, new String(c)); + + byte[] faviconData = getFileContent("small_zimfile_data/favicon.png"); + assertEquals(faviconData.length, reader.getArticleSize("I/favicon.png")); + c = reader.getContent(new JNIKiwixString("I/favicon.png"), + new JNIKiwixString(), + new JNIKiwixString(), + new JNIKiwixInt()); + assertTrue(Arrays.equals(faviconData, c)); + + DirectAccessInfo dai = reader.getDirectAccessInformation("I/favicon.png"); + assertNotEquals("", dai.filename); + c = getFileContentPartial(dai.filename, (int)dai.offset, faviconData.length); + assertTrue(Arrays.equals(faviconData, c)); +} + +@Test +public void testLibrary() +throws IOException +{ + Library lib = new Library(); + Manager manager = new Manager(lib); + String content = getTextFileContent("catalog.xml"); + manager.readOpds(content, "http://localhost"); + assertEquals(lib.getBookCount(true, true), 1); + String[] bookIds = lib.getBooksIds(); + assertEquals(bookIds.length, 1); + Book book = lib.getBookById(bookIds[0]); + assertEquals(book.getTitle(), "Test ZIM file"); + assertEquals(book.getTags(), "unit;test"); + assertEquals(book.getFaviconUrl(), "http://localhost/meta?name=favicon&content=small"); + assertEquals(book.getUrl(), "http://localhost/small.zim"); +} + +static +public void main(String[] args) { + Library lib = new Library(); + lib.getBookCount(true, true); +} + + + +} diff --git a/src/wrapper/java/res/values/strings.xml b/src/wrapper/java/res/values/strings.xml new file mode 100644 index 0000000..8386572 --- /dev/null +++ b/src/wrapper/java/res/values/strings.xml @@ -0,0 +1,3 @@ + + Kiwix Lib + diff --git a/src/wrapper/java/utils.h b/src/wrapper/java/utils.h new file mode 100644 index 0000000..232071d --- /dev/null +++ b/src/wrapper/java/utils.h @@ -0,0 +1,259 @@ +/* + * Copyright (C) 2013 Emmanuel Engelhart + * Copyright (C) 2017 Matthieu Gautier + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301, USA. + */ + + +#ifndef _ANDROID_JNI_UTILS_H +#define _ANDROID_JNI_UTILS_H + +#include + +#include +#include +#include +#include + +#if __ANDROID__ + #include + #define LOG(...) __android_log_print(ANDROID_LOG_ERROR, "kiwix", __VA_ARGS__) +#else + #define LOG(...) +#endif + +extern std::mutex globalLock; + +template +void setPtr(JNIEnv* env, jobject thisObj, T* ptr) +{ + jclass thisClass = env->GetObjectClass(thisObj); + jfieldID fieldId = env->GetFieldID(thisClass, "nativeHandle", "J"); + env->SetLongField(thisObj, fieldId, reinterpret_cast(ptr)); +} + +template +void allocate(JNIEnv* env, jobject thisObj, Args && ...args) +{ + T* ptr = new T(std::forward(args)...); + setPtr(env, thisObj, ptr); +} + +template +T* getPtr(JNIEnv* env, jobject thisObj) +{ + jclass thisClass = env->GetObjectClass(thisObj); + jfieldID fidNumber = env->GetFieldID(thisClass, "nativeHandle", "J"); + return reinterpret_cast(env->GetLongField(thisObj, fidNumber)); +} + +template +void dispose(JNIEnv* env, jobject thisObj) +{ + delete getPtr(env, thisObj); +} + +#define METHOD0(retType, class, name) \ +JNIEXPORT retType JNICALL Java_org_kiwix_kiwixlib_##class##_##name( \ + JNIEnv* env, jobject thisObj) + +#define METHOD(retType, class, name, ...) \ +JNIEXPORT retType JNICALL Java_org_kiwix_kiwixlib_##class##_##name( \ + JNIEnv* env, jobject thisObj, __VA_ARGS__) + +inline jfieldID getHandleField(JNIEnv* env, jobject obj) +{ + jclass c = env->GetObjectClass(obj); + // J is the type signature for long: + return env->GetFieldID(c, "nativeHandle", "J"); +} + +inline jobjectArray createArray(JNIEnv* env, size_t length, const std::string& type_sig) +{ + jclass c = env->FindClass(type_sig.c_str()); + return env->NewObjectArray(length, c, NULL); +} + +class Lock : public std::unique_lock +{ + public: + Lock() : std::unique_lock(globalLock) { } +}; + +template +class LockedHandle; + +template +class Handle +{ + protected: + T* h; + + public: + Handle(T* h) : h(h){}; + + // No destructor. This must and will be handled by dispose method. + + static LockedHandle getHandle(JNIEnv* env, jobject obj) + { + jlong handle = env->GetLongField(obj, getHandleField(env, obj)); + return LockedHandle(reinterpret_cast*>(handle)); + } + + static void dispose(JNIEnv* env, jobject obj) + { + auto lHandle = getHandle(env, obj); + auto handle = lHandle.h; + delete handle->h; + delete handle; + } + friend class LockedHandle; +}; + +template +struct LockedHandle : public Lock { + Handle* h; + LockedHandle(Handle* h) : h(h) {} + T* operator->() { return h->h; } + T* operator*() { return h->h; } + operator bool() const { return (h->h != nullptr); } + operator T*() const { return h->h; } +}; + +template +struct JType { }; + +template<> struct JType{ typedef jboolean type_t; }; +template<> struct JType{ typedef jint type_t; }; +template<> struct JType{ typedef jlong type_t; }; +template<> struct JType { typedef jlong type_t; }; +template<> struct JType { typedef jlong type_t; }; +template<> struct JType{ typedef jstring type_t; }; +template<> struct JType>{ typedef jobjectArray type_t; }; + +template +inline typename JType::type_t c2jni(const T& val, JNIEnv* env) { + return static_cast::type_t>(val); +} + +template<> +inline jboolean c2jni(const bool& val, JNIEnv* env) { return val ? JNI_TRUE : JNI_FALSE; } + +template<> +inline jstring c2jni(const std::string& val, JNIEnv* env) +{ + return env->NewStringUTF(val.c_str()); +} + +template<> +inline jobjectArray c2jni(const std::vector& val, JNIEnv* env) +{ + auto array = createArray(env, val.size(), "java/lang/String"); + size_t index = 0; + for (auto& elem: val) { + auto jElem = c2jni(elem, env); + env->SetObjectArrayElement(array, index++, jElem); + } + return array; +} + +template +struct CType { }; + +template<> struct CType{ typedef bool type_t; }; +template<> struct CType{ typedef int type_t; }; +template<> struct CType{ typedef long type_t; }; +template<> struct CType{ typedef std::string type_t; }; + +template +struct CType{ typedef std::vector::type_t> type_t; }; + +/* jni2c type conversion functions */ +template +inline typename CType::type_t jni2c(const T& val, JNIEnv* env) { + return static_cast::type_t>(val); +} + +template<> +inline bool jni2c(const jboolean& val, JNIEnv* env) { return val == JNI_TRUE; } + +template<> +inline std::string jni2c(const jstring& val, JNIEnv* env) +{ + const char* chars = env->GetStringUTFChars(val, 0); + std::string ret(chars); + env->ReleaseStringUTFChars(val, chars); + return ret; +} + +template +inline typename CType::type_t jni2c(const jobjectArray& val, JNIEnv* env) +{ + jsize length = env->GetArrayLength(val); + typename CType::type_t v(length); + + int i; + for (i = 0; i < length; i++) { + U obj = (U) env->GetObjectArrayElement(val, i); + auto cobj = jni2c(obj, env); + v.push_back(cobj); + } + return v; +} + +/* Method to deal with variable passed by reference */ +inline std::string getStringObjValue(const jobject obj, JNIEnv* env) +{ + jclass objClass = env->GetObjectClass(obj); + jfieldID objFid = env->GetFieldID(objClass, "value", "Ljava/lang/String;"); + jstring jstr = (jstring)env->GetObjectField(obj, objFid); + return jni2c(jstr, env); +} +inline void setStringObjValue(const std::string& value, + const jobject obj, + JNIEnv* env) +{ + jclass objClass = env->GetObjectClass(obj); + jfieldID objFid = env->GetFieldID(objClass, "value", "Ljava/lang/String;"); + env->SetObjectField(obj, objFid, c2jni(value, env)); +} + +inline void setIntObjValue(const int value, const jobject obj, JNIEnv* env) +{ + jclass objClass = env->GetObjectClass(obj); + jfieldID objFid = env->GetFieldID(objClass, "value", "I"); + env->SetIntField(obj, objFid, value); +} + +inline void setBoolObjValue(const bool value, const jobject obj, JNIEnv* env) +{ + jclass objClass = env->GetObjectClass(obj); + jfieldID objFid = env->GetFieldID(objClass, "value", "Z"); + env->SetIntField(obj, objFid, c2jni(value, env)); +} + +inline void setDaiObjValue(const std::string& filename, const long offset, + const jobject obj, JNIEnv* env) +{ + jclass objClass = env->GetObjectClass(obj); + jfieldID filenameFid = env->GetFieldID(objClass, "filename", "Ljava/lang/String;"); + env->SetObjectField(obj, filenameFid, c2jni(filename, env)); + jfieldID offsetFid = env->GetFieldID(objClass, "offset", "J"); + env->SetLongField(obj, offsetFid, offset); +} + +#endif // _ANDROID_JNI_UTILS_H