Use translation of fallback umlauts digraphs for German.

For German : handle "ae", "oe" and "ue" to be alternate forms for
umlaut-bearing versions of "a", "o" and "u".

Issue: 3275926

Change-Id: I056c707cdacc464ceab63be56c016c7f8439196c
main
Jean Chalard 2011-02-25 17:56:53 +09:00
parent e59491460b
commit c2bbc6a449
8 changed files with 239 additions and 56 deletions

View File

@ -65,6 +65,7 @@
android:label="@string/subtype_mode_de_keyboard" android:label="@string/subtype_mode_de_keyboard"
android:imeSubtypeLocale="de" android:imeSubtypeLocale="de"
android:imeSubtypeMode="keyboard" android:imeSubtypeMode="keyboard"
android:imeSubtypeExtraValue="requiresGermanUmlautProcessing"
/> />
<subtype android:icon="@drawable/ic_subtype_mic" <subtype android:icon="@drawable/ic_subtype_mic"
android:label="@string/subtype_mode_de_voice" android:label="@string/subtype_mode_de_voice"

View File

@ -58,6 +58,25 @@ public class BinaryDictionary extends Dictionary {
private final int[] mFrequencies_bigrams = new int[MAX_BIGRAMS]; private final int[] mFrequencies_bigrams = new int[MAX_BIGRAMS];
private final KeyboardSwitcher mKeyboardSwitcher = KeyboardSwitcher.getInstance(); private final KeyboardSwitcher mKeyboardSwitcher = KeyboardSwitcher.getInstance();
private final SubtypeSwitcher mSubtypeSwitcher = SubtypeSwitcher.getInstance();
private static class Flags {
private static class FlagEntry {
public final String mName;
public final int mValue;
public FlagEntry(String name, int value) {
mName = name;
mValue = value;
}
}
public static final FlagEntry[] ALL_FLAGS = {
// Here should reside all flags that trigger some special processing
// These *must* match the definition in UnigramDictionary enum in
// unigram_dictionary.h so please update both at the same time.
new FlagEntry("requiresGermanUmlautProcessing", 0x1)
};
}
private int mFlags = 0;
private BinaryDictionary() { private BinaryDictionary() {
} }
@ -91,6 +110,7 @@ public class BinaryDictionary extends Dictionary {
return null; return null;
} }
} }
sInstance.initFlags();
return sInstance; return sInstance;
} }
@ -109,16 +129,26 @@ public class BinaryDictionary extends Dictionary {
return sInstance; return sInstance;
} }
private void initFlags() {
int flags = 0;
for (Flags.FlagEntry entry : Flags.ALL_FLAGS) {
if (mSubtypeSwitcher.currentSubtypeContainsExtraValueKey(entry.mName))
flags |= entry.mValue;
}
mFlags = flags;
}
static { static {
Utils.loadNativeLibrary(); Utils.loadNativeLibrary();
} }
private native int openNative(String sourceDir, long dictOffset, long dictSize, private native int openNative(String sourceDir, long dictOffset, long dictSize,
int typedLetterMultiplier, int fullWordMultiplier, int maxWordLength, int typedLetterMultiplier, int fullWordMultiplier, int maxWordLength,
int maxWords, int maxAlternatives); int maxWords, int maxAlternatives);
private native void closeNative(int dict); private native void closeNative(int dict);
private native boolean isValidWordNative(int nativeData, char[] word, int wordLength); private native boolean isValidWordNative(int nativeData, char[] word, int wordLength);
private native int getSuggestionsNative(int dict, int proximityInfo, int[] xCoordinates, private native int getSuggestionsNative(int dict, int proximityInfo, int[] xCoordinates,
int[] yCoordinates, int[] inputCodes, int codesSize, char[] outputChars, int[] yCoordinates, int[] inputCodes, int codesSize, int flags, char[] outputChars,
int[] frequencies); int[] frequencies);
private native int getBigramsNative(int dict, char[] prevWord, int prevWordLength, private native int getBigramsNative(int dict, char[] prevWord, int prevWordLength,
int[] inputCodes, int inputCodesLength, char[] outputChars, int[] frequencies, int[] inputCodes, int inputCodesLength, char[] outputChars, int[] frequencies,
@ -207,7 +237,7 @@ public class BinaryDictionary extends Dictionary {
return getSuggestionsNative( return getSuggestionsNative(
mNativeDict, keyboard.getProximityInfo(), mNativeDict, keyboard.getProximityInfo(),
codes.getXCoordinates(), codes.getYCoordinates(), mInputCodes, codesSize, codes.getXCoordinates(), codes.getYCoordinates(), mInputCodes, codesSize,
outputChars, frequencies); mFlags, outputChars, frequencies);
} }
@Override @Override

View File

@ -74,10 +74,10 @@ public class SubtypeSwitcher {
private InputMethodInfo mShortcutInputMethodInfo; private InputMethodInfo mShortcutInputMethodInfo;
private InputMethodSubtype mShortcutSubtype; private InputMethodSubtype mShortcutSubtype;
private List<InputMethodSubtype> mAllEnabledSubtypesOfCurrentInputMethod; private List<InputMethodSubtype> mAllEnabledSubtypesOfCurrentInputMethod;
private InputMethodSubtype mCurrentSubtype;
private Locale mSystemLocale; private Locale mSystemLocale;
private Locale mInputLocale; private Locale mInputLocale;
private String mInputLocaleStr; private String mInputLocaleStr;
private String mMode;
private VoiceInput mVoiceInput; private VoiceInput mVoiceInput;
/*-----------------------------------------------------------*/ /*-----------------------------------------------------------*/
@ -110,8 +110,7 @@ public class SubtypeSwitcher {
mSystemLocale = null; mSystemLocale = null;
mInputLocale = null; mInputLocale = null;
mInputLocaleStr = null; mInputLocaleStr = null;
// Mode is initialized to KEYBOARD_MODE, in case that LatinIME can't obtain currentSubtype mCurrentSubtype = null;
mMode = KEYBOARD_MODE;
mAllEnabledSubtypesOfCurrentInputMethod = null; mAllEnabledSubtypesOfCurrentInputMethod = null;
// TODO: Voice input should be created here // TODO: Voice input should be created here
mVoiceInput = null; mVoiceInput = null;
@ -145,6 +144,7 @@ public class SubtypeSwitcher {
// Reload enabledSubtypes from the framework. // Reload enabledSubtypes from the framework.
private void updateEnabledSubtypes() { private void updateEnabledSubtypes() {
final String currentMode = getCurrentSubtypeMode();
boolean foundCurrentSubtypeBecameDisabled = true; boolean foundCurrentSubtypeBecameDisabled = true;
mAllEnabledSubtypesOfCurrentInputMethod = mImm.getEnabledInputMethodSubtypeList( mAllEnabledSubtypesOfCurrentInputMethod = mImm.getEnabledInputMethodSubtypeList(
null, true); null, true);
@ -157,7 +157,7 @@ public class SubtypeSwitcher {
if (mLocaleSplitter.hasNext()) { if (mLocaleSplitter.hasNext()) {
mEnabledLanguagesOfCurrentInputMethod.add(mLocaleSplitter.next()); mEnabledLanguagesOfCurrentInputMethod.add(mLocaleSplitter.next());
} }
if (locale.equals(mInputLocaleStr) && mode.equals(mMode)) { if (locale.equals(mInputLocaleStr) && mode.equals(currentMode)) {
foundCurrentSubtypeBecameDisabled = false; foundCurrentSubtypeBecameDisabled = false;
} }
if (KEYBOARD_MODE.equals(ims.getMode())) { if (KEYBOARD_MODE.equals(ims.getMode())) {
@ -168,7 +168,7 @@ public class SubtypeSwitcher {
&& mIsSystemLanguageSameAsInputLanguage); && mIsSystemLanguageSameAsInputLanguage);
if (foundCurrentSubtypeBecameDisabled) { if (foundCurrentSubtypeBecameDisabled) {
if (DBG) { if (DBG) {
Log.w(TAG, "Current subtype: " + mInputLocaleStr + ", " + mMode); Log.w(TAG, "Current subtype: " + mInputLocaleStr + ", " + currentMode);
Log.w(TAG, "Last subtype was disabled. Update to the current one."); Log.w(TAG, "Last subtype was disabled. Update to the current one.");
} }
updateSubtype(mImm.getCurrentInputMethodSubtype()); updateSubtype(mImm.getCurrentInputMethodSubtype());
@ -209,9 +209,10 @@ public class SubtypeSwitcher {
public void updateSubtype(InputMethodSubtype newSubtype) { public void updateSubtype(InputMethodSubtype newSubtype) {
final String newLocale; final String newLocale;
final String newMode; final String newMode;
final String oldMode = getCurrentSubtypeMode();
if (newSubtype == null) { if (newSubtype == null) {
// Normally, newSubtype shouldn't be null. But just in case newSubtype was null, // Normally, newSubtype shouldn't be null. But just in case newSubtype was null,
// fallback to the default locale and mode. // fallback to the default locale.
Log.w(TAG, "Couldn't get the current subtype."); Log.w(TAG, "Couldn't get the current subtype.");
newLocale = "en_US"; newLocale = "en_US";
newMode = KEYBOARD_MODE; newMode = KEYBOARD_MODE;
@ -220,8 +221,8 @@ public class SubtypeSwitcher {
newMode = newSubtype.getMode(); newMode = newSubtype.getMode();
} }
if (DBG) { if (DBG) {
Log.w(TAG, "Update subtype to:" + newLocale + "," + newMode Log.w(TAG, "Update subtype to:" + newLocale + "," + newSubtype.getMode()
+ ", from: " + mInputLocaleStr + ", " + mMode); + ", from: " + mInputLocaleStr + ", " + oldMode);
} }
boolean languageChanged = false; boolean languageChanged = false;
if (!newLocale.equals(mInputLocaleStr)) { if (!newLocale.equals(mInputLocaleStr)) {
@ -231,13 +232,12 @@ public class SubtypeSwitcher {
updateInputLocale(newLocale); updateInputLocale(newLocale);
} }
boolean modeChanged = false; boolean modeChanged = false;
String oldMode = mMode; if (!newMode.equals(oldMode)) {
if (!newMode.equals(mMode)) { if (oldMode != null) {
if (mMode != null) {
modeChanged = true; modeChanged = true;
} }
mMode = newMode;
} }
mCurrentSubtype = newSubtype;
// If the old mode is voice input, we need to reset or cancel its status. // If the old mode is voice input, we need to reset or cancel its status.
// We cancel its status when we change mode, while we reset otherwise. // We cancel its status when we change mode, while we reset otherwise.
@ -262,7 +262,7 @@ public class SubtypeSwitcher {
triggerVoiceIME(); triggerVoiceIME();
} }
} else { } else {
Log.w(TAG, "Unknown subtype mode: " + mMode); Log.w(TAG, "Unknown subtype mode: " + newMode);
if (VOICE_MODE.equals(oldMode) && mVoiceInput != null) { if (VOICE_MODE.equals(oldMode) && mVoiceInput != null) {
// We need to reset the voice input to release the resources and to reset its status // We need to reset the voice input to release the resources and to reset its status
// as it is not the current input mode. // as it is not the current input mode.
@ -483,7 +483,7 @@ public class SubtypeSwitcher {
} }
public boolean isKeyboardMode() { public boolean isKeyboardMode() {
return KEYBOARD_MODE.equals(mMode); return KEYBOARD_MODE.equals(getCurrentSubtypeMode());
} }
@ -506,7 +506,7 @@ public class SubtypeSwitcher {
} }
public boolean isVoiceMode() { public boolean isVoiceMode() {
return VOICE_MODE.equals(mMode); return null == mCurrentSubtype ? false : VOICE_MODE.equals(getCurrentSubtypeMode());
} }
private void triggerVoiceIME() { private void triggerVoiceIME() {
@ -572,6 +572,30 @@ public class SubtypeSwitcher {
} }
} }
/////////////////////////////
// Other utility functions //
/////////////////////////////
public String getCurrentSubtypeExtraValue() {
// If null, return what an empty ExtraValue would return : the empty string.
return null != mCurrentSubtype ? mCurrentSubtype.getExtraValue() : "";
}
public boolean currentSubtypeContainsExtraValueKey(String key) {
// If null, return what an empty ExtraValue would return : false.
return null != mCurrentSubtype ? mCurrentSubtype.containsExtraValueKey(key) : false;
}
public String getCurrentSubtypeExtraValueOf(String key) {
// If null, return what an empty ExtraValue would return : null.
return null != mCurrentSubtype ? mCurrentSubtype.getExtraValueOf(key) : null;
}
public String getCurrentSubtypeMode() {
return null != mCurrentSubtype ? mCurrentSubtype.getMode() : KEYBOARD_MODE;
}
// A list of locales which are supported by default for voice input, unless we get a // A list of locales which are supported by default for voice input, unless we get a
// different list from Gservices. // different list from Gservices.
private static final String DEFAULT_VOICE_INPUT_SUPPORTED_LOCALES = private static final String DEFAULT_VOICE_INPUT_SUPPORTED_LOCALES =

View File

@ -126,7 +126,8 @@ static jint latinime_BinaryDictionary_open(JNIEnv *env, jobject object,
static int latinime_BinaryDictionary_getSuggestions(JNIEnv *env, jobject object, jint dict, static int latinime_BinaryDictionary_getSuggestions(JNIEnv *env, jobject object, jint dict,
jint proximityInfo, jintArray xCoordinatesArray, jintArray yCoordinatesArray, jint proximityInfo, jintArray xCoordinatesArray, jintArray yCoordinatesArray,
jintArray inputArray, jint arraySize, jcharArray outputArray, jintArray frequencyArray) { jintArray inputArray, jint arraySize, jint flags,
jcharArray outputArray, jintArray frequencyArray) {
Dictionary *dictionary = (Dictionary*)dict; Dictionary *dictionary = (Dictionary*)dict;
if (!dictionary) return 0; if (!dictionary) return 0;
ProximityInfo *pInfo = (ProximityInfo*)proximityInfo; ProximityInfo *pInfo = (ProximityInfo*)proximityInfo;
@ -140,7 +141,7 @@ static int latinime_BinaryDictionary_getSuggestions(JNIEnv *env, jobject object,
jchar *outputChars = env->GetCharArrayElements(outputArray, NULL); jchar *outputChars = env->GetCharArrayElements(outputArray, NULL);
int count = dictionary->getSuggestions(pInfo, xCoordinates, yCoordinates, inputCodes, int count = dictionary->getSuggestions(pInfo, xCoordinates, yCoordinates, inputCodes,
arraySize, (unsigned short*) outputChars, frequencies); arraySize, flags, (unsigned short*) outputChars, frequencies);
env->ReleaseIntArrayElements(frequencyArray, frequencies, 0); env->ReleaseIntArrayElements(frequencyArray, frequencies, 0);
env->ReleaseIntArrayElements(inputArray, inputCodes, JNI_ABORT); env->ReleaseIntArrayElements(inputArray, inputCodes, JNI_ABORT);
@ -213,7 +214,7 @@ static void latinime_BinaryDictionary_close(JNIEnv *env, jobject object, jint di
static JNINativeMethod sMethods[] = { static JNINativeMethod sMethods[] = {
{"openNative", "(Ljava/lang/String;JJIIIII)I", (void*)latinime_BinaryDictionary_open}, {"openNative", "(Ljava/lang/String;JJIIIII)I", (void*)latinime_BinaryDictionary_open},
{"closeNative", "(I)V", (void*)latinime_BinaryDictionary_close}, {"closeNative", "(I)V", (void*)latinime_BinaryDictionary_close},
{"getSuggestionsNative", "(II[I[I[II[C[I)I", (void*)latinime_BinaryDictionary_getSuggestions}, {"getSuggestionsNative", "(II[I[I[III[C[I)I", (void*)latinime_BinaryDictionary_getSuggestions},
{"isValidWordNative", "(I[CI)Z", (void*)latinime_BinaryDictionary_isValidWord}, {"isValidWordNative", "(I[CI)Z", (void*)latinime_BinaryDictionary_isValidWord},
{"getBigramsNative", "(I[CI[II[C[IIII)I", (void*)latinime_BinaryDictionary_getBigrams} {"getBigramsNative", "(I[CI[II[C[IIII)I", (void*)latinime_BinaryDictionary_getBigrams}
}; };

View File

@ -55,4 +55,15 @@ static inline void LOGI_S16_PLUS(unsigned short* string, const unsigned int leng
// usleep(10); // usleep(10);
} }
static inline void printDebug(const char* tag, int* codes, int codesSize, int MAX_PROXIMITY_CHARS) {
unsigned char *buf = (unsigned char*)malloc((1 + codesSize) * sizeof(*buf));
buf[codesSize] = 0;
while (--codesSize >= 0)
buf[codesSize] = (unsigned char)codes[codesSize * MAX_PROXIMITY_CHARS];
LOGI("%s, WORD = %s", tag, buf);
free(buf);
}
#endif // LATINIME_DEBUG_H #endif // LATINIME_DEBUG_H

View File

@ -29,9 +29,9 @@ public:
Dictionary(void *dict, int dictSize, int mmapFd, int dictBufAdjust, int typedLetterMultipler, Dictionary(void *dict, int dictSize, int mmapFd, int dictBufAdjust, int typedLetterMultipler,
int fullWordMultiplier, int maxWordLength, int maxWords, int maxAlternatives); int fullWordMultiplier, int maxWordLength, int maxWords, int maxAlternatives);
int getSuggestions(ProximityInfo *proximityInfo, int *xcoordinates, int *ycoordinates, int getSuggestions(ProximityInfo *proximityInfo, int *xcoordinates, int *ycoordinates,
int *codes, int codesSize, unsigned short *outWords, int *frequencies) { int *codes, int codesSize, int flags, unsigned short *outWords, int *frequencies) {
return mUnigramDictionary->getSuggestions(proximityInfo, xcoordinates, ycoordinates, codes, return mUnigramDictionary->getSuggestions(proximityInfo, xcoordinates, ycoordinates, codes,
codesSize, outWords, frequencies); codesSize, flags, outWords, frequencies);
} }
// TODO: Call mBigramDictionary instead of mUnigramDictionary // TODO: Call mBigramDictionary instead of mUnigramDictionary

View File

@ -29,20 +29,136 @@
namespace latinime { namespace latinime {
const UnigramDictionary::digraph_t UnigramDictionary::GERMAN_UMLAUT_DIGRAPHS[] =
{ { 'a', 'e' },
{ 'o', 'e' },
{ 'u', 'e' } };
UnigramDictionary::UnigramDictionary(const unsigned char *dict, int typedLetterMultiplier, UnigramDictionary::UnigramDictionary(const unsigned char *dict, int typedLetterMultiplier,
int fullWordMultiplier, int maxWordLength, int maxWords, int maxProximityChars, int fullWordMultiplier, int maxWordLength, int maxWords, int maxProximityChars,
const bool isLatestDictVersion) const bool isLatestDictVersion)
: DICT(dict), MAX_WORD_LENGTH(maxWordLength), MAX_WORDS(maxWords), : DICT(dict), MAX_WORD_LENGTH(maxWordLength), MAX_WORDS(maxWords),
MAX_PROXIMITY_CHARS(maxProximityChars), IS_LATEST_DICT_VERSION(isLatestDictVersion), MAX_PROXIMITY_CHARS(maxProximityChars), IS_LATEST_DICT_VERSION(isLatestDictVersion),
TYPED_LETTER_MULTIPLIER(typedLetterMultiplier), FULL_WORD_MULTIPLIER(fullWordMultiplier), TYPED_LETTER_MULTIPLIER(typedLetterMultiplier), FULL_WORD_MULTIPLIER(fullWordMultiplier),
ROOT_POS(isLatestDictVersion ? DICTIONARY_HEADER_SIZE : 0) { ROOT_POS(isLatestDictVersion ? DICTIONARY_HEADER_SIZE : 0),
BYTES_IN_ONE_CHAR(MAX_PROXIMITY_CHARS * sizeof(*mInputCodes)) {
if (DEBUG_DICT) LOGI("UnigramDictionary - constructor"); if (DEBUG_DICT) LOGI("UnigramDictionary - constructor");
} }
UnigramDictionary::~UnigramDictionary() {} UnigramDictionary::~UnigramDictionary() {}
int UnigramDictionary::getSuggestions(ProximityInfo *proximityInfo, int *xcoordinates, static inline unsigned int getCodesBufferSize(const int* codes, const int codesSize,
int *ycoordinates, int *codes, int codesSize, unsigned short *outWords, int *frequencies) { const int MAX_PROXIMITY_CHARS) {
return sizeof(*codes) * MAX_PROXIMITY_CHARS * codesSize;
}
bool UnigramDictionary::isDigraph(const int* codes, const int i, const int codesSize) const {
// There can't be a digraph if we don't have at least 2 characters to examine
if (i + 2 > codesSize) return false;
// Search for the first char of some digraph
int lastDigraphIndex = -1;
const int thisChar = codes[i * MAX_PROXIMITY_CHARS];
for (lastDigraphIndex = sizeof(GERMAN_UMLAUT_DIGRAPHS) / sizeof(GERMAN_UMLAUT_DIGRAPHS[0]) - 1;
lastDigraphIndex >= 0; --lastDigraphIndex) {
if (thisChar == GERMAN_UMLAUT_DIGRAPHS[lastDigraphIndex].first) break;
}
// No match: return early
if (lastDigraphIndex < 0) return false;
// It's an interesting digraph if the second char matches too.
return GERMAN_UMLAUT_DIGRAPHS[lastDigraphIndex].second == codes[(i + 1) * MAX_PROXIMITY_CHARS];
}
// Mostly the same arguments as the non-recursive version, except:
// codes is the original value. It points to the start of the work buffer, and gets passed as is.
// codesSize is the size of the user input (thus, it is the size of codesSrc).
// codesDest is the current point in the work buffer.
// codesSrc is the current point in the user-input, original, content-unmodified buffer.
// codesRemain is the remaining size in codesSrc.
void UnigramDictionary::getWordWithDigraphSuggestionsRec(const ProximityInfo *proximityInfo,
const int *xcoordinates, const int* ycoordinates, const int *codesBuffer,
const int codesBufferSize, const int flags, const int* codesSrc, const int codesRemain,
int* codesDest, unsigned short* outWords, int* frequencies) {
for (int i = 0; i < codesRemain; ++i) {
if (isDigraph(codesSrc, i, codesRemain)) {
// Found a digraph. We will try both spellings. eg. the word is "pruefen"
// Copy the word up to the first char of the digraph, then continue processing
// on the remaining part of the word, skipping the second char of the digraph.
// In our example, copy "pru" and continue running on "fen"
memcpy(codesDest, codesSrc, i * BYTES_IN_ONE_CHAR);
getWordWithDigraphSuggestionsRec(proximityInfo, xcoordinates, ycoordinates, codesBuffer,
codesBufferSize, flags, codesSrc + (i + 1) * MAX_PROXIMITY_CHARS,
codesRemain - i - 1, codesDest + i * MAX_PROXIMITY_CHARS,
outWords, frequencies);
// Copy the second char of the digraph in place, then continue processing on
// the remaining part of the word.
// In our example, after "pru" in the buffer copy the "e", and continue running on "fen"
memcpy(codesDest + i * MAX_PROXIMITY_CHARS, codesSrc + i * MAX_PROXIMITY_CHARS,
BYTES_IN_ONE_CHAR);
getWordWithDigraphSuggestionsRec(proximityInfo, xcoordinates, ycoordinates, codesBuffer,
codesBufferSize, flags, codesSrc + i * MAX_PROXIMITY_CHARS, codesRemain - i,
codesDest + i * MAX_PROXIMITY_CHARS, outWords, frequencies);
return;
}
}
// If we come here, we hit the end of the word: let's check it against the dictionary.
// In our example, we'll come here once for "prufen" and then once for "pruefen".
// If the word contains several digraphs, we'll come it for the product of them.
// eg. if the word is "ueberpruefen" we'll test, in order, against
// "uberprufen", "uberpruefen", "ueberprufen", "ueberpruefen".
const unsigned int remainingBytes = BYTES_IN_ONE_CHAR * codesRemain;
if (0 != remainingBytes)
memcpy(codesDest, codesSrc, remainingBytes);
getWordSuggestions(proximityInfo, xcoordinates, ycoordinates, codesBuffer,
(codesDest - codesBuffer) / MAX_PROXIMITY_CHARS + codesRemain, outWords, frequencies);
}
int UnigramDictionary::getSuggestions(const ProximityInfo *proximityInfo, const int *xcoordinates,
const int *ycoordinates, const int *codes, const int codesSize, const int flags,
unsigned short *outWords, int *frequencies) {
if (REQUIRES_GERMAN_UMLAUT_PROCESSING & flags)
{ // Incrementally tune the word and try all possibilities
int codesBuffer[getCodesBufferSize(codes, codesSize, MAX_PROXIMITY_CHARS)];
getWordWithDigraphSuggestionsRec(proximityInfo, xcoordinates, ycoordinates, codesBuffer,
codesSize, flags, codes, codesSize, codesBuffer, outWords, frequencies);
} else { // Normal processing
getWordSuggestions(proximityInfo, xcoordinates, ycoordinates, codes, codesSize,
outWords, frequencies);
}
PROF_START(6);
// Get the word count
int suggestedWordsCount = 0;
while (suggestedWordsCount < MAX_WORDS && mFrequencies[suggestedWordsCount] > 0) {
suggestedWordsCount++;
}
if (DEBUG_DICT) {
LOGI("Returning %d words", suggestedWordsCount);
LOGI("Next letters: ");
for (int k = 0; k < NEXT_LETTERS_SIZE; k++) {
if (mNextLettersFrequency[k] > 0) {
LOGI("%c = %d,", k, mNextLettersFrequency[k]);
}
}
}
PROF_END(6);
PROF_CLOSE;
return suggestedWordsCount;
}
void UnigramDictionary::getWordSuggestions(const ProximityInfo *proximityInfo,
const int *xcoordinates, const int *ycoordinates, const int *codes, const int codesSize,
unsigned short *outWords, int *frequencies) {
PROF_OPEN; PROF_OPEN;
PROF_START(0); PROF_START(0);
initSuggestions(codes, codesSize, outWords, frequencies); initSuggestions(codes, codesSize, outWords, frequencies);
@ -103,30 +219,10 @@ int UnigramDictionary::getSuggestions(ProximityInfo *proximityInfo, int *xcoordi
} }
} }
PROF_END(5); PROF_END(5);
PROF_START(6);
// Get the word count
int suggestedWordsCount = 0;
while (suggestedWordsCount < MAX_WORDS && mFrequencies[suggestedWordsCount] > 0) {
suggestedWordsCount++;
}
if (DEBUG_DICT) {
LOGI("Returning %d words", suggestedWordsCount);
LOGI("Next letters: ");
for (int k = 0; k < NEXT_LETTERS_SIZE; k++) {
if (mNextLettersFrequency[k] > 0) {
LOGI("%c = %d,", k, mNextLettersFrequency[k]);
}
}
}
PROF_END(6);
PROF_CLOSE;
return suggestedWordsCount;
} }
void UnigramDictionary::initSuggestions(int *codes, int codesSize, unsigned short *outWords, void UnigramDictionary::initSuggestions(const int *codes, const int codesSize,
int *frequencies) { unsigned short *outWords, int *frequencies) {
if (DEBUG_DICT) LOGI("initSuggest"); if (DEBUG_DICT) LOGI("initSuggest");
mFrequencies = frequencies; mFrequencies = frequencies;
mOutputChars = outWords; mOutputChars = outWords;
@ -204,7 +300,7 @@ bool UnigramDictionary::sameAsTyped(unsigned short *word, int length) {
if (length != mInputLength) { if (length != mInputLength) {
return false; return false;
} }
int *inputCodes = mInputCodes; const int *inputCodes = mInputCodes;
while (length--) { while (length--) {
if ((unsigned int) *inputCodes != (unsigned int) *word) { if ((unsigned int) *inputCodes != (unsigned int) *word) {
return false; return false;
@ -423,7 +519,7 @@ inline bool UnigramDictionary::existsAdjacentProximityChars(const int inputIndex
const int currentChar = *getInputCharsAt(inputIndex); const int currentChar = *getInputCharsAt(inputIndex);
const int leftIndex = inputIndex - 1; const int leftIndex = inputIndex - 1;
if (leftIndex >= 0) { if (leftIndex >= 0) {
int *leftChars = getInputCharsAt(leftIndex); const int *leftChars = getInputCharsAt(leftIndex);
int i = 0; int i = 0;
while (leftChars[i] > 0 && i < MAX_PROXIMITY_CHARS) { while (leftChars[i] > 0 && i < MAX_PROXIMITY_CHARS) {
if (leftChars[i++] == currentChar) return true; if (leftChars[i++] == currentChar) return true;
@ -431,7 +527,7 @@ inline bool UnigramDictionary::existsAdjacentProximityChars(const int inputIndex
} }
const int rightIndex = inputIndex + 1; const int rightIndex = inputIndex + 1;
if (rightIndex < inputLength) { if (rightIndex < inputLength) {
int *rightChars = getInputCharsAt(rightIndex); const int *rightChars = getInputCharsAt(rightIndex);
int i = 0; int i = 0;
while (rightChars[i] > 0 && i < MAX_PROXIMITY_CHARS) { while (rightChars[i] > 0 && i < MAX_PROXIMITY_CHARS) {
if (rightChars[i++] == currentChar) return true; if (rightChars[i++] == currentChar) return true;
@ -523,7 +619,7 @@ inline bool UnigramDictionary::processCurrentNode(const int pos, const int depth
*newDiffs = diffs; *newDiffs = diffs;
*newInputIndex = inputIndex; *newInputIndex = inputIndex;
} else { } else {
int *currentChars = getInputCharsAt(inputIndex); const int *currentChars = getInputCharsAt(inputIndex);
if (transposedPos >= 0) { if (transposedPos >= 0) {
if (inputIndex == transposedPos) currentChars += MAX_PROXIMITY_CHARS; if (inputIndex == transposedPos) currentChars += MAX_PROXIMITY_CHARS;

View File

@ -33,12 +33,22 @@ class UnigramDictionary {
public: public:
UnigramDictionary(const unsigned char *dict, int typedLetterMultipler, int fullWordMultiplier, UnigramDictionary(const unsigned char *dict, int typedLetterMultipler, int fullWordMultiplier,
int maxWordLength, int maxWords, int maxProximityChars, const bool isLatestDictVersion); int maxWordLength, int maxWords, int maxProximityChars, const bool isLatestDictVersion);
int getSuggestions(ProximityInfo *proximityInfo, int *xcoordinates, int *ycoordinates, int getSuggestions(const ProximityInfo *proximityInfo, const int *xcoordinates,
int *codes, int codesSize, unsigned short *outWords, int *frequencies); const int *ycoordinates, const int *codes, const int codesSize, const int flags,
unsigned short *outWords, int *frequencies);
~UnigramDictionary(); ~UnigramDictionary();
private: private:
void initSuggestions(int *codes, int codesSize, unsigned short *outWords, int *frequencies); void getWordSuggestions(const ProximityInfo *proximityInfo, const int *xcoordinates,
const int *ycoordinates, const int *codes, const int codesSize,
unsigned short *outWords, int *frequencies);
bool isDigraph(const int* codes, const int i, const int codesSize) const;
void getWordWithDigraphSuggestionsRec(const ProximityInfo *proximityInfo,
const int *xcoordinates, const int* ycoordinates, const int *codesBuffer,
const int codesBufferSize, const int flags, const int* codesSrc, const int codesRemain,
int* codesDest, unsigned short* outWords, int* frequencies);
void initSuggestions(const int *codes, const int codesSize, unsigned short *outWords,
int *frequencies);
void getSuggestionCandidates(const int skipPos, const int excessivePos, void getSuggestionCandidates(const int skipPos, const int excessivePos,
const int transposedPos, int *nextLetters, const int nextLettersSize, const int transposedPos, int *nextLetters, const int nextLettersSize,
const int maxDepth); const int maxDepth);
@ -86,7 +96,7 @@ private:
const int startInputIndex, const int depth, unsigned short *word, const int startInputIndex, const int depth, unsigned short *word,
int *newChildPosition, int *newCount, bool *newTerminal, int *newFreq, int *siblingPos); int *newChildPosition, int *newCount, bool *newTerminal, int *newFreq, int *siblingPos);
bool existsAdjacentProximityChars(const int inputIndex, const int inputLength); bool existsAdjacentProximityChars(const int inputIndex, const int inputLength);
inline int* getInputCharsAt(const int index) { inline const int* getInputCharsAt(const int index) {
return mInputCodes + (index * MAX_PROXIMITY_CHARS); return mInputCodes + (index * MAX_PROXIMITY_CHARS);
} }
const unsigned char *DICT; const unsigned char *DICT;
@ -97,10 +107,20 @@ private:
const int TYPED_LETTER_MULTIPLIER; const int TYPED_LETTER_MULTIPLIER;
const int FULL_WORD_MULTIPLIER; const int FULL_WORD_MULTIPLIER;
const int ROOT_POS; const int ROOT_POS;
const unsigned int BYTES_IN_ONE_CHAR;
// Flags for special processing
// Those *must* match the flags in BinaryDictionary.Flags.ALL_FLAGS in BinaryDictionary.java
// or something very bad (like, the apocalypse) will happen.
// Please update both at the same time.
enum {
REQUIRES_GERMAN_UMLAUT_PROCESSING = 0x1
};
static const struct digraph_t { int first; int second; } GERMAN_UMLAUT_DIGRAPHS[];
int *mFrequencies; int *mFrequencies;
unsigned short *mOutputChars; unsigned short *mOutputChars;
int *mInputCodes; const int *mInputCodes;
int mInputLength; int mInputLength;
// MAX_WORD_LENGTH_INTERNAL must be bigger than MAX_WORD_LENGTH // MAX_WORD_LENGTH_INTERNAL must be bigger than MAX_WORD_LENGTH
unsigned short mWord[MAX_WORD_LENGTH_INTERNAL]; unsigned short mWord[MAX_WORD_LENGTH_INTERNAL];