Home | History | Annotate | Download | only in jni
      1 /*
      2  * Copyright (C) 2009 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 #define LOG_TAG "LatinIME: jni: BinaryDictionary"
     18 
     19 #include "com_android_inputmethod_latin_BinaryDictionary.h"
     20 
     21 #include <cstring> // for memset()
     22 
     23 #include "defines.h"
     24 #include "jni.h"
     25 #include "jni_common.h"
     26 #include "suggest/core/dictionary/dictionary.h"
     27 #include "suggest/core/suggest_options.h"
     28 #include "suggest/policyimpl/dictionary/dictionary_structure_with_buffer_policy_factory.h"
     29 #include "suggest/policyimpl/dictionary/utils/dict_file_writing_utils.h"
     30 #include "utils/autocorrection_threshold_utils.h"
     31 
     32 namespace latinime {
     33 
     34 class ProximityInfo;
     35 
     36 // TODO: Move to makedict.
     37 static jboolean latinime_BinaryDictionary_createEmptyDictFile(JNIEnv *env, jclass clazz,
     38         jstring filePath, jlong dictVersion, jobjectArray attributeKeyStringArray,
     39         jobjectArray attributeValueStringArray) {
     40     const jsize filePathUtf8Length = env->GetStringUTFLength(filePath);
     41     char filePathChars[filePathUtf8Length + 1];
     42     env->GetStringUTFRegion(filePath, 0, env->GetStringLength(filePath), filePathChars);
     43     filePathChars[filePathUtf8Length] = '\0';
     44 
     45     const int keyCount = env->GetArrayLength(attributeKeyStringArray);
     46     const int valueCount = env->GetArrayLength(attributeValueStringArray);
     47     if (keyCount != valueCount) {
     48         return false;
     49     }
     50 
     51     HeaderReadWriteUtils::AttributeMap attributeMap;
     52     for (int i = 0; i < keyCount; i++) {
     53         jstring keyString = static_cast<jstring>(
     54                 env->GetObjectArrayElement(attributeKeyStringArray, i));
     55         const jsize keyUtf8Length = env->GetStringUTFLength(keyString);
     56         char keyChars[keyUtf8Length + 1];
     57         env->GetStringUTFRegion(keyString, 0, env->GetStringLength(keyString), keyChars);
     58         keyChars[keyUtf8Length] = '\0';
     59         HeaderReadWriteUtils::AttributeMap::key_type key;
     60         HeaderReadWriteUtils::insertCharactersIntoVector(keyChars, &key);
     61 
     62         jstring valueString = static_cast<jstring>(
     63                 env->GetObjectArrayElement(attributeValueStringArray, i));
     64         const jsize valueUtf8Length = env->GetStringUTFLength(valueString);
     65         char valueChars[valueUtf8Length + 1];
     66         env->GetStringUTFRegion(valueString, 0, env->GetStringLength(valueString), valueChars);
     67         valueChars[valueUtf8Length] = '\0';
     68         HeaderReadWriteUtils::AttributeMap::mapped_type value;
     69         HeaderReadWriteUtils::insertCharactersIntoVector(valueChars, &value);
     70         attributeMap[key] = value;
     71     }
     72 
     73     return DictFileWritingUtils::createEmptyDictFile(filePathChars, static_cast<int>(dictVersion),
     74             &attributeMap);
     75 }
     76 
     77 static jlong latinime_BinaryDictionary_open(JNIEnv *env, jclass clazz, jstring sourceDir,
     78         jlong dictOffset, jlong dictSize, jboolean isUpdatable) {
     79     PROF_OPEN;
     80     PROF_START(66);
     81     const jsize sourceDirUtf8Length = env->GetStringUTFLength(sourceDir);
     82     if (sourceDirUtf8Length <= 0) {
     83         AKLOGE("DICT: Can't get sourceDir string");
     84         return 0;
     85     }
     86     char sourceDirChars[sourceDirUtf8Length + 1];
     87     env->GetStringUTFRegion(sourceDir, 0, env->GetStringLength(sourceDir), sourceDirChars);
     88     sourceDirChars[sourceDirUtf8Length] = '\0';
     89     DictionaryStructureWithBufferPolicy *const dictionaryStructureWithBufferPolicy =
     90             DictionaryStructureWithBufferPolicyFactory::newDictionaryStructureWithBufferPolicy(
     91                     sourceDirChars, static_cast<int>(dictOffset), static_cast<int>(dictSize),
     92                     isUpdatable == JNI_TRUE);
     93     if (!dictionaryStructureWithBufferPolicy) {
     94         return 0;
     95     }
     96 
     97     Dictionary *const dictionary = new Dictionary(env, dictionaryStructureWithBufferPolicy);
     98     PROF_END(66);
     99     PROF_CLOSE;
    100     return reinterpret_cast<jlong>(dictionary);
    101 }
    102 
    103 static void latinime_BinaryDictionary_flush(JNIEnv *env, jclass clazz, jlong dict,
    104         jstring filePath) {
    105     Dictionary *dictionary = reinterpret_cast<Dictionary *>(dict);
    106     if (!dictionary) return;
    107     const jsize filePathUtf8Length = env->GetStringUTFLength(filePath);
    108     char filePathChars[filePathUtf8Length + 1];
    109     env->GetStringUTFRegion(filePath, 0, env->GetStringLength(filePath), filePathChars);
    110     filePathChars[filePathUtf8Length] = '\0';
    111     dictionary->flush(filePathChars);
    112 }
    113 
    114 static bool latinime_BinaryDictionary_needsToRunGC(JNIEnv *env, jclass clazz,
    115         jlong dict, jboolean mindsBlockByGC) {
    116     Dictionary *dictionary = reinterpret_cast<Dictionary *>(dict);
    117     if (!dictionary) return false;
    118     return dictionary->needsToRunGC(mindsBlockByGC == JNI_TRUE);
    119 }
    120 
    121 static void latinime_BinaryDictionary_flushWithGC(JNIEnv *env, jclass clazz, jlong dict,
    122         jstring filePath) {
    123     Dictionary *dictionary = reinterpret_cast<Dictionary *>(dict);
    124     if (!dictionary) return;
    125     const jsize filePathUtf8Length = env->GetStringUTFLength(filePath);
    126     char filePathChars[filePathUtf8Length + 1];
    127     env->GetStringUTFRegion(filePath, 0, env->GetStringLength(filePath), filePathChars);
    128     filePathChars[filePathUtf8Length] = '\0';
    129     dictionary->flushWithGC(filePathChars);
    130 }
    131 
    132 static void latinime_BinaryDictionary_close(JNIEnv *env, jclass clazz, jlong dict) {
    133     Dictionary *dictionary = reinterpret_cast<Dictionary *>(dict);
    134     if (!dictionary) return;
    135     delete dictionary;
    136 }
    137 
    138 static int latinime_BinaryDictionary_getSuggestions(JNIEnv *env, jclass clazz, jlong dict,
    139         jlong proximityInfo, jlong dicTraverseSession, jintArray xCoordinatesArray,
    140         jintArray yCoordinatesArray, jintArray timesArray, jintArray pointerIdsArray,
    141         jintArray inputCodePointsArray, jint inputSize, jint commitPoint, jintArray suggestOptions,
    142         jintArray prevWordCodePointsForBigrams, jintArray outputCodePointsArray,
    143         jintArray scoresArray, jintArray spaceIndicesArray, jintArray outputTypesArray,
    144         jintArray outputAutoCommitFirstWordConfidenceArray) {
    145     Dictionary *dictionary = reinterpret_cast<Dictionary *>(dict);
    146     if (!dictionary) return 0;
    147     ProximityInfo *pInfo = reinterpret_cast<ProximityInfo *>(proximityInfo);
    148     DicTraverseSession *traverseSession =
    149             reinterpret_cast<DicTraverseSession *>(dicTraverseSession);
    150 
    151     // Input values
    152     int xCoordinates[inputSize];
    153     int yCoordinates[inputSize];
    154     int times[inputSize];
    155     int pointerIds[inputSize];
    156     const jsize inputCodePointsLength = env->GetArrayLength(inputCodePointsArray);
    157     int inputCodePoints[inputCodePointsLength];
    158     const jsize prevWordCodePointsLength =
    159             prevWordCodePointsForBigrams ? env->GetArrayLength(prevWordCodePointsForBigrams) : 0;
    160     int prevWordCodePointsInternal[prevWordCodePointsLength];
    161     int *prevWordCodePoints = 0;
    162     env->GetIntArrayRegion(xCoordinatesArray, 0, inputSize, xCoordinates);
    163     env->GetIntArrayRegion(yCoordinatesArray, 0, inputSize, yCoordinates);
    164     env->GetIntArrayRegion(timesArray, 0, inputSize, times);
    165     env->GetIntArrayRegion(pointerIdsArray, 0, inputSize, pointerIds);
    166     env->GetIntArrayRegion(inputCodePointsArray, 0, inputCodePointsLength, inputCodePoints);
    167     if (prevWordCodePointsForBigrams) {
    168         env->GetIntArrayRegion(prevWordCodePointsForBigrams, 0, prevWordCodePointsLength,
    169                 prevWordCodePointsInternal);
    170         prevWordCodePoints = prevWordCodePointsInternal;
    171     }
    172 
    173     const jsize numberOfOptions = env->GetArrayLength(suggestOptions);
    174     int options[numberOfOptions];
    175     env->GetIntArrayRegion(suggestOptions, 0, numberOfOptions, options);
    176     SuggestOptions givenSuggestOptions(options, numberOfOptions);
    177 
    178     // Output values
    179     /* By the way, let's check the output array length here to make sure */
    180     const jsize outputCodePointsLength = env->GetArrayLength(outputCodePointsArray);
    181     if (outputCodePointsLength != (MAX_WORD_LENGTH * MAX_RESULTS)) {
    182         AKLOGE("Invalid outputCodePointsLength: %d", outputCodePointsLength);
    183         ASSERT(false);
    184         return 0;
    185     }
    186     const jsize scoresLength = env->GetArrayLength(scoresArray);
    187     if (scoresLength != MAX_RESULTS) {
    188         AKLOGE("Invalid scoresLength: %d", scoresLength);
    189         ASSERT(false);
    190         return 0;
    191     }
    192     int outputCodePoints[outputCodePointsLength];
    193     int scores[scoresLength];
    194     const jsize spaceIndicesLength = env->GetArrayLength(spaceIndicesArray);
    195     int spaceIndices[spaceIndicesLength];
    196     const jsize outputTypesLength = env->GetArrayLength(outputTypesArray);
    197     int outputTypes[outputTypesLength];
    198     const jsize outputAutoCommitFirstWordConfidenceLength =
    199             env->GetArrayLength(outputAutoCommitFirstWordConfidenceArray);
    200     // We only use the first result, as obviously we will only ever autocommit the first one
    201     ASSERT(outputAutoCommitFirstWordConfidenceLength == 1);
    202     int outputAutoCommitFirstWordConfidence[outputAutoCommitFirstWordConfidenceLength];
    203     memset(outputCodePoints, 0, sizeof(outputCodePoints));
    204     memset(scores, 0, sizeof(scores));
    205     memset(spaceIndices, 0, sizeof(spaceIndices));
    206     memset(outputTypes, 0, sizeof(outputTypes));
    207     memset(outputAutoCommitFirstWordConfidence, 0, sizeof(outputAutoCommitFirstWordConfidence));
    208 
    209     int count;
    210     if (givenSuggestOptions.isGesture() || inputSize > 0) {
    211         count = dictionary->getSuggestions(pInfo, traverseSession, xCoordinates, yCoordinates,
    212                 times, pointerIds, inputCodePoints, inputSize, prevWordCodePoints,
    213                 prevWordCodePointsLength, commitPoint, &givenSuggestOptions, outputCodePoints,
    214                 scores, spaceIndices, outputTypes, outputAutoCommitFirstWordConfidence);
    215     } else {
    216         count = dictionary->getBigrams(prevWordCodePoints, prevWordCodePointsLength,
    217                 outputCodePoints, scores, outputTypes);
    218     }
    219 
    220     // Copy back the output values
    221     env->SetIntArrayRegion(outputCodePointsArray, 0, outputCodePointsLength, outputCodePoints);
    222     env->SetIntArrayRegion(scoresArray, 0, scoresLength, scores);
    223     env->SetIntArrayRegion(spaceIndicesArray, 0, spaceIndicesLength, spaceIndices);
    224     env->SetIntArrayRegion(outputTypesArray, 0, outputTypesLength, outputTypes);
    225     env->SetIntArrayRegion(outputAutoCommitFirstWordConfidenceArray, 0,
    226             outputAutoCommitFirstWordConfidenceLength, outputAutoCommitFirstWordConfidence);
    227 
    228     return count;
    229 }
    230 
    231 static jint latinime_BinaryDictionary_getProbability(JNIEnv *env, jclass clazz, jlong dict,
    232         jintArray word) {
    233     Dictionary *dictionary = reinterpret_cast<Dictionary *>(dict);
    234     if (!dictionary) return NOT_A_PROBABILITY;
    235     const jsize wordLength = env->GetArrayLength(word);
    236     int codePoints[wordLength];
    237     env->GetIntArrayRegion(word, 0, wordLength, codePoints);
    238     return dictionary->getProbability(codePoints, wordLength);
    239 }
    240 
    241 static jint latinime_BinaryDictionary_getBigramProbability(JNIEnv *env, jclass clazz,
    242         jlong dict, jintArray word0, jintArray word1) {
    243     Dictionary *dictionary = reinterpret_cast<Dictionary *>(dict);
    244     if (!dictionary) return JNI_FALSE;
    245     const jsize word0Length = env->GetArrayLength(word0);
    246     const jsize word1Length = env->GetArrayLength(word1);
    247     int word0CodePoints[word0Length];
    248     int word1CodePoints[word1Length];
    249     env->GetIntArrayRegion(word0, 0, word0Length, word0CodePoints);
    250     env->GetIntArrayRegion(word1, 0, word1Length, word1CodePoints);
    251     return dictionary->getBigramProbability(word0CodePoints, word0Length, word1CodePoints,
    252             word1Length);
    253 }
    254 
    255 static jfloat latinime_BinaryDictionary_calcNormalizedScore(JNIEnv *env, jclass clazz,
    256         jintArray before, jintArray after, jint score) {
    257     jsize beforeLength = env->GetArrayLength(before);
    258     jsize afterLength = env->GetArrayLength(after);
    259     int beforeCodePoints[beforeLength];
    260     int afterCodePoints[afterLength];
    261     env->GetIntArrayRegion(before, 0, beforeLength, beforeCodePoints);
    262     env->GetIntArrayRegion(after, 0, afterLength, afterCodePoints);
    263     return AutocorrectionThresholdUtils::calcNormalizedScore(beforeCodePoints, beforeLength,
    264             afterCodePoints, afterLength, score);
    265 }
    266 
    267 static jint latinime_BinaryDictionary_editDistance(JNIEnv *env, jclass clazz, jintArray before,
    268         jintArray after) {
    269     jsize beforeLength = env->GetArrayLength(before);
    270     jsize afterLength = env->GetArrayLength(after);
    271     int beforeCodePoints[beforeLength];
    272     int afterCodePoints[afterLength];
    273     env->GetIntArrayRegion(before, 0, beforeLength, beforeCodePoints);
    274     env->GetIntArrayRegion(after, 0, afterLength, afterCodePoints);
    275     return AutocorrectionThresholdUtils::editDistance(beforeCodePoints, beforeLength,
    276             afterCodePoints, afterLength);
    277 }
    278 
    279 static void latinime_BinaryDictionary_addUnigramWord(JNIEnv *env, jclass clazz, jlong dict,
    280         jintArray word, jint probability) {
    281     Dictionary *dictionary = reinterpret_cast<Dictionary *>(dict);
    282     if (!dictionary) {
    283         return;
    284     }
    285     jsize wordLength = env->GetArrayLength(word);
    286     int codePoints[wordLength];
    287     env->GetIntArrayRegion(word, 0, wordLength, codePoints);
    288     dictionary->addUnigramWord(codePoints, wordLength, probability);
    289 }
    290 
    291 static void latinime_BinaryDictionary_addBigramWords(JNIEnv *env, jclass clazz, jlong dict,
    292         jintArray word0, jintArray word1, jint probability) {
    293     Dictionary *dictionary = reinterpret_cast<Dictionary *>(dict);
    294     if (!dictionary) {
    295         return;
    296     }
    297     jsize word0Length = env->GetArrayLength(word0);
    298     int word0CodePoints[word0Length];
    299     env->GetIntArrayRegion(word0, 0, word0Length, word0CodePoints);
    300     jsize word1Length = env->GetArrayLength(word1);
    301     int word1CodePoints[word1Length];
    302     env->GetIntArrayRegion(word1, 0, word1Length, word1CodePoints);
    303     dictionary->addBigramWords(word0CodePoints, word0Length, word1CodePoints,
    304             word1Length, probability);
    305 }
    306 
    307 static void latinime_BinaryDictionary_removeBigramWords(JNIEnv *env, jclass clazz, jlong dict,
    308         jintArray word0, jintArray word1) {
    309     Dictionary *dictionary = reinterpret_cast<Dictionary *>(dict);
    310     if (!dictionary) {
    311         return;
    312     }
    313     jsize word0Length = env->GetArrayLength(word0);
    314     int word0CodePoints[word0Length];
    315     env->GetIntArrayRegion(word0, 0, word0Length, word0CodePoints);
    316     jsize word1Length = env->GetArrayLength(word1);
    317     int word1CodePoints[word1Length];
    318     env->GetIntArrayRegion(word1, 0, word1Length, word1CodePoints);
    319     dictionary->removeBigramWords(word0CodePoints, word0Length, word1CodePoints,
    320             word1Length);
    321 }
    322 
    323 static int latinime_BinaryDictionary_calculateProbabilityNative(JNIEnv *env, jclass clazz,
    324         jlong dict, jint unigramProbability, jint bigramProbability) {
    325     Dictionary *dictionary = reinterpret_cast<Dictionary *>(dict);
    326     if (!dictionary) {
    327         return NOT_A_PROBABILITY;
    328     }
    329     return dictionary->getDictionaryStructurePolicy()->getProbability(unigramProbability,
    330             bigramProbability);
    331 }
    332 
    333 static jstring latinime_BinaryDictionary_getProperty(JNIEnv *env, jclass clazz, jlong dict,
    334         jstring query) {
    335     Dictionary *dictionary = reinterpret_cast<Dictionary *>(dict);
    336     if (!dictionary) {
    337         return env->NewStringUTF("");
    338     }
    339     const jsize queryUtf8Length = env->GetStringUTFLength(query);
    340     char queryChars[queryUtf8Length + 1];
    341     env->GetStringUTFRegion(query, 0, env->GetStringLength(query), queryChars);
    342     queryChars[queryUtf8Length] = '\0';
    343     static const int GET_PROPERTY_RESULT_LENGTH = 100;
    344     char resultChars[GET_PROPERTY_RESULT_LENGTH];
    345     resultChars[0] = '\0';
    346     dictionary->getProperty(queryChars, resultChars, GET_PROPERTY_RESULT_LENGTH);
    347     return env->NewStringUTF(resultChars);
    348 }
    349 
    350 static const JNINativeMethod sMethods[] = {
    351     {
    352         const_cast<char *>("createEmptyDictFileNative"),
    353         const_cast<char *>("(Ljava/lang/String;J[Ljava/lang/String;[Ljava/lang/String;)Z"),
    354         reinterpret_cast<void *>(latinime_BinaryDictionary_createEmptyDictFile)
    355     },
    356     {
    357         const_cast<char *>("openNative"),
    358         const_cast<char *>("(Ljava/lang/String;JJZ)J"),
    359         reinterpret_cast<void *>(latinime_BinaryDictionary_open)
    360     },
    361     {
    362         const_cast<char *>("closeNative"),
    363         const_cast<char *>("(J)V"),
    364         reinterpret_cast<void *>(latinime_BinaryDictionary_close)
    365     },
    366     {
    367         const_cast<char *>("flushNative"),
    368         const_cast<char *>("(JLjava/lang/String;)V"),
    369         reinterpret_cast<void *>(latinime_BinaryDictionary_flush)
    370     },
    371     {
    372         const_cast<char *>("needsToRunGCNative"),
    373         const_cast<char *>("(JZ)Z"),
    374         reinterpret_cast<void *>(latinime_BinaryDictionary_needsToRunGC)
    375     },
    376     {
    377         const_cast<char *>("flushWithGCNative"),
    378         const_cast<char *>("(JLjava/lang/String;)V"),
    379         reinterpret_cast<void *>(latinime_BinaryDictionary_flushWithGC)
    380     },
    381     {
    382         const_cast<char *>("getSuggestionsNative"),
    383         const_cast<char *>("(JJJ[I[I[I[I[III[I[I[I[I[I[I[I)I"),
    384         reinterpret_cast<void *>(latinime_BinaryDictionary_getSuggestions)
    385     },
    386     {
    387         const_cast<char *>("getProbabilityNative"),
    388         const_cast<char *>("(J[I)I"),
    389         reinterpret_cast<void *>(latinime_BinaryDictionary_getProbability)
    390     },
    391     {
    392         const_cast<char *>("getBigramProbabilityNative"),
    393         const_cast<char *>("(J[I[I)I"),
    394         reinterpret_cast<void *>(latinime_BinaryDictionary_getBigramProbability)
    395     },
    396     {
    397         const_cast<char *>("calcNormalizedScoreNative"),
    398         const_cast<char *>("([I[II)F"),
    399         reinterpret_cast<void *>(latinime_BinaryDictionary_calcNormalizedScore)
    400     },
    401     {
    402         const_cast<char *>("editDistanceNative"),
    403         const_cast<char *>("([I[I)I"),
    404         reinterpret_cast<void *>(latinime_BinaryDictionary_editDistance)
    405     },
    406     {
    407         const_cast<char *>("addUnigramWordNative"),
    408         const_cast<char *>("(J[II)V"),
    409         reinterpret_cast<void *>(latinime_BinaryDictionary_addUnigramWord)
    410     },
    411     {
    412         const_cast<char *>("addBigramWordsNative"),
    413         const_cast<char *>("(J[I[II)V"),
    414         reinterpret_cast<void *>(latinime_BinaryDictionary_addBigramWords)
    415     },
    416     {
    417         const_cast<char *>("removeBigramWordsNative"),
    418         const_cast<char *>("(J[I[I)V"),
    419         reinterpret_cast<void *>(latinime_BinaryDictionary_removeBigramWords)
    420     },
    421     {
    422         const_cast<char *>("calculateProbabilityNative"),
    423         const_cast<char *>("(JII)I"),
    424         reinterpret_cast<void *>(latinime_BinaryDictionary_calculateProbabilityNative)
    425     },
    426     {
    427         const_cast<char *>("getPropertyNative"),
    428         const_cast<char *>("(JLjava/lang/String;)Ljava/lang/String;"),
    429         reinterpret_cast<void *>(latinime_BinaryDictionary_getProperty)
    430     }
    431 };
    432 
    433 int register_BinaryDictionary(JNIEnv *env) {
    434     const char *const kClassPathName = "com/android/inputmethod/latin/BinaryDictionary";
    435     return registerNativeMethods(env, kClassPathName, sMethods, NELEMS(sMethods));
    436 }
    437 } // namespace latinime
    438