Merge "Implement KIND_OOV_CORRECTION"
diff --git a/java/src/com/android/inputmethod/latin/AdditionalSubtype.java b/java/src/com/android/inputmethod/latin/AdditionalSubtype.java
index 99b95ea..2c700e5 100644
--- a/java/src/com/android/inputmethod/latin/AdditionalSubtype.java
+++ b/java/src/com/android/inputmethod/latin/AdditionalSubtype.java
@@ -49,13 +49,14 @@
                 && SubtypeLocale.isExceptionalLocale(localeString)) {
             final String layoutDisplayName = SubtypeLocale.getKeyboardLayoutSetDisplayName(
                     keyboardLayoutSetName);
-            layoutDisplayNameExtraValue = StringUtils.appendToCsvIfNotExists(
+            layoutDisplayNameExtraValue = StringUtils.appendToCommaConcatenatedTextIfNotExists(
                     UNTRANSLATABLE_STRING_IN_SUBTYPE_NAME + "=" + layoutDisplayName, extraValue);
         } else {
             layoutDisplayNameExtraValue = extraValue;
         }
-        final String additionalSubtypeExtraValue = StringUtils.appendToCsvIfNotExists(
-                IS_ADDITIONAL_SUBTYPE, layoutDisplayNameExtraValue);
+        final String additionalSubtypeExtraValue =
+                StringUtils.appendToCommaConcatenatedTextIfNotExists(
+                        IS_ADDITIONAL_SUBTYPE, layoutDisplayNameExtraValue);
         final int nameId = SubtypeLocale.getSubtypeNameId(localeString, keyboardLayoutSetName);
         return new InputMethodSubtype(nameId, R.drawable.ic_subtype_keyboard,
                 localeString, KEYBOARD_MODE,
@@ -66,8 +67,9 @@
         final String localeString = subtype.getLocale();
         final String keyboardLayoutSetName = SubtypeLocale.getKeyboardLayoutSetName(subtype);
         final String layoutExtraValue = KEYBOARD_LAYOUT_SET + "=" + keyboardLayoutSetName;
-        final String extraValue = StringUtils.removeFromCsvIfExists(layoutExtraValue,
-                StringUtils.removeFromCsvIfExists(IS_ADDITIONAL_SUBTYPE, subtype.getExtraValue()));
+        final String extraValue = StringUtils.removeFromCommaConcatenatedTextIfExists(
+                layoutExtraValue, StringUtils.removeFromCommaConcatenatedTextIfExists(
+                        IS_ADDITIONAL_SUBTYPE, subtype.getExtraValue()));
         final String basePrefSubtype = localeString + LOCALE_AND_LAYOUT_SEPARATOR
                 + keyboardLayoutSetName;
         return extraValue.isEmpty() ? basePrefSubtype
diff --git a/java/src/com/android/inputmethod/latin/InputAttributes.java b/java/src/com/android/inputmethod/latin/InputAttributes.java
index dd58db5..8f98e3a 100644
--- a/java/src/com/android/inputmethod/latin/InputAttributes.java
+++ b/java/src/com/android/inputmethod/latin/InputAttributes.java
@@ -199,6 +199,7 @@
         if (editorInfo == null) return false;
         final String findingKey = (packageName != null) ? packageName + "." + key
                 : key;
-        return StringUtils.containsInCsv(findingKey, editorInfo.privateImeOptions);
+        return StringUtils.containsInCommaConcatenatedText(
+                findingKey, editorInfo.privateImeOptions);
     }
 }
diff --git a/java/src/com/android/inputmethod/latin/StringUtils.java b/java/src/com/android/inputmethod/latin/StringUtils.java
index ab050d7..fa90ba2 100644
--- a/java/src/com/android/inputmethod/latin/StringUtils.java
+++ b/java/src/com/android/inputmethod/latin/StringUtils.java
@@ -35,33 +35,50 @@
         return text.codePointCount(0, text.length());
     }
 
