BinaryDictionary.java revision da50e1e98dadc3733c615dfb8d87fe8b4688c782
1/*
2 * Copyright (C) 2008 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License"); you may not
5 * use this file except in compliance with the License. You may obtain a copy of
6 * 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, WITHOUT
12 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13 * License for the specific language governing permissions and limitations under
14 * the License.
15 */
16
17package com.android.inputmethod.latin;
18
19import android.content.Context;
20import android.content.res.AssetFileDescriptor;
21import android.util.Log;
22
23import java.util.Arrays;
24
25/**
26 * Implements a static, compacted, binary dictionary of standard words.
27 */
28public class BinaryDictionary extends Dictionary {
29
30    /**
31     * There is difference between what java and native code can handle.
32     * This value should only be used in BinaryDictionary.java
33     * It is necessary to keep it at this value because some languages e.g. German have
34     * really long words.
35     */
36    protected static final int MAX_WORD_LENGTH = 48;
37
38    private static final String TAG = "BinaryDictionary";
39    private static final int MAX_ALTERNATIVES = 16;
40    private static final int MAX_WORDS = 18;
41    private static final int MAX_BIGRAMS = 60;
42
43    private static final int TYPED_LETTER_MULTIPLIER = 2;
44
45    private static final BinaryDictionary sInstance = new BinaryDictionary();
46    private int mDicTypeId;
47    private int mNativeDict;
48    private long mDictLength;
49    private final int[] mInputCodes = new int[MAX_WORD_LENGTH * MAX_ALTERNATIVES];
50    private final char[] mOutputChars = new char[MAX_WORD_LENGTH * MAX_WORDS];
51    private final char[] mOutputChars_bigrams = new char[MAX_WORD_LENGTH * MAX_BIGRAMS];
52    private final int[] mFrequencies = new int[MAX_WORDS];
53    private final int[] mFrequencies_bigrams = new int[MAX_BIGRAMS];
54
55    static {
56        try {
57            System.loadLibrary("jni_latinime");
58        } catch (UnsatisfiedLinkError ule) {
59            Log.e(TAG, "Could not load native library jni_latinime");
60        }
61    }
62
63    private BinaryDictionary() {
64    }
65
66    /**
67     * Initialize a dictionary from a raw resource file
68     * @param context application context for reading resources
69     * @param resId the resource containing the raw binary dictionary
70     * @return initialized instance of BinaryDictionary
71     */
72    public static BinaryDictionary initDictionary(Context context, int resId, int dicTypeId) {
73        synchronized (sInstance) {
74            sInstance.closeInternal();
75            if (resId != 0) {
76                sInstance.loadDictionary(context, resId);
77                sInstance.mDicTypeId = dicTypeId;
78            }
79        }
80        return sInstance;
81    }
82
83    private native int openNative(String sourceDir, long dictOffset, long dictSize,
84            int typedLetterMultiplier, int fullWordMultiplier, int maxWordLength,
85            int maxWords, int maxAlternatives);
86    private native void closeNative(int dict);
87    private native boolean isValidWordNative(int nativeData, char[] word, int wordLength);
88    private native int getSuggestionsNative(int dict, int[] inputCodes, int codesSize,
89            char[] outputChars, int[] frequencies,
90            int[] nextLettersFrequencies, int nextLettersSize);
91    private native int getBigramsNative(int dict, char[] prevWord, int prevWordLength,
92            int[] inputCodes, int inputCodesLength, char[] outputChars, int[] frequencies,
93            int maxWordLength, int maxBigrams, int maxAlternatives);
94
95    private final void loadDictionary(Context context, int resId) {
96        try {
97            final AssetFileDescriptor afd = context.getResources().openRawResourceFd(resId);
98            if (afd == null) {
99                Log.e(TAG, "Found the resource but it is compressed. resId=" + resId);
100                return;
101            }
102            mNativeDict = openNative(context.getApplicationInfo().sourceDir,
103                    afd.getStartOffset(), afd.getLength(),
104                    TYPED_LETTER_MULTIPLIER, FULL_WORD_FREQ_MULTIPLIER,
105                    MAX_WORD_LENGTH, MAX_WORDS, MAX_ALTERNATIVES);
106            mDictLength = afd.getLength();
107        } catch (android.content.res.Resources.NotFoundException e) {
108            Log.e(TAG, "Could not find the resource. resId=" + resId);
109            return;
110        }
111    }
112
113    @Override
114    public void getBigrams(final WordComposer codes, final CharSequence previousWord,
115            final WordCallback callback, int[] nextLettersFrequencies) {
116        if (mNativeDict == 0) return;
117
118        char[] chars = previousWord.toString().toCharArray();
119        Arrays.fill(mOutputChars_bigrams, (char) 0);
120        Arrays.fill(mFrequencies_bigrams, 0);
121
122        int codesSize = codes.size();
123        Arrays.fill(mInputCodes, -1);
124        int[] alternatives = codes.getCodesAt(0);
125        System.arraycopy(alternatives, 0, mInputCodes, 0,
126                Math.min(alternatives.length, MAX_ALTERNATIVES));
127
128        int count = getBigramsNative(mNativeDict, chars, chars.length, mInputCodes, codesSize,
129                mOutputChars_bigrams, mFrequencies_bigrams, MAX_WORD_LENGTH, MAX_BIGRAMS,
130                MAX_ALTERNATIVES);
131
132        for (int j = 0; j < count; ++j) {
133            if (mFrequencies_bigrams[j] < 1) break;
134            final int start = j * MAX_WORD_LENGTH;
135            int len = 0;
136            while (len <  MAX_WORD_LENGTH && mOutputChars_bigrams[start + len] != 0) {
137                ++len;
138            }
139            if (len > 0) {
140                callback.addWord(mOutputChars_bigrams, start, len, mFrequencies_bigrams[j],
141                        mDicTypeId, DataType.BIGRAM);
142            }
143        }
144    }
145
146    @Override
147    public void getWords(final WordComposer codes, final WordCallback callback,
148            int[] nextLettersFrequencies) {
149        if (mNativeDict == 0) return;
150
151        final int codesSize = codes.size();
152        // Won't deal with really long words.
153        if (codesSize > MAX_WORD_LENGTH - 1) return;
154
155        Arrays.fill(mInputCodes, -1);
156        for (int i = 0; i < codesSize; i++) {
157            int[] alternatives = codes.getCodesAt(i);
158            System.arraycopy(alternatives, 0, mInputCodes, i * MAX_ALTERNATIVES,
159                    Math.min(alternatives.length, MAX_ALTERNATIVES));
160        }
161        Arrays.fill(mOutputChars, (char) 0);
162        Arrays.fill(mFrequencies, 0);
163
164        int count = getSuggestionsNative(mNativeDict, mInputCodes, codesSize, mOutputChars,
165                mFrequencies, nextLettersFrequencies,
166                nextLettersFrequencies != null ? nextLettersFrequencies.length : 0);
167
168        for (int j = 0; j < count; ++j) {
169            if (mFrequencies[j] < 1) break;
170            final int start = j * MAX_WORD_LENGTH;
171            int len = 0;
172            while (len < MAX_WORD_LENGTH && mOutputChars[start + len] != 0) {
173                ++len;
174            }
175            if (len > 0) {
176                callback.addWord(mOutputChars, start, len, mFrequencies[j], mDicTypeId,
177                        DataType.UNIGRAM);
178            }
179        }
180    }
181
182    @Override
183    public boolean isValidWord(CharSequence word) {
184        if (word == null) return false;
185        char[] chars = word.toString().toCharArray();
186        return isValidWordNative(mNativeDict, chars, chars.length);
187    }
188
189    public long getSize() {
190        return mDictLength; // This value is initialized in loadDictionary()
191    }
192
193    @Override
194    public synchronized void close() {
195        closeInternal();
196    }
197
198    private void closeInternal() {
199        if (mNativeDict != 0) {
200            closeNative(mNativeDict);
201            mNativeDict = 0;
202            mDictLength = 0;
203        }
204    }
205
206    @Override
207    protected void finalize() throws Throwable {
208        try {
209            closeInternal();
210        } finally {
211            super.finalize();
212        }
213    }
214}
215