Implement the common Dictionary interface (A89)

This will de-duplicate a lot of existing code.

Change-Id: Idaffb2fde23b9741f057bcb2ecb3dde9d12ea5c5
diff --git a/java/src/com/android/inputmethod/latin/BinaryDictionary.java b/java/src/com/android/inputmethod/latin/BinaryDictionary.java
index 15646b8..03e2862 100644
--- a/java/src/com/android/inputmethod/latin/BinaryDictionary.java
+++ b/java/src/com/android/inputmethod/latin/BinaryDictionary.java
@@ -106,6 +106,17 @@
     }
 
     @Override
+    public ArrayList<SuggestedWordInfo> getSuggestions(final WordComposer composer,
+            final CharSequence prevWord, final ProximityInfo proximityInfo) {
+        if (composer.size() <= 1) {
+            return TextUtils.isEmpty(prevWord) ? null : getBigrams(composer, prevWord);
+        } else {
+            return getWords(composer, prevWord, proximityInfo);
+        }
+    }
+
+    // TODO: rename this to getBigramsInternal, then move to native code
+    @Override
     protected ArrayList<SuggestedWordInfo> getBigrams(final WordComposer codes,
             final CharSequence previousWord) {
         if (mNativeDict == 0) return null;
@@ -143,6 +154,7 @@
         return suggestions;
     }
 
+    // TODO: rename this to getWordsInternal, then move to native code
     // proximityInfo and/or prevWordForBigrams may not be null.
     @Override
     protected ArrayList<SuggestedWordInfo> getWords(final WordComposer codes,
diff --git a/java/src/com/android/inputmethod/latin/Dictionary.java b/java/src/com/android/inputmethod/latin/Dictionary.java
index 4b02e11..38ef00d 100644
--- a/java/src/com/android/inputmethod/latin/Dictionary.java
+++ b/java/src/com/android/inputmethod/latin/Dictionary.java
@@ -61,14 +61,8 @@
      */
     // TODO: pass more context than just the previous word, to enable better suggestions (n-gram
     // and more)
-    public ArrayList<SuggestedWordInfo> getSuggestions(final WordComposer composer,
-            final CharSequence prevWord, final ProximityInfo proximityInfo) {
-        if (composer.size() <= 1) {
-            return TextUtils.isEmpty(prevWord) ? null : getBigrams(composer, prevWord);
-        } else {
-            return getWords(composer, prevWord, proximityInfo);
-        }
-    }
+    abstract public ArrayList<SuggestedWordInfo> getSuggestions(final WordComposer composer,
+            final CharSequence prevWord, final ProximityInfo proximityInfo);
 
     /**
      * Searches for words in the dictionary that match the characters in the composer. Matched
@@ -78,6 +72,7 @@
      * @param proximityInfo the object for key proximity. May be ignored by some implementations.
      * @return the list of suggestions
      */
+    // TODO: remove this
     abstract protected ArrayList<SuggestedWordInfo> getWords(final WordComposer composer,
             final CharSequence prevWordForBigrams, final ProximityInfo proximityInfo);
 
@@ -87,6 +82,7 @@
      * @param previousWord the word before
      * @return the list of suggestions
      */
+    // TODO: remove this
     abstract protected ArrayList<SuggestedWordInfo> getBigrams(final WordComposer composer,
             final CharSequence previousWord);
 
diff --git a/java/src/com/android/inputmethod/latin/DictionaryCollection.java b/java/src/com/android/inputmethod/latin/DictionaryCollection.java
index d2ddebf..7c589ef 100644
--- a/java/src/com/android/inputmethod/latin/DictionaryCollection.java
+++ b/java/src/com/android/inputmethod/latin/DictionaryCollection.java
@@ -55,6 +55,26 @@
     }
 
     @Override
+    public ArrayList<SuggestedWordInfo> getSuggestions(final WordComposer composer,
+            final CharSequence prevWord, final ProximityInfo proximityInfo) {
+        final CopyOnWriteArrayList<Dictionary> dictionaries = mDictionaries;
+        if (dictionaries.isEmpty()) return null;
+        // To avoid creating unnecessary objects, we get the list out of the first
+        // dictionary and add the rest to it if not null, hence the get(0)
+        ArrayList<SuggestedWordInfo> suggestions = dictionaries.get(0).getSuggestions(composer,
+                prevWord, proximityInfo);
+        if (null == suggestions) suggestions = new ArrayList<SuggestedWordInfo>();
+        final int length = dictionaries.size();
+        for (int i = 0; i < length; ++ i) {
+            final ArrayList<SuggestedWordInfo> sugg = dictionaries.get(i).getSuggestions(composer,
+                    prevWord, proximityInfo);
+            if (null != sugg) suggestions.addAll(sugg);
+        }
+        return suggestions;
+    }
+
+    // TODO: remove this
+    @Override
     protected ArrayList<SuggestedWordInfo> getWords(final WordComposer composer,
             final CharSequence prevWordForBigrams, final ProximityInfo proximityInfo) {
         final CopyOnWriteArrayList<Dictionary> dictionaries = mDictionaries;
@@ -73,6 +93,7 @@
         return suggestions;
     }
 
