Introduce EMPTY_PREV_WORDS_INFO and BEGINNING_OF_SENTENCE.

Bug: 14119293
Change-Id: I5020e5f0aa64bc3e97b3a3c2c07a60c8b765ed64
diff --git a/java/src/com/android/inputmethod/latin/ContactsBinaryDictionary.java b/java/src/com/android/inputmethod/latin/ContactsBinaryDictionary.java
index 3fb76b1..538bfc0 100644
--- a/java/src/com/android/inputmethod/latin/ContactsBinaryDictionary.java
+++ b/java/src/com/android/inputmethod/latin/ContactsBinaryDictionary.java
@@ -224,7 +224,7 @@
      */
     private void addNameLocked(final String name) {
         int len = StringUtils.codePointCount(name);
-        PrevWordsInfo prevWordsInfo = new PrevWordsInfo(null);
+        PrevWordsInfo prevWordsInfo = PrevWordsInfo.EMPTY_PREV_WORDS_INFO;
         // TODO: Better tokenization for non-Latin writing systems
         for (int i = 0; i < len; i++) {
             if (Character.isLetter(name.codePointAt(i))) {
diff --git a/java/src/com/android/inputmethod/latin/PrevWordsInfo.java b/java/src/com/android/inputmethod/latin/PrevWordsInfo.java
index 3494d16..e44239f 100644
--- a/java/src/com/android/inputmethod/latin/PrevWordsInfo.java
+++ b/java/src/com/android/inputmethod/latin/PrevWordsInfo.java
@@ -16,20 +16,27 @@
 
 package com.android.inputmethod.latin;
 
-import android.util.Log;
-
+/**
+ * Class to represent information of previous words. This class is used to add n-gram entries
+ * into binary dictionaries, to get predictions, and to get suggestions.
+ */
 // TODO: Support multiple previous words for n-gram.
 public class PrevWordsInfo {
+    public static final PrevWordsInfo EMPTY_PREV_WORDS_INFO = new PrevWordsInfo(null);
     public static final PrevWordsInfo BEGINNING_OF_SENTENCE = new PrevWordsInfo();
 
-    // The previous word. May be null after resetting and before starting a new composing word, or
-    // when there is no context like at the start of text for example. It can also be set to null
-    // externally when the user enters a separator that does not let bigrams across, like a period
-    // or a comma.
+    // The word immediately before the considered word. null means we don't have any context
+    // including the "beginning of sentence context" - we just don't know what to predict.
+    // An example of that is after a comma.
+    // For simplicity of implementation, this may also be null transiently after the WordComposer
+    // was reset and before starting a new composing word, but we should never be calling
+    // getSuggetions* in this situation.
+    // This is an empty string when mIsBeginningOfSentence is true.
     public final String mPrevWord;
 
     // TODO: Have sentence separator.
-    // Whether the current context is beginning of sentence or not.
+    // Whether the current context is beginning of sentence or not. This is true when composing at
+    // the beginning of an input field or composing a word after a sentence separator.
     public final boolean mIsBeginningOfSentence;
 
     // Beginning of sentence.
diff --git a/java/src/com/android/inputmethod/latin/RichInputConnection.java b/java/src/com/android/inputmethod/latin/RichInputConnection.java
index 2c54e10..e7c1636 100644
--- a/java/src/com/android/inputmethod/latin/RichInputConnection.java
+++ b/java/src/com/android/inputmethod/latin/RichInputConnection.java
@@ -542,7 +542,7 @@
             final SpacingAndPunctuations spacingAndPunctuations, final int n) {
         mIC = mParent.getCurrentInputConnection();
         if (null == mIC) {
-            return new PrevWordsInfo(null);
+            return PrevWordsInfo.EMPTY_PREV_WORDS_INFO;
         }
         final CharSequence prev = getTextBeforeCursor(LOOKBACK_CHARACTER_NUM, 0);
         if (DEBUG_PREVIOUS_TEXT && null != prev) {
@@ -588,30 +588,30 @@
     // (n = 2) "abc. def|" -> beginning-of-sentence
     public static PrevWordsInfo getPrevWordsInfoFromNthPreviousWord(final CharSequence prev,
             final SpacingAndPunctuations spacingAndPunctuations, final int n) {
-        if (prev == null) return new PrevWordsInfo(null);
+        if (prev == null) return PrevWordsInfo.EMPTY_PREV_WORDS_INFO;
         final String[] w = spaceRegex.split(prev);
 
         // If we can't find n words, or we found an empty word, the context is
         // beginning-of-sentence.
         if (w.length < n) {
-            return new PrevWordsInfo();
+            return PrevWordsInfo.BEGINNING_OF_SENTENCE;
         }
         final String nthPrevWord = w[w.length - n];
         final int length = nthPrevWord.length();
         if (length <= 0) {
-            return  new PrevWordsInfo();
+            return PrevWordsInfo.BEGINNING_OF_SENTENCE;
         }
 
         // If ends in a sentence separator, the context is beginning-of-sentence.
         final char lastChar = nthPrevWord.charAt(length - 1);
         if (spacingAndPunctuations.isSentenceSeparator(lastChar)) {
-            new PrevWordsInfo();
+            return PrevWordsInfo.BEGINNING_OF_SENTENCE;
         }
         // If ends in a word separator or connector, the context is unclear.
         // TODO: Return meaningful context for this case.
         if (spacingAndPunctuations.isWordSeparator(lastChar)
                 || spacingAndPunctuations.isWordConnector(lastChar)) {
-            return new PrevWordsInfo(null);
+            return PrevWordsInfo.EMPTY_PREV_WORDS_INFO;
         }
         return new PrevWordsInfo(nthPrevWord);
     }
diff --git a/java/src/com/android/inputmethod/latin/WordComposer.java b/java/src/com/android/inputmethod/latin/WordComposer.java
index 6ecb373..c53a8fd 100644
--- a/java/src/com/android/inputmethod/latin/WordComposer.java
+++ b/java/src/com/android/inputmethod/latin/WordComposer.java
@@ -85,7 +85,7 @@
         mIsBatchMode = false;
         mCursorPositionWithinWord = 0;
         mRejectedBatchModeSuggestion = null;
-        mPrevWordsInfo = new PrevWordsInfo(null);
+        mPrevWordsInfo = PrevWordsInfo.EMPTY_PREV_WORDS_INFO;
         refreshTypedWordCache();
     }
 
@@ -117,7 +117,7 @@
         mIsBatchMode = false;
         mCursorPositionWithinWord = 0;
         mRejectedBatchModeSuggestion = null;
-        mPrevWordsInfo = new PrevWordsInfo(null);
+        mPrevWordsInfo = PrevWordsInfo.EMPTY_PREV_WORDS_INFO;
         refreshTypedWordCache();
     }
 
@@ -445,7 +445,7 @@
     // when the user inputs a separator that's not whitespace (including the case of the
     // double-space-to-period feature).
     public void discardPreviousWordForSuggestion() {
-        mPrevWordsInfo = new PrevWordsInfo(null);
+        mPrevWordsInfo = PrevWordsInfo.EMPTY_PREV_WORDS_INFO;
     }
 
     public void resumeSuggestionOnLastComposedWord(final LastComposedWord lastComposedWord,
diff --git a/java/src/com/android/inputmethod/latin/inputlogic/InputLogic.java b/java/src/com/android/inputmethod/latin/inputlogic/InputLogic.java
index 78d4bc8..d6d29a8 100644
--- a/java/src/com/android/inputmethod/latin/inputlogic/InputLogic.java
+++ b/java/src/com/android/inputmethod/latin/inputlogic/InputLogic.java
@@ -1612,8 +1612,9 @@
             return mConnection.getPrevWordsInfoFromNthPreviousWord(
                     spacingAndPunctuations, nthPreviousWord);
         } else {
-            return LastComposedWord.NOT_A_COMPOSED_WORD == mLastComposedWord ? new PrevWordsInfo()
-                    : new PrevWordsInfo(mLastComposedWord.mCommittedWord.toString());
+            return LastComposedWord.NOT_A_COMPOSED_WORD == mLastComposedWord ?
+                    PrevWordsInfo.BEGINNING_OF_SENTENCE :
+                            new PrevWordsInfo(mLastComposedWord.mCommittedWord.toString());
         }
     }
 
diff --git a/java/src/com/android/inputmethod/latin/utils/LanguageModelParam.java b/java/src/com/android/inputmethod/latin/utils/LanguageModelParam.java
index 430efdd..9c759ed 100644
--- a/java/src/com/android/inputmethod/latin/utils/LanguageModelParam.java
+++ b/java/src/com/android/inputmethod/latin/utils/LanguageModelParam.java
@@ -87,7 +87,7 @@
         final ArrayList<LanguageModelParam> languageModelParams =
                 CollectionUtils.newArrayList();
         final int N = tokens.size();
-        PrevWordsInfo prevWordsInfo = new PrevWordsInfo(null);
+        PrevWordsInfo prevWordsInfo = PrevWordsInfo.EMPTY_PREV_WORDS_INFO;
         for (int i = 0; i < N; ++i) {
             final String tempWord = tokens.get(i);
             if (StringUtils.isEmptyStringOrWhiteSpaces(tempWord)) {
@@ -104,7 +104,7 @@
                             + tempWord + "\"");
                 }
                 // Sentence terminator found. Split.
-                prevWordsInfo = new PrevWordsInfo(null);
+                prevWordsInfo = PrevWordsInfo.EMPTY_PREV_WORDS_INFO;
                 continue;
             }
             if (DEBUG_TOKEN) {
diff --git a/tests/src/com/android/inputmethod/latin/DistracterFilterTest.java b/tests/src/com/android/inputmethod/latin/DistracterFilterTest.java
index ddbc8ac..33f3794 100644
--- a/tests/src/com/android/inputmethod/latin/DistracterFilterTest.java
+++ b/tests/src/com/android/inputmethod/latin/DistracterFilterTest.java
@@ -37,7 +37,7 @@
     }
 
     public void testIsDistractorToWordsInDictionaries() {
-        final PrevWordsInfo EMPTY_PREV_WORDS_INFO = new PrevWordsInfo(null);
+        final PrevWordsInfo EMPTY_PREV_WORDS_INFO = PrevWordsInfo.EMPTY_PREV_WORDS_INFO;
 
         final Locale localeEnUs = new Locale("en", "US");
         String typedWord = "alot";
diff --git a/tests/src/com/android/inputmethod/latin/WordComposerTests.java b/tests/src/com/android/inputmethod/latin/WordComposerTests.java
index 17e7185..274555a 100644
--- a/tests/src/com/android/inputmethod/latin/WordComposerTests.java
+++ b/tests/src/com/android/inputmethod/latin/WordComposerTests.java
@@ -74,7 +74,7 @@
                 CoordinateUtils.newCoordinateArray(CODEPOINTS_WITH_SUPPLEMENTARY_CHAR.length,
                         Constants.NOT_A_COORDINATE, Constants.NOT_A_COORDINATE);
         wc.setComposingWord(CODEPOINTS_WITH_SUPPLEMENTARY_CHAR, COORDINATES_WITH_SUPPLEMENTARY_CHAR,
-                new PrevWordsInfo(null));
+                PrevWordsInfo.EMPTY_PREV_WORDS_INFO);
         assertEquals(wc.size(), CODEPOINTS_WITH_SUPPLEMENTARY_CHAR.length);
         assertFalse(wc.isCursorFrontOrMiddleOfComposingWord());
         wc.setCursorPositionWithinWord(3);
@@ -109,7 +109,7 @@
         assertEquals(PREV_WORDS_INFO_STR_WITHIN_BMP, wc.getPrevWordsInfoForSuggestion());
 
 
-        final PrevWordsInfo PREV_WORDS_INFO_NULL = new PrevWordsInfo(null);
+        final PrevWordsInfo PREV_WORDS_INFO_NULL = PrevWordsInfo.EMPTY_PREV_WORDS_INFO;
         wc.setComposingWord(CODEPOINTS_WITH_SUPPLEMENTARY_CHAR, COORDINATES_WITH_SUPPLEMENTARY_CHAR,
                 PREV_WORDS_INFO_NULL);
         wc.setCursorPositionWithinWord(3);
diff --git a/tests/src/com/android/inputmethod/latin/personalization/UserHistoryDictionaryTests.java b/tests/src/com/android/inputmethod/latin/personalization/UserHistoryDictionaryTests.java
index bc86864..7d3214a 100644
--- a/tests/src/com/android/inputmethod/latin/personalization/UserHistoryDictionaryTests.java
+++ b/tests/src/com/android/inputmethod/latin/personalization/UserHistoryDictionaryTests.java
@@ -110,7 +110,7 @@
     }
 
     private static void addToDict(final UserHistoryDictionary dict, final List<String> words) {
-        PrevWordsInfo prevWordsInfo = new PrevWordsInfo(null);
+        PrevWordsInfo prevWordsInfo = PrevWordsInfo.EMPTY_PREV_WORDS_INFO;
         for (String word : words) {
             UserHistoryDictionary.addToDictionary(dict, prevWordsInfo, word, true,
                     (int)TimeUnit.MILLISECONDS.toSeconds(System.currentTimeMillis()));