hisat-3n/hisat2lib/java_jni/ht2module.c
2025-01-18 21:09:52 +08:00

433 lines
11 KiB
C

/*
* Copyright 2018, Chanhee Park <parkchanhee@gmail.com> and Daehwan Kim <infphilo@gmail.com>
*
* This file is part of HISAT 2.
*
* HISAT 2 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
* (at your option) any later version.
*
* HISAT 2 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 HISAT 2. If not, see <http://www.gnu.org/licenses/>.
*/
#include <jni.h>
#include <stdio.h>
#include "ht2.h"
#include "HT2Module.h"
#define CLASSPATH_INTEGER "java/lang/Integer"
#define CLASSPATH_HASHMAP "java/util/HashMap"
#define CLASSPATH_ARRAYLIST "java/util/ArrayList"
#define CLASSPATH_HT2POSITION "HT2Module$HT2Position"
#ifdef DEBUG
#define DEBUGLOG(fmt, ...) do { fprintf(stderr, "%s:%d:%s(): " fmt, __FILE__, __LINE__, __func__, ##__VA_ARGS__); } while(0)
#else
#define DEBUGLOG(fmt, ...)
#endif
static jint JNI_VERSION = JNI_VERSION_1_2;
static jclass classInteger;
static jclass classHashMap;
static jclass classArrayList;
static jclass classHT2Position;
static jmethodID mthIntegerInit;
static jmethodID mthIntegerIntValue;
static jmethodID mthHashMapInit;
static jmethodID mthHashMapPut;
static jmethodID mthHashMapGet;
static jmethodID mthArrayListInit;
static jmethodID mthArrayListEnsureCapacity;
static jmethodID mthArrayListAdd;
static jmethodID mthHT2PositionInit;
static jobject NewHashMap(JNIEnv *env)
{
jobject obj = (*env)->NewObject(env, classHashMap, mthHashMapInit);
return obj;
}
static jobject NewInteger(JNIEnv *env, int value)
{
jobject obj = (*env)->NewObject(env, classInteger, mthIntegerInit, value);
return obj;
}
static jobject NewArrayList(JNIEnv *env)
{
jobject obj = (*env)->NewObject(env, classArrayList, mthArrayListInit);
return obj;
}
static jobject NewHT2Position(JNIEnv *env, struct ht2_position *htpos)
{
jobject obj = (*env)->NewObject(env, classHT2Position, mthHT2PositionInit,
htpos->chr_id, htpos->direction, htpos->pos);
return obj;
}
static int GetInteger(JNIEnv *env, jobject jobjInt, int *val)
{
int value = (*env)->CallIntMethod(env, jobjInt, mthIntegerIntValue);
*val = value;
return 0;
}
static void hashmap_put(JNIEnv *env, jobject jobjMap,
const char *key_str, const int val)
{
jstring keyObj;
jobject valueObj;
keyObj = (*env)->NewStringUTF(env, key_str);
valueObj = NewInteger(env, val);
(*env)->CallObjectMethod(env, jobjMap, mthHashMapPut, keyObj, valueObj);
(*env)->DeleteLocalRef(env, keyObj);
(*env)->DeleteLocalRef(env, valueObj);
}
static int hashmap_get(JNIEnv *env, jobject jobjMap,
const char *key_str, int *val)
{
jstring keyObj;
jobject valueObj;
keyObj = (*env)->NewStringUTF(env, key_str);
valueObj = (*env)->CallObjectMethod(env, jobjMap, mthHashMapGet, keyObj);
if(valueObj == NULL) {
DEBUGLOG("Can't find key: %s\n", key_str);
(*env)->DeleteLocalRef(env, keyObj);
return -1;
}
// Integer -> int
int value = 0;
if(GetInteger(env, valueObj, &value) < 0) {
DEBUGLOG("Can't get Integer\n");
(*env)->DeleteLocalRef(env, keyObj);
(*env)->DeleteLocalRef(env, valueObj);
return -1;
}
(*env)->DeleteLocalRef(env, keyObj);
(*env)->DeleteLocalRef(env, valueObj);
*val = value;
return 0;
}
static jobject conv_ht2option(JNIEnv *env, ht2_option_t *opts)
{
jobject hashMap = NewHashMap(env);
#define HT2_OPT_BUILD(_name) \
hashmap_put(env, hashMap, #_name , opts->_name)
HT2_OPT_BUILD(offRate);
HT2_OPT_BUILD(useMm);
HT2_OPT_BUILD(useShmem);
HT2_OPT_BUILD(mmSweep);
HT2_OPT_BUILD(noRefNames);
HT2_OPT_BUILD(noSplicedAlignment);
HT2_OPT_BUILD(gVerbose);
HT2_OPT_BUILD(startVerbose);
HT2_OPT_BUILD(sanityCheck);
HT2_OPT_BUILD(useHaplotype);
return hashMap;
}
static void update_ht2option(JNIEnv *env, ht2_option_t *opts, jobject jmapObject)
{
#define HT2_OPT_UPDATE(_name) \
do { \
int value = 0; \
if(hashmap_get(env, jmapObject, #_name, &value) == 0) { \
DEBUGLOG("Using %s, %d\n", #_name, value); \
opts->_name = value; \
} else { \
DEBUGLOG("Using default %s, %d\n", #_name, opts->_name); \
}\
} while(0)
HT2_OPT_UPDATE(offRate);
HT2_OPT_UPDATE(useMm);
HT2_OPT_UPDATE(useShmem);
HT2_OPT_UPDATE(mmSweep);
HT2_OPT_UPDATE(noRefNames);
HT2_OPT_UPDATE(noSplicedAlignment);
HT2_OPT_UPDATE(gVerbose);
HT2_OPT_UPDATE(startVerbose);
HT2_OPT_UPDATE(sanityCheck);
HT2_OPT_UPDATE(useHaplotype);
}
static void conv_refnames_result(JNIEnv *env,
struct ht2_index_getrefnames_result *result, jobject jobjList)
{
size_t i;
// Resize
DEBUGLOG("count: %d\n", result->count);
(*env)->CallVoidMethod(env, jobjList, mthArrayListEnsureCapacity, result->count);
for(i = 0; i < result->count; i++) {
DEBUGLOG("names[%d] %s\n", i, result->names[i]);
jobject elem = (*env)->NewStringUTF(env, result->names[i]);
(*env)->CallObjectMethod(env, jobjList, mthArrayListAdd, elem);
(*env)->DeleteLocalRef(env, elem);
}
}
static void conv_repeat_expand_result(JNIEnv *env,
struct ht2_repeat_expand_result *result,
jobject jobjList)
{
size_t i;
// Resize
DEBUGLOG("count: %d\n", result->count);
(*env)->CallVoidMethod(env, jobjList, mthArrayListEnsureCapacity, result->count);
// Add Items
for(i = 0; i < result->count; i++) {
struct ht2_position *htpos = &result->positions[i];
DEBUGLOG("position[%d]: %u, %d, %lu\n", i, htpos->chr_id, htpos->direction, htpos->pos);
jobject elem = NewHT2Position(env, htpos);
(*env)->CallObjectMethod(env, jobjList, mthArrayListAdd, elem);
(*env)->DeleteLocalRef(env, elem);
}
}
JNIEXPORT jlong JNICALL Java_HT2Module_init(JNIEnv *env,
jobject thisObj, jstring indexNameObj, jobject optionMap)
{
DEBUGLOG("Init\n");
const char *index_path = (*env)->GetStringUTFChars(env, indexNameObj, NULL);
if(index_path == NULL) {
DEBUGLOG("Can't get string\n");
return 0;
}
DEBUGLOG("Index Path: %s\n", index_path);
ht2_option_t ht2opt;
ht2_init_options(&ht2opt);
if(optionMap != NULL) {
// update options
update_ht2option(env, &ht2opt, optionMap);
}
ht2_handle_t handle = ht2_init(index_path, &ht2opt);
DEBUGLOG("Initailzed %p\n", handle);
(*env)->ReleaseStringUTFChars(env, indexNameObj, index_path);
return (jlong)handle;
}
JNIEXPORT void JNICALL Java_HT2Module_close(JNIEnv *env,
jobject thisObj, jlong handlePtr)
{
DEBUGLOG("Closing\n");
ht2_handle_t handle = (ht2_handle_t)handlePtr;
DEBUGLOG("Received handle: %p\n", handle);
ht2_close(handle);
}
JNIEXPORT jobject JNICALL Java_HT2Module_get_1options(JNIEnv *env,
jobject thisObj)
{
DEBUGLOG("get_option\n");
// Get Default options
ht2_option_t ht2opt;
ht2_init_options(&ht2opt);
// convert ht2opt to java hashmap
jobject hashobj = conv_ht2option(env, &ht2opt);
return hashobj;
}
JNIEXPORT jstring JNICALL Java_HT2Module_index_1getrefnamebyid(JNIEnv *env,
jobject thisObj, jlong handlePtr, jint chr_id)
{
DEBUGLOG("index_getrefnamebyid\n");
ht2_handle_t handle = (ht2_handle_t)handlePtr;
if(handle == NULL) {
DEBUGLOG("Invalid handle\n");
return NULL;
}
const char *refname = ht2_index_getrefnamebyid(handle, chr_id);
if(refname == NULL) {
DEBUGLOG("Can't get refname(%d)\n", chr_id);
return NULL;
}
return (*env)->NewStringUTF(env, refname);
}
JNIEXPORT jobject JNICALL Java_HT2Module_index_1getrefnames(JNIEnv *env,
jobject thisObj, jlong handlePtr)
{
DEBUGLOG("index_getrefnames\n");
ht2_handle_t handle = (ht2_handle_t)handlePtr;
if(handle == NULL) {
DEBUGLOG("Invalid handle\n");
return NULL;
}
struct ht2_index_getrefnames_result *result = NULL;
ht2_error_t ret = ht2_index_getrefnames(handle, &result);
jobject refnames = NewArrayList(env);
if(ret == HT2_OK) {
DEBUGLOG("refnames size: %d\n", result->count);
conv_refnames_result(env, result, refnames);
free(result);
} else {
DEBUGLOG("Can't get refnames\n");
}
return refnames;
}
JNIEXPORT jobject JNICALL Java_HT2Module_repeat_1expand(JNIEnv *env,
jobject thisObj, jlong handlePtr, jstring nameObj, jlong rpos, jlong rlen)
{
DEBUGLOG("repeat_expand\n");
ht2_handle_t handle = (ht2_handle_t)handlePtr;
if(handle == NULL) {
DEBUGLOG("Invalid handle\n");
return NULL;
}
const char *name = (*env)->GetStringUTFChars(env, nameObj, NULL);
if(name == NULL) {
DEBUGLOG("Can't get string\n");
return NULL;
}
DEBUGLOG("Repeat Expand: %s, %lu, %lu\n", name, rpos, rlen);
struct ht2_repeat_expand_result *result = NULL;
ht2_error_t ret = ht2_repeat_expand(handle, name, rpos, rlen, &result);
jobject positions = NewArrayList(env);
if(ret == HT2_OK) {
DEBUGLOG("expand position size: %d\n", result->count);
conv_repeat_expand_result(env, result, positions);
free(result);
} else {
DEBUGLOG("Can't expand repeat\n");
}
(*env)->ReleaseStringUTFChars(env, nameObj, name);
return positions;
}
jint JNI_OnLoad(JavaVM *vm, void *reserved)
{
DEBUGLOG("JNI Loaded\n");
JNIEnv *env;
if((*vm)->GetEnv(vm, (void **)&env, JNI_VERSION) != JNI_OK) {
return JNI_ERR;
}
// Load Class
#define LOAD_CLASS(_globalRef, _clazz) \
do { \
jclass tmpClassRef = (*env)->FindClass(env, _clazz); \
if(tmpClassRef == NULL) { \
DEBUGLOG("Can't find class %s\n", _clazz); \
return JNI_ERR; \
} \
_globalRef = (jclass)(*env)->NewGlobalRef(env, tmpClassRef); \
(*env)->DeleteLocalRef(env, tmpClassRef); \
} while(0)
LOAD_CLASS(classInteger, CLASSPATH_INTEGER);
LOAD_CLASS(classHashMap, CLASSPATH_HASHMAP);
LOAD_CLASS(classArrayList, CLASSPATH_ARRAYLIST);
LOAD_CLASS(classHT2Position, CLASSPATH_HT2POSITION);
// Load Method
// Integer
mthIntegerInit = (*env)->GetMethodID(env, classInteger, "<init>", "(I)V");
mthIntegerIntValue = (*env)->GetMethodID(env, classInteger, "intValue", "()I");
// HashMap
mthHashMapInit = (*env)->GetMethodID(env, classHashMap, "<init>", "()V");
mthHashMapPut = (*env)->GetMethodID(env, classHashMap, "put",
"(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;");
mthHashMapGet = (*env)->GetMethodID(env, classHashMap, "get",
"(Ljava/lang/Object;)Ljava/lang/Object;");
// ArrayList
mthArrayListInit = (*env)->GetMethodID(env, classArrayList, "<init>", "()V");
mthArrayListEnsureCapacity = (*env)->GetMethodID(env, classArrayList, "ensureCapacity", "(I)V");
mthArrayListAdd = (*env)->GetMethodID(env, classArrayList, "add",
"(Ljava/lang/Object;)Z");
// HT2Position
mthHT2PositionInit = (*env)->GetMethodID(env, classHT2Position, "<init>", "(III)V");
return JNI_VERSION;
}
void JNI_OnUnload(JavaVM *vm, void *reserved)
{
DEBUGLOG("JNI Unload\n");
JNIEnv *env;
(*vm)->GetEnv(vm, (void **)&env, JNI_VERSION);
(*env)->DeleteGlobalRef(env, classInteger);
(*env)->DeleteGlobalRef(env, classHashMap);
(*env)->DeleteGlobalRef(env, classArrayList);
(*env)->DeleteGlobalRef(env, classHT2Position);
}