+    // TODO: remove this
     @Override
     protected ArrayList<SuggestedWordInfo> getBigrams(final WordComposer composer,
             final CharSequence previousWord) {
diff --git a/java/src/com/android/inputmethod/latin/ExpandableBinaryDictionary.java b/java/src/com/android/inputmethod/latin/ExpandableBinaryDictionary.java
index 40d46a8..dd949f1 100644
--- a/java/src/com/android/inputmethod/latin/ExpandableBinaryDictionary.java
+++ b/java/src/com/android/inputmethod/latin/ExpandableBinaryDictionary.java
@@ -16,6 +16,7 @@
 
 import android.content.Context;
 import android.os.SystemClock;
+import android.text.TextUtils;
 import android.util.Log;
 
 import com.android.inputmethod.keyboard.ProximityInfo;
@@ -192,6 +193,23 @@
     }
 
     @Override
+    public ArrayList<SuggestedWordInfo> getSuggestions(final WordComposer composer,
+            final CharSequence prevWord, final ProximityInfo proximityInfo) {
+        asyncReloadDictionaryIfRequired();
+        if (mLocalDictionaryController.tryLock()) {
+            try {
+                if (mBinaryDictionary != null) {
+                    return mBinaryDictionary.getSuggestions(composer, prevWord, proximityInfo);
+                }
+            } finally {
+                mLocalDictionaryController.unlock();
+            }
+        }
+        return null;
+    }
+
+    // TODO: remove this
+    @Override
     protected ArrayList<SuggestedWordInfo> getWords(final WordComposer codes,
             final CharSequence prevWordForBigrams, final ProximityInfo proximityInfo) {
         asyncReloadDictionaryIfRequired();
@@ -214,6 +232,7 @@
         return null;
     }
 
+    // TODO: remove this
     @Override
     protected ArrayList<SuggestedWordInfo> getBigrams(final WordComposer codes,
             final CharSequence previousWord) {
diff --git a/java/src/com/android/inputmethod/latin/ExpandableDictionary.java b/java/src/com/android/inputmethod/latin/ExpandableDictionary.java
index a3c183c..6cd4f65 100644
--- a/java/src/com/android/inputmethod/latin/ExpandableDictionary.java
+++ b/java/src/com/android/inputmethod/latin/ExpandableDictionary.java
@@ -17,6 +17,7 @@
 package com.android.inputmethod.latin;
 
 import android.content.Context;
+import android.text.TextUtils;
 
 import com.android.inputmethod.keyboard.KeyDetector;
 import com.android.inputmethod.keyboard.Keyboard;
@@ -247,6 +248,26 @@
     }
 
     @Override
+    public ArrayList<SuggestedWordInfo> getSuggestions(final WordComposer composer,
+            final CharSequence prevWord, final ProximityInfo proximityInfo) {
+        if (reloadDictionaryIfRequired()) return null;
+        if (composer.size() <= 1) {
+            if (composer.size() >= BinaryDictionary.MAX_WORD_LENGTH) {
+                return null;
+            }
+            final ArrayList<SuggestedWordInfo> suggestions =
+                    getWordsInner(composer, prevWord, proximityInfo);
+            return suggestions;
+        } else {
+            if (TextUtils.isEmpty(prevWord)) return null;
+            final ArrayList<SuggestedWordInfo> suggestions = new ArrayList<SuggestedWordInfo>();
+            runBigramReverseLookUp(prevWord, suggestions);
+            return suggestions;
+        }
+    }
+
+    // TODO: remove this
+    @Override
     protected ArrayList<SuggestedWordInfo> getWords(final WordComposer codes,
             final CharSequence prevWordForBigrams, final ProximityInfo proximityInfo) {
         if (reloadDictionaryIfRequired()) return null;
@@ -269,6 +290,7 @@
         }
     }
 
+    // TODO: remove this
     @Override
     protected ArrayList<SuggestedWordInfo> getBigrams(final WordComposer codes,
             final CharSequence previousWord) {