Merge "Use XmlPullParser interface instead of XmlResourceParser"
diff --git a/java/src/com/android/inputmethod/latin/Suggest.java b/java/src/com/android/inputmethod/latin/Suggest.java
index 208fd13..f6f5581 100644
--- a/java/src/com/android/inputmethod/latin/Suggest.java
+++ b/java/src/com/android/inputmethod/latin/Suggest.java
@@ -113,14 +113,15 @@
         initAsynchronously(context, dictionaryResId, locale);
     }
 
-    /* package for test */ Suggest(Context context, File dictionary, long startOffset, long length,
-            Flag[] flagArray) {
+    /* package for test */ Suggest(final Context context, final File dictionary,
+            final long startOffset, final long length, final Flag[] flagArray,
+            final Locale locale) {
         initSynchronously(null, DictionaryFactory.createDictionaryForTest(context, dictionary,
-                startOffset, length, flagArray));
+                startOffset, length, flagArray), locale);
     }
 
-    private void initWhitelistAndAutocorrectAndPool(final Context context) {
-        mWhiteListDictionary = WhitelistDictionary.init(context);
+    private void initWhitelistAndAutocorrectAndPool(final Context context, final Locale locale) {
+        mWhiteListDictionary = new WhitelistDictionary(context, locale);
         addOrReplaceDictionary(mUnigramDictionaries, DICT_KEY_WHITELIST, mWhiteListDictionary);
         mAutoCorrection = new AutoCorrection();
         StringBuilderPool.ensureCapacity(mPrefMaxSuggestions, getApproxMaxWordLength());
@@ -132,14 +133,15 @@
 
         // TODO: read the whitelist and init the pool asynchronously too.
         // initPool should be done asynchronously now that the pool is thread-safe.
-        initWhitelistAndAutocorrectAndPool(context);
+        initWhitelistAndAutocorrectAndPool(context, locale);
     }
 
-    private void initSynchronously(Context context, Dictionary mainDict) {
+    private void initSynchronously(final Context context, final Dictionary mainDict,
+            final Locale locale) {
         mMainDict = mainDict;
         addOrReplaceDictionary(mUnigramDictionaries, DICT_KEY_MAIN, mainDict);
         addOrReplaceDictionary(mBigramDictionaries, DICT_KEY_MAIN, mainDict);
-        initWhitelistAndAutocorrectAndPool(context);
+        initWhitelistAndAutocorrectAndPool(context, locale);
     }
 
     private void addOrReplaceDictionary(Map<String, Dictionary> dictionaries, String key,
diff --git a/java/src/com/android/inputmethod/latin/WhitelistDictionary.java b/java/src/com/android/inputmethod/latin/WhitelistDictionary.java
index 639c966..93474b6 100644
--- a/java/src/com/android/inputmethod/latin/WhitelistDictionary.java
+++ b/java/src/com/android/inputmethod/latin/WhitelistDictionary.java
@@ -17,6 +17,7 @@
 package com.android.inputmethod.latin;
 
 import android.content.Context;
+import android.content.res.Resources;
 import android.text.TextUtils;
 import android.util.Log;
 import android.util.Pair;
@@ -24,6 +25,7 @@
 import com.android.inputmethod.keyboard.ProximityInfo;
 
 import java.util.HashMap;
+import java.util.Locale;
 
 public class WhitelistDictionary extends Dictionary {
 
@@ -33,22 +35,13 @@
     private final HashMap<String, Pair<Integer, String>> mWhitelistWords =
             new HashMap<String, Pair<Integer, String>>();
 
-    private static final WhitelistDictionary sInstance = new WhitelistDictionary();
-
-    private WhitelistDictionary() {
-    }
-
-    public static WhitelistDictionary init(Context context) {
-        synchronized (sInstance) {
-            if (context != null) {
-                // Wordlist is initialized by the proper language in Suggestion.java#init
-                sInstance.initWordlist(
-                        context.getResources().getStringArray(R.array.wordlist_whitelist));
-            } else {
-                sInstance.mWhitelistWords.clear();
-            }
+    public WhitelistDictionary(final Context context, final Locale locale) {
+        final Resources res = context.getResources();
+        final Locale previousLocale = LocaleUtils.setSystemLocale(res, locale);
+        if (context != null) {
+            initWordlist(context.getResources().getStringArray(R.array.wordlist_whitelist));
         }
-        return sInstance;
+        LocaleUtils.setSystemLocale(res, previousLocale);
     }
 
     private void initWordlist(String[] wordlist) {
diff --git a/java/src/com/android/inputmethod/latin/spellcheck/AndroidSpellCheckerService.java b/java/src/com/android/inputmethod/latin/spellcheck/AndroidSpellCheckerService.java
index b197c5b..4d569b8 100644
--- a/java/src/com/android/inputmethod/latin/spellcheck/AndroidSpellCheckerService.java
+++ b/java/src/com/android/inputmethod/latin/spellcheck/AndroidSpellCheckerService.java
@@ -156,6 +156,11 @@
                 // }
                 return true;
             }
+            if (insertIndex >= mMaxLength) {
+                // We found a suggestion, but its score is too weak to be kept considering
+                // the suggestion limit.
+                return true;
+            }
 
             // Compute the normalized score and skip this word if it's normalized score does not
             // make the threshold.
@@ -438,8 +443,10 @@
                     Log.i(TAG, "IsInDict = " + isInDict);
                     Log.i(TAG, "LooksLikeTypo = " + (!isInDict));
                     Log.i(TAG, "HasLikelySuggestions = " + result.mHasLikelySuggestions);
-                    for (String suggestion : result.mSuggestions) {
-                        Log.i(TAG, suggestion);
+                    if (null != result.mSuggestions) {
+                        for (String suggestion : result.mSuggestions) {
+                            Log.i(TAG, suggestion);
+                        }
                     }
                 }
 
diff --git a/native/src/correction.cpp b/native/src/correction.cpp
index d1e866d..a08a21b 100644
--- a/native/src/correction.cpp
+++ b/native/src/correction.cpp
@@ -299,23 +299,41 @@
 
     // TODO: Change the limit if we'll allow two or more proximity chars with corrections
     const bool checkProximityChars = noCorrectionsHappenedSoFar ||  mProximityCount == 0;
-    const ProximityInfo::ProximityType matchedProximityCharId = secondTransposing
+    ProximityInfo::ProximityType matchedProximityCharId = secondTransposing
             ? ProximityInfo::EQUIVALENT_CHAR
             : mProximityInfo->getMatchedProximityId(mInputIndex, c, checkProximityChars);
 
     if (ProximityInfo::UNRELATED_CHAR == matchedProximityCharId) {
+        if (canTryCorrection && mOutputIndex > 0
+                && mCorrectionStates[mOutputIndex].mProximityMatching
+                && mCorrectionStates[mOutputIndex].mExceeding
+                && isEquivalentChar(mProximityInfo->getMatchedProximityId(
+                        mInputIndex, mWord[mOutputIndex - 1], false))) {
+            if (DEBUG_CORRECTION) {
+                LOGI("CONVERSION p->e %c", mWord[mOutputIndex - 1]);
+            }
+            // Conversion p->e
+            // Example:
+            // wearth ->    earth
+            // px     -> (E)mmmmm
+            ++mExcessiveCount;
+            --mProximityCount;
+            mExcessivePos = mOutputIndex - 1;
+            ++mInputIndex;
+            // Here, we are doing something equivalent to matchedProximityCharId,
+            // but we already know that "excessive char correction" just happened
+            // so that we just need to check "mProximityCount == 0".
+            matchedProximityCharId =
+                    mProximityInfo->getMatchedProximityId(mInputIndex, c, mProximityCount == 0);
+        }
+    }
+
+    if (ProximityInfo::UNRELATED_CHAR == matchedProximityCharId) {
         // TODO: Optimize
         // As the current char turned out to be an unrelated char,
         // we will try other correction-types. Please note that mCorrectionStates[mOutputIndex]
         // here refers to the previous state.
-        if (canTryCorrection && mCorrectionStates[mOutputIndex].mProximityMatching
-                && mCorrectionStates[mOutputIndex].mExceeding
-                && isEquivalentChar(mProximityInfo->getMatchedProximityId(
-                        mInputIndex, mWord[mOutputIndex], false))) {
-            // Conversion p->e
-            ++mExcessiveCount;
-            --mProximityCount;
-        } else if (mInputIndex < mInputLength - 1 && mOutputIndex > 0 && mTransposedCount > 0
+        if (mInputIndex < mInputLength - 1 && mOutputIndex > 0 && mTransposedCount > 0
                 && !mCorrectionStates[mOutputIndex].mTransposing
                 && mCorrectionStates[mOutputIndex - 1].mTransposing
                 && isEquivalentChar(mProximityInfo->getMatchedProximityId(
@@ -425,6 +443,11 @@
             || isSameAsUserTypedLength) && isTerminal) {
         mTerminalInputIndex = mInputIndex - 1;
         mTerminalOutputIndex = mOutputIndex - 1;
+        if (DEBUG_CORRECTION) {
+            DUMP_WORD(mWord, mOutputIndex);
+            LOGI("ONTERMINAL(1): %d, %d, %d, %d, %c", mProximityCount, mSkippedCount,
+                    mTransposedCount, mExcessiveCount, c);
+        }
         return ON_TERMINAL;
     } else {
         return NOT_ON_TERMINAL;
diff --git a/tests/src/com/android/inputmethod/latin/SuggestHelper.java b/tests/src/com/android/inputmethod/latin/SuggestHelper.java
index 43b8cf6..a089335 100644
--- a/tests/src/com/android/inputmethod/latin/SuggestHelper.java
+++ b/tests/src/com/android/inputmethod/latin/SuggestHelper.java
@@ -25,6 +25,7 @@
 import com.android.inputmethod.keyboard.LatinKeyboard;
 
 import java.io.File;
+import java.util.Locale;
 
 public class SuggestHelper {
     protected final Suggest mSuggest;
@@ -40,9 +41,10 @@
         init();
     }
 
-    protected SuggestHelper(Context context, File dictionaryPath, long startOffset, long length,
-            KeyboardId keyboardId) {
-        mSuggest = new Suggest(context, dictionaryPath, startOffset, length, null);
+    protected SuggestHelper(final Context context, final File dictionaryPath,
+            final long startOffset, final long length, final KeyboardId keyboardId,
+            final Locale locale) {
+        mSuggest = new Suggest(context, dictionaryPath, startOffset, length, null, locale);
         mKeyboard = new LatinKeyboard.Builder(context).load(keyboardId).build();
         mKeyDetector = new KeyDetector(0);
         init();
diff --git a/tests/src/com/android/inputmethod/latin/SuggestTests.java b/tests/src/com/android/inputmethod/latin/SuggestTests.java
index cf45580..4080f34 100644
--- a/tests/src/com/android/inputmethod/latin/SuggestTests.java
+++ b/tests/src/com/android/inputmethod/latin/SuggestTests.java
@@ -30,9 +30,10 @@
     protected void setUp() throws Exception {
         super.setUp();
         final AssetFileDescriptor dict = openTestRawResourceFd(R.raw.test);
+        final Locale locale = Locale.US;
         mHelper = new SuggestHelper(
                 getContext(), mTestPackageFile, dict.getStartOffset(), dict.getLength(),
-                createKeyboardId(Locale.US, Configuration.ORIENTATION_PORTRAIT));
+                createKeyboardId(locale, Configuration.ORIENTATION_PORTRAIT), locale);
         mHelper.setCorrectionMode(Suggest.CORRECTION_FULL_BIGRAM);
     }
 
diff --git a/tests/src/com/android/inputmethod/latin/UserBigramSuggestHelper.java b/tests/src/com/android/inputmethod/latin/UserBigramSuggestHelper.java
index 7efd5d0..023e20a 100644
--- a/tests/src/com/android/inputmethod/latin/UserBigramSuggestHelper.java
+++ b/tests/src/com/android/inputmethod/latin/UserBigramSuggestHelper.java
@@ -29,11 +29,12 @@
     private final Context mContext;
     private UserBigramDictionary mUserBigram;
 
-    public UserBigramSuggestHelper(Context context, File dictionaryPath, long startOffset,
-            long length, int userBigramMax, int userBigramDelete, KeyboardId keyboardId) {
-        super(context, dictionaryPath, startOffset, length, keyboardId);
+    public UserBigramSuggestHelper(final Context context, final File dictionaryPath,
+            final long startOffset, final long length, final int userBigramMax,
+            final int userBigramDelete, final KeyboardId keyboardId, final Locale locale) {
+        super(context, dictionaryPath, startOffset, length, keyboardId, locale);
         mContext = context;
-        mUserBigram = new UserBigramDictionary(context, null, Locale.US.toString(),
+        mUserBigram = new UserBigramDictionary(context, null, locale.toString(),
                 Suggest.DIC_USER);
         mUserBigram.setDatabaseMax(userBigramMax);
         mUserBigram.setDatabaseDelete(userBigramDelete);
diff --git a/tests/src/com/android/inputmethod/latin/UserBigramSuggestTests.java b/tests/src/com/android/inputmethod/latin/UserBigramSuggestTests.java
index 0b97e41..2bc0aab 100644
--- a/tests/src/com/android/inputmethod/latin/UserBigramSuggestTests.java
+++ b/tests/src/com/android/inputmethod/latin/UserBigramSuggestTests.java
@@ -33,10 +33,11 @@
     protected void setUp() throws Exception {
         super.setUp();
         final AssetFileDescriptor dict = openTestRawResourceFd(R.raw.test);
+        final Locale locale = Locale.US;
         mHelper = new UserBigramSuggestHelper(
                 getContext(), mTestPackageFile, dict.getStartOffset(), dict.getLength(),
                 MAX_DATA, DELETE_DATA,
-                createKeyboardId(Locale.US, Configuration.ORIENTATION_PORTRAIT));
+                createKeyboardId(locale, Configuration.ORIENTATION_PORTRAIT), locale);
     }
 
     /************************** Tests ************************/