1/*
2**
3** Copyright 2009, The Android Open Source Project
4**
5** Licensed under the Apache License, Version 2.0 (the "License");
6** you may not use this file except in compliance with the License.
7** You may obtain a copy of the License at
8**
9**     http://www.apache.org/licenses/LICENSE-2.0
10**
11** Unless required by applicable law or agreed to in writing, software
12** distributed under the License is distributed on an "AS IS" BASIS,
13** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14** See the License for the specific language governing permissions and
15** limitations under the License.
16*/
17
18#define LOG_TAG "BinaryDictionary"
19#include "utils/Log.h"
20
21#include <stdio.h>
22#include <assert.h>
23#include <unistd.h>
24#include <fcntl.h>
25
26#include <nativehelper/jni.h>
27#include "utils/AssetManager.h"
28#include "utils/Asset.h"
29
30#include "dictionary.h"
31
32// ----------------------------------------------------------------------------
33
34using namespace latinime;
35
36using namespace android;
37
38static jfieldID sDescriptorField;
39static jfieldID sAssetManagerNativeField;
40static jmethodID sAddWordMethod;
41static jfieldID sDictLength;
42
43//
44// helper function to throw an exception
45//
46static void throwException(JNIEnv *env, const char* ex, const char* fmt, int data)
47{
48    if (jclass cls = env->FindClass(ex)) {
49        char msg[1000];
50        sprintf(msg, fmt, data);
51        env->ThrowNew(cls, msg);
52        env->DeleteLocalRef(cls);
53    }
54}
55
56static jint latinime_BinaryDictionary_open
57        (JNIEnv *env, jobject object, jobject assetManager, jstring resourceString,
58         jint typedLetterMultiplier, jint fullWordMultiplier)
59{
60    // Get the native file descriptor from the FileDescriptor object
61    AssetManager *am = (AssetManager*) env->GetIntField(assetManager, sAssetManagerNativeField);
62    if (!am) {
63        LOGE("DICT: Couldn't get AssetManager native peer\n");
64        return 0;
65    }
66    const char *resourcePath = env->GetStringUTFChars(resourceString, NULL);
67
68    Asset *dictAsset = am->openNonAsset(resourcePath, Asset::ACCESS_BUFFER);
69    if (dictAsset == NULL) {
70        LOGE("DICT: Couldn't get asset %s\n", resourcePath);
71        env->ReleaseStringUTFChars(resourceString, resourcePath);
72        return 0;
73    }
74
75    void *dict = (void*) dictAsset->getBuffer(false);
76    if (dict == NULL) {
77        LOGE("DICT: Dictionary buffer is null\n");
78        env->ReleaseStringUTFChars(resourceString, resourcePath);
79        return 0;
80    }
81    Dictionary *dictionary = new Dictionary(dict, typedLetterMultiplier, fullWordMultiplier);
82    dictionary->setAsset(dictAsset);
83    env->SetIntField(object, sDictLength, (jint) dictAsset->getLength());
84
85    env->ReleaseStringUTFChars(resourceString, resourcePath);
86    return (jint) dictionary;
87}
88
89static int latinime_BinaryDictionary_getSuggestions(
90        JNIEnv *env, jobject object, jint dict, jintArray inputArray, jint arraySize,
91        jcharArray outputArray, jintArray frequencyArray, jint maxWordLength, jint maxWords,
92        jint maxAlternatives, jint skipPos, jintArray nextLettersArray, jint nextLettersSize)
93{
94    Dictionary *dictionary = (Dictionary*) dict;
95    if (dictionary == NULL)
96        return 0;
97
98    int *frequencies = env->GetIntArrayElements(frequencyArray, NULL);
99    int *inputCodes = env->GetIntArrayElements(inputArray, NULL);
100    jchar *outputChars = env->GetCharArrayElements(outputArray, NULL);
101    int *nextLetters = nextLettersArray != NULL ? env->GetIntArrayElements(nextLettersArray, NULL)
102            : NULL;
103
104    int count = dictionary->getSuggestions(inputCodes, arraySize, (unsigned short*) outputChars, frequencies,
105            maxWordLength, maxWords, maxAlternatives, skipPos, nextLetters, nextLettersSize);
106
107    env->ReleaseIntArrayElements(frequencyArray, frequencies, 0);
108    env->ReleaseIntArrayElements(inputArray, inputCodes, JNI_ABORT);
109    env->ReleaseCharArrayElements(outputArray, outputChars, 0);
110    if (nextLetters) {
111        env->ReleaseIntArrayElements(nextLettersArray, nextLetters, 0);
112    }
113
114    return count;
115}
116
117static jboolean latinime_BinaryDictionary_isValidWord
118        (JNIEnv *env, jobject object, jint dict, jcharArray wordArray, jint wordLength)
119{
120    Dictionary *dictionary = (Dictionary*) dict;
121    if (dictionary == NULL) return (jboolean) false;
122
123    jchar *word = env->GetCharArrayElements(wordArray, NULL);
124    jboolean result = dictionary->isValidWord((unsigned short*) word, wordLength);
125    env->ReleaseCharArrayElements(wordArray, word, JNI_ABORT);
126
127    return result;
128}
129
130static void latinime_BinaryDictionary_close
131        (JNIEnv *env, jobject object, jint dict)
132{
133    Dictionary *dictionary = (Dictionary*) dict;
134    ((Asset*) dictionary->getAsset())->close();
135    delete (Dictionary*) dict;
136}
137
138// ----------------------------------------------------------------------------
139
140static JNINativeMethod gMethods[] = {
141    {"openNative",           "(Landroid/content/res/AssetManager;Ljava/lang/String;II)I",
142                                          (void*)latinime_BinaryDictionary_open},
143    {"closeNative",          "(I)V",            (void*)latinime_BinaryDictionary_close},
144    {"getSuggestionsNative", "(I[II[C[IIIII[II)I",  (void*)latinime_BinaryDictionary_getSuggestions},
145    {"isValidWordNative",    "(I[CI)Z",         (void*)latinime_BinaryDictionary_isValidWord}
146};
147
148static int registerNativeMethods(JNIEnv* env, const char* className,
149    JNINativeMethod* gMethods, int numMethods)
150{
151    jclass clazz;
152
153    clazz = env->FindClass(className);
154    if (clazz == NULL) {
155        fprintf(stderr,
156            "Native registration unable to find class '%s'\n", className);
157        return JNI_FALSE;
158    }
159    if (env->RegisterNatives(clazz, gMethods, numMethods) < 0) {
160        fprintf(stderr, "RegisterNatives failed for '%s'\n", className);
161        return JNI_FALSE;
162    }
163
164    return JNI_TRUE;
165}
166
167static int registerNatives(JNIEnv *env)
168{
169    const char* const kClassPathName = "com/android/inputmethod/latin/BinaryDictionary";
170    jclass clazz;
171
172    clazz = env->FindClass("java/io/FileDescriptor");
173    if (clazz == NULL) {
174        LOGE("Can't find %s", "java/io/FileDescriptor");
175        return -1;
176    }
177    sDescriptorField = env->GetFieldID(clazz, "descriptor", "I");
178
179    clazz = env->FindClass("android/content/res/AssetManager");
180    if (clazz == NULL) {
181        LOGE("Can't find %s", "java/io/FileDescriptor");
182        return -1;
183    }
184    sAssetManagerNativeField = env->GetFieldID(clazz, "mObject", "I");
185
186    // Get the field pointer for the dictionary length
187    clazz = env->FindClass(kClassPathName);
188    if (clazz == NULL) {
189        LOGE("Can't find %s", kClassPathName);
190        return -1;
191    }
192    sDictLength = env->GetFieldID(clazz, "mDictLength", "I");
193
194    return registerNativeMethods(env,
195            kClassPathName, gMethods, sizeof(gMethods) / sizeof(gMethods[0]));
196}
197
198/*
199 * Returns the JNI version on success, -1 on failure.
200 */
201jint JNI_OnLoad(JavaVM* vm, void* reserved)
202{
203    JNIEnv* env = NULL;
204    jint result = -1;
205
206    if (vm->GetEnv((void**) &env, JNI_VERSION_1_4) != JNI_OK) {
207        fprintf(stderr, "ERROR: GetEnv failed\n");
208        goto bail;
209    }
210    assert(env != NULL);
211
212    if (!registerNatives(env)) {
213        fprintf(stderr, "ERROR: BinaryDictionary native registration failed\n");
214        goto bail;
215    }
216
217    /* success -- return valid version number */
218    result = JNI_VERSION_1_4;
219
220bail:
221    return result;
222}
223