/*
* Copyright (C) 2009 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <assert.h>
#include <cutils/log.h>
#include <jni.h>
#include <string.h>
#include <sys/types.h>
#include <unistd.h>
#include "../include/pinyinime.h"
#include "../include/sync.h"
#include "../include/userdict.h"
#ifdef __cplusplus
extern "C" {
#endif
using namespace ime_pinyin;
#define RET_BUF_LEN 256
static char16 retbuf[RET_BUF_LEN];
static char16 (*predict_buf)[kMaxPredictSize + 1] = NULL;
static size_t predict_len;
static Sync sync_worker;
static struct file_descriptor_offsets_t
{
jclass mClass;
jfieldID mDescriptor;
} gFileDescriptorOffsets;
JNIEXPORT jboolean JNICALL nativeImOpenDecoder(JNIEnv* env, jclass jclazz,
jbyteArray fn_sys_dict,
jbyteArray fn_usr_dict) {
jbyte *fsd = (*env).GetByteArrayElements(fn_sys_dict, 0);
jbyte *fud = (*env).GetByteArrayElements(fn_usr_dict, 0);
jboolean jret = JNI_FALSE;
if (im_open_decoder((const char*)fsd, (const char*)fud))
jret = JNI_TRUE;
(*env).ReleaseByteArrayElements(fn_sys_dict, fsd, 0);
(*env).ReleaseByteArrayElements(fn_usr_dict, fud, 0);
return jret;
}
JNIEXPORT jboolean JNICALL nativeImOpenDecoderFd(JNIEnv* env, jclass jclazz,
jobject fd_sys_dict,
jlong startoffset,
jlong length,
jbyteArray fn_usr_dict) {
jint fd = env->GetIntField(fd_sys_dict, gFileDescriptorOffsets.mDescriptor);
jbyte *fud = (*env).GetByteArrayElements(fn_usr_dict, 0);
jboolean jret = JNI_FALSE;
int newfd = dup(fd);
if (im_open_decoder_fd(newfd, startoffset, length, (const char*)fud))
jret = JNI_TRUE;
close(newfd);
(*env).ReleaseByteArrayElements(fn_usr_dict, fud, 0);
return jret;
}
JNIEXPORT void JNICALL nativeImSetMaxLens(JNIEnv* env, jclass jclazz,
jint max_sps_len,
jint max_hzs_len) {
im_set_max_lens(static_cast<size_t>(max_sps_len),
static_cast<size_t>(max_hzs_len));
return;
}
JNIEXPORT jboolean JNICALL nativeImCloseDecoder(JNIEnv* env, jclass jclazz) {
im_close_decoder();
return JNI_TRUE;
}
JNIEXPORT jint JNICALL nativeImSearch(JNIEnv* env, jclass jclazz,
jbyteArray pybuf, jint pylen) {
jbyte *array_body = (*env).GetByteArrayElements(pybuf, 0);
jint jret = 0;
if (NULL != array_body) {
jret = im_search((const char*)array_body, pylen);
}
(*env).ReleaseByteArrayElements(pybuf, array_body, 0);
return jret;
}
JNIEXPORT jint JNICALL nativeImDelSearch(JNIEnv* env, jclass jclazz, jint pos,
jboolean is_pos_in_splid,
jboolean clear_fixed_this_step) {
return im_delsearch(pos, is_pos_in_splid, clear_fixed_this_step);
}
JNIEXPORT void JNICALL nativeImResetSearch(JNIEnv* env, jclass jclazz) {
im_reset_search();
return;
}
JNIEXPORT jint JNICALL nativeImAddLetter(JNIEnv *env, jclass clazz, jbyte ch) {
return im_add_letter(ch);
}
JNIEXPORT jstring JNICALL nativeImGetPyStr(JNIEnv* env, jclass jclazz,
jboolean decoded) {
size_t py_len;
const char *py = im_get_sps_str(&py_len); // py_len gets decoded length
assert(NULL != py);
if (!decoded)
py_len = strlen(py);
const unsigned short *spl_start;
size_t len;
len = im_get_spl_start_pos(spl_start);
size_t i;
for (i = 0; i < py_len; i++)
retbuf[i] = py[i];
retbuf[i] = (char16)'\0';
jstring retstr = (*env).NewString((unsigned short*)retbuf, i);
return retstr;
}
JNIEXPORT jint JNICALL nativeImGetPyStrLen(JNIEnv* env, jclass jclazz,
jboolean decoded) {
size_t py_len;
const char *py = im_get_sps_str(&py_len); // py_len gets decoded length
assert(NULL != py);
if (!decoded)
py_len = strlen(py);
return py_len;
}
JNIEXPORT jintArray JNICALL nativeImGetSplStart(JNIEnv* env, jclass jclazz) {
const unsigned short *spl_start;
size_t len;
// There will be len + 1 elements in the buffer when len > 0.
len = im_get_spl_start_pos(spl_start);
jintArray arr = (*env).NewIntArray(len + 2);
jint *arr_body = (*env).GetIntArrayElements(arr, 0);
assert(NULL != arr_body);
arr_body[0] = len; // element 0 is used to store the length of buffer.
for (size_t i = 0; i <= len; i++)
arr_body[i + 1] = spl_start[i];
(*env).ReleaseIntArrayElements(arr, arr_body, 0);
return arr;
}
JNIEXPORT jstring JNICALL nativeImGetChoice(JNIEnv *env, jclass clazz,
jint candidateId) {
jstring retstr;
if(im_get_candidate(candidateId, retbuf, RET_BUF_LEN)) {
retstr = (*env).NewString(retbuf, utf16_strlen(retbuf));
return retstr;
} else {
retstr = (*env).NewString((unsigned short*)retbuf, 0);
return retstr;
}
}
JNIEXPORT jint JNICALL nativeImChoose(JNIEnv *env, jclass clazz,
jint choice_id) {
return im_choose(choice_id);
}
JNIEXPORT jint JNICALL nativeImCancelLastChoice(JNIEnv *env, jclass clazz) {
return im_cancel_last_choice();
}
JNIEXPORT jint JNICALL nativeImGetFixedLen(JNIEnv *env, jclass clazz) {
return im_get_fixed_len();
}
JNIEXPORT jboolean JNICALL nativeImCancelInput(JNIEnv *env, jclass clazz) {
if (im_cancel_input())
return JNI_TRUE;
return JNI_FALSE;
}
JNIEXPORT jboolean JNICALL nativeImFlushCache(JNIEnv *env, jclass clazz) {
im_flush_cache();
return JNI_TRUE;
}
JNIEXPORT jint JNICALL nativeImGetPredictsNum(JNIEnv *env, jclass clazz,
jstring fixed_str) {
char16 *fixed_ptr = (char16*)(*env).GetStringChars(fixed_str, false);
size_t fixed_len = (size_t)(*env).GetStringLength(fixed_str);
char16 fixed_buf[kMaxPredictSize + 1];
if (fixed_len > kMaxPredictSize) {
fixed_ptr += fixed_len - kMaxPredictSize;
fixed_len = kMaxPredictSize;
}
utf16_strncpy(fixed_buf, fixed_ptr, fixed_len);
fixed_buf[fixed_len] = (char16)'\0';
predict_len = im_get_predicts(fixed_buf, predict_buf);
(*env).ReleaseStringChars(fixed_str, fixed_ptr);
return predict_len;
}
JNIEXPORT jstring JNICALL nativeImGetPredictItem(JNIEnv *env, jclass clazz,
jint predict_no) {
jstring retstr;
if (predict_no < 0 || (size_t)predict_no >= predict_len) {
retstr = (*env).NewString((unsigned short*)predict_buf[0], 0);
} else {
retstr = (*env).NewString((unsigned short*)predict_buf[predict_no],
utf16_strlen(predict_buf[predict_no]));
}
return retstr;
}
JNIEXPORT jboolean JNICALL nativeSyncBegin(JNIEnv *env, jclass clazz,
jbyteArray dict_file) {
jbyte *file_name = (*env).GetByteArrayElements(dict_file, 0);
jboolean jret = JNI_FALSE;
if (true == sync_worker.begin((const char *)file_name))
jret = JNI_TRUE;
(*env).ReleaseByteArrayElements(dict_file, file_name, 0);
return jret;
}
JNIEXPORT jboolean JNICALL nativeSyncFinish(JNIEnv *env, jclass clazz) {
sync_worker.finish();
return JNI_TRUE;
}
JNIEXPORT jint JNICALL nativeSyncGetCapacity(JNIEnv *env, jclass clazz) {
return sync_worker.get_capacity();
}
JNIEXPORT jint JNICALL nativeSyncPutLemmas(JNIEnv *env, jclass clazz,
jstring tomerge) {
char16 *ptr = (char16*)(*env).GetStringChars(tomerge, NULL);
int len = (size_t)(*env).GetStringLength(tomerge);
int added = sync_worker.put_lemmas(ptr, len);
(*env).ReleaseStringChars(tomerge, ptr);
return added;
}
JNIEXPORT jstring JNICALL nativeSyncGetLemmas(JNIEnv *env, jclass clazz) {
int len = sync_worker.get_lemmas(retbuf, RET_BUF_LEN);
if (len == 0)
return NULL;
jstring retstr;
retstr = (*env).NewString((unsigned short*)retbuf, len);
return retstr;
}
JNIEXPORT jint JNICALL nativeSyncGetLastCount(JNIEnv *env, jclass clazz) {
return sync_worker.get_last_got_count();
}
JNIEXPORT jint JNICALL nativeSyncGetTotalCount(JNIEnv *env, jclass clazz) {
return sync_worker.get_total_count();
}
JNIEXPORT jboolean JNICALL nativeSyncClearLastGot(JNIEnv *env, jclass clazz) {
sync_worker.clear_last_got();
return JNI_TRUE;
}
/**
* Table of methods associated with a single class.
*/
static JNINativeMethod gMethods[] = {
/* name, signature, funcPtr */
/* ------Functions for Pinyin-to-hanzi decoding begin--------->> */
{ "nativeImOpenDecoder", "([B[B)Z",
(void*) nativeImOpenDecoder },
{ "nativeImOpenDecoderFd", "(Ljava/io/FileDescriptor;JJ[B)Z",
(void*) nativeImOpenDecoderFd },
{ "nativeImSetMaxLens", "(II)V",
(void*) nativeImSetMaxLens },
{ "nativeImCloseDecoder", "()Z",
(void*) nativeImCloseDecoder },
{ "nativeImSearch", "([BI)I",
(void*) nativeImSearch },
{ "nativeImDelSearch", "(IZZ)I",
(void*) nativeImDelSearch },
{ "nativeImResetSearch", "()V",
(void*) nativeImResetSearch },
{ "nativeImAddLetter", "(B)I",
(void*) nativeImAddLetter },
{ "nativeImGetPyStr", "(Z)Ljava/lang/String;",
(void*) nativeImGetPyStr },
{ "nativeImGetPyStrLen", "(Z)I",
(void*) nativeImGetPyStrLen },
{ "nativeImGetSplStart", "()[I",
(void*) nativeImGetSplStart },
{ "nativeImGetChoice", "(I)Ljava/lang/String;",
(void*) nativeImGetChoice },
{ "nativeImChoose", "(I)I",
(void*) nativeImChoose },
{ "nativeImCancelLastChoice", "()I",
(void*) nativeImCancelLastChoice },
{ "nativeImGetFixedLen", "()I",
(void*) nativeImGetFixedLen },
{ "nativeImGetPredictsNum", "(Ljava/lang/String;)I",
(void*) nativeImGetPredictsNum },
{ "nativeImGetPredictItem", "(I)Ljava/lang/String;",
(void*) nativeImGetPredictItem },
{ "nativeImCancelInput", "()Z",
(void*) nativeImCancelInput },
{ "nativeImFlushCache", "()Z",
(void*) nativeImFlushCache },
/* <<----Functions for Pinyin-to-hanzi decoding end------------- */
/* ------Functions for sync begin----------------------------->> */
{ "nativeSyncBegin", "([B)Z",
(void*) nativeSyncBegin },
{ "nativeSyncFinish", "()Z",
(void*) nativeSyncFinish },
{ "nativeSyncPutLemmas", "(Ljava/lang/String;)I",
(void*) nativeSyncPutLemmas },
{ "nativeSyncGetLemmas", "()Ljava/lang/String;",
(void*) nativeSyncGetLemmas },
{ "nativeSyncGetLastCount", "()I",
(void*) nativeSyncGetLastCount },
{ "nativeSyncGetTotalCount", "()I",
(void*) nativeSyncGetTotalCount },
{ "nativeSyncClearLastGot", "()Z",
(void*) nativeSyncClearLastGot },
{ "nativeSyncGetCapacity", "()I",
(void*) nativeSyncGetCapacity },
/* <<----Functions for sync end--------------------------------- */
};
/*
* Register several native methods for one class.
*/
static int registerNativeMethods(JNIEnv* env, const char* className,
JNINativeMethod* gMethods, int numMethods)
{
jclass clazz;
clazz = (*env).FindClass(className);
if (clazz == NULL) {
return JNI_FALSE;
}
if ((*env).RegisterNatives(clazz, gMethods, numMethods) < 0) {
return JNI_FALSE;
}
clazz = env->FindClass("java/io/FileDescriptor");
LOG_FATAL_IF(clazz == NULL, "Unable to find Java class java.io.FileDescriptor");
gFileDescriptorOffsets.mClass = (jclass) env->NewGlobalRef(clazz);
gFileDescriptorOffsets.mDescriptor = env->GetFieldID(clazz, "descriptor", "I");
LOG_FATAL_IF(gFileDescriptorOffsets.mDescriptor == NULL,
"Unable to find descriptor field in java.io.FileDescriptor");
return JNI_TRUE;
}
/*
* Register native methods for all classes we know about.
*/
static int registerNatives(JNIEnv* env)
{
if (!registerNativeMethods(env,
"com/android/inputmethod/pinyin/PinyinDecoderService",
gMethods, sizeof(gMethods) / sizeof(gMethods[0])))
return JNI_FALSE;
return JNI_TRUE;
}
/*
* Set some test stuff up.
*
* Returns the JNI version on success, -1 on failure.
*/
JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM* vm, void* reserved)
{
JNIEnv* env = NULL;
jint result = -1;
if ((*vm).GetEnv((void**) &env, JNI_VERSION_1_4) != JNI_OK) {
goto bail;
}
assert(env != NULL);
if (!registerNatives(env)) {
goto bail;
}
/* success -- return valid version number */
result = JNI_VERSION_1_4;
bail:
return result;
}
#ifdef __cplusplus
}
#endif