-    public static boolean containsInArray(final String key, final String[] array) {
+    public static boolean containsInArray(final String text, final String[] array) {
         for (final String element : array) {
-            if (key.equals(element)) return true;
+            if (text.equals(element)) return true;
         }
         return false;
     }
 
-    public static boolean containsInCsv(final String key, final String csv) {
-        if (TextUtils.isEmpty(csv)) return false;
-        return containsInArray(key, csv.split(","));
+    private static final String SEPARATOR_FOR_COMMA_CONCATENATED_TEXT = ",";
+
+    public static boolean containsInCommaConcatenatedText(final String text,
+            final String extraValues) {
+        if (TextUtils.isEmpty(extraValues)) {
+            return false;
+        }
+        return containsInArray(text, extraValues.split(SEPARATOR_FOR_COMMA_CONCATENATED_TEXT));
     }
 
-    public static String appendToCsvIfNotExists(final String key, final String csv) {
-        if (TextUtils.isEmpty(csv)) return key;
-        if (containsInCsv(key, csv)) return csv;
-        return csv + "," + key;
+    public static String appendToCommaConcatenatedTextIfNotExists(final String text,
+            final String extraValues) {
+        if (TextUtils.isEmpty(extraValues)) {
+            return text;
+        }
+        if (containsInCommaConcatenatedText(text, extraValues)) {
+            return extraValues;
+        }
+        return extraValues + SEPARATOR_FOR_COMMA_CONCATENATED_TEXT + text;
     }
 
-    public static String removeFromCsvIfExists(final String key, final String csv) {
-        if (TextUtils.isEmpty(csv)) return "";
-        final String[] elements = csv.split(",");
-        if (!containsInArray(key, elements)) return csv;
+    public static String removeFromCommaConcatenatedTextIfExists(final String text,
+            final String extraValues) {
+        if (TextUtils.isEmpty(extraValues)) {
+            return "";
+        }
+        final String[] elements = extraValues.split(SEPARATOR_FOR_COMMA_CONCATENATED_TEXT);
+        if (!containsInArray(text, elements)) {
+            return extraValues;
+        }
         final ArrayList<String> result = CollectionUtils.newArrayList(elements.length - 1);
         for (final String element : elements) {
-            if (!key.equals(element)) result.add(element);
+            if (!text.equals(element)) {
+                result.add(element);
+            }
         }
-        return TextUtils.join(",", result);
+        return TextUtils.join(SEPARATOR_FOR_COMMA_CONCATENATED_TEXT, result);
     }
 
     /**
diff --git a/tests/src/com/android/inputmethod/latin/StringUtilsTests.java b/tests/src/com/android/inputmethod/latin/StringUtilsTests.java
index 29e790a..e980683 100644
--- a/tests/src/com/android/inputmethod/latin/StringUtilsTests.java
+++ b/tests/src/com/android/inputmethod/latin/StringUtilsTests.java
@@ -40,57 +40,62 @@
         }));
     }
 
-    public void testContainsInCsv() {
-        assertFalse("null", StringUtils.containsInCsv("key", null));
-        assertFalse("empty", StringUtils.containsInCsv("key", ""));
-        assertFalse("not in 1 element", StringUtils.containsInCsv("key", "key1"));
-        assertFalse("not in 2 elements", StringUtils.containsInCsv("key", "key1,key2"));
+    public void testContainsInExtraValues() {
+        assertFalse("null", StringUtils.containsInCommaConcatenatedText("key", null));
+        assertFalse("empty", StringUtils.containsInCommaConcatenatedText("key", ""));
+        assertFalse("not in 1 element",
+                StringUtils.containsInCommaConcatenatedText("key", "key1"));
+        assertFalse("not in 2 elements",
+                StringUtils.containsInCommaConcatenatedText("key", "key1,key2"));
 
-        assertTrue("in 1 element", StringUtils.containsInCsv("key", "key"));
-        assertTrue("in 2 elements", StringUtils.containsInCsv("key", "key1,key"));
+        assertTrue("in 1 element", StringUtils.containsInCommaConcatenatedText("key", "key"));
+        assertTrue("in 2 elements", StringUtils.containsInCommaConcatenatedText("key", "key1,key"));
     }
 
-    public void testAppendToCsvIfNotExists() {
-        assertEquals("null", "key", StringUtils.appendToCsvIfNotExists("key", null));
-        assertEquals("empty", "key", StringUtils.appendToCsvIfNotExists("key", ""));
+    public void testAppendToExtraValuesIfNotExists() {
+        assertEquals("null", "key",
+                StringUtils.appendToCommaConcatenatedTextIfNotExists("key", null));
+        assertEquals("empty", "key",
+                StringUtils.appendToCommaConcatenatedTextIfNotExists("key", ""));
 
         assertEquals("not in 1 element", "key1,key",
-                StringUtils.appendToCsvIfNotExists("key", "key1"));
+                StringUtils.appendToCommaConcatenatedTextIfNotExists("key", "key1"));
         assertEquals("not in 2 elements", "key1,key2,key",
-                StringUtils.appendToCsvIfNotExists("key", "key1,key2"));
+                StringUtils.appendToCommaConcatenatedTextIfNotExists("key", "key1,key2"));
 
         assertEquals("in 1 element", "key",
-                StringUtils.appendToCsvIfNotExists("key", "key"));
+                StringUtils.appendToCommaConcatenatedTextIfNotExists("key", "key"));
         assertEquals("in 2 elements at position 1", "key,key2",
-                StringUtils.appendToCsvIfNotExists("key", "key,key2"));
+                StringUtils.appendToCommaConcatenatedTextIfNotExists("key", "key,key2"));
         assertEquals("in 2 elements at position 2", "key1,key",
-                StringUtils.appendToCsvIfNotExists("key", "key1,key"));
+                StringUtils.appendToCommaConcatenatedTextIfNotExists("key", "key1,key"));
         assertEquals("in 3 elements at position 2", "key1,key,key3",
-                StringUtils.appendToCsvIfNotExists("key", "key1,key,key3"));
+                StringUtils.appendToCommaConcatenatedTextIfNotExists("key", "key1,key,key3"));
     }
 
-    public void testRemoveFromCsvIfExists() {
-        assertEquals("null", "", StringUtils.removeFromCsvIfExists("key", null));
-        assertEquals("empty", "", StringUtils.removeFromCsvIfExists("key", ""));
+    public void testRemoveFromExtraValuesIfExists() {
+        assertEquals("null", "", StringUtils.removeFromCommaConcatenatedTextIfExists("key", null));
+        assertEquals("empty", "", StringUtils.removeFromCommaConcatenatedTextIfExists("key", ""));
 
         assertEquals("not in 1 element", "key1",
-                StringUtils.removeFromCsvIfExists("key", "key1"));
+                StringUtils.removeFromCommaConcatenatedTextIfExists("key", "key1"));
         assertEquals("not in 2 elements", "key1,key2",
-                StringUtils.removeFromCsvIfExists("key", "key1,key2"));
+                StringUtils.removeFromCommaConcatenatedTextIfExists("key", "key1,key2"));
 
         assertEquals("in 1 element", "",
-                StringUtils.removeFromCsvIfExists("key", "key"));
+                StringUtils.removeFromCommaConcatenatedTextIfExists("key", "key"));
         assertEquals("in 2 elements at position 1", "key2",
-                StringUtils.removeFromCsvIfExists("key", "key,key2"));
+                StringUtils.removeFromCommaConcatenatedTextIfExists("key", "key,key2"));
         assertEquals("in 2 elements at position 2", "key1",
-                StringUtils.removeFromCsvIfExists("key", "key1,key"));
+                StringUtils.removeFromCommaConcatenatedTextIfExists("key", "key1,key"));
         assertEquals("in 3 elements at position 2", "key1,key3",
-                StringUtils.removeFromCsvIfExists("key", "key1,key,key3"));
+                StringUtils.removeFromCommaConcatenatedTextIfExists("key", "key1,key,key3"));
 
         assertEquals("in 3 elements at position 1,2,3", "",
-                StringUtils.removeFromCsvIfExists("key", "key,key,key"));
+                StringUtils.removeFromCommaConcatenatedTextIfExists("key", "key,key,key"));
         assertEquals("in 5 elements at position 2,4", "key1,key3,key5",
-                StringUtils.removeFromCsvIfExists("key", "key1,key,key3,key,key5"));
+                StringUtils.removeFromCommaConcatenatedTextIfExists(
+                        "key", "key1,key,key3,key,key5"));
     }