Merge "Write multi-byte chargroup count to the binary dict"
diff --git a/native/src/binary_format.h b/native/src/binary_format.h
index 9944fa2..1d74998 100644
--- a/native/src/binary_format.h
+++ b/native/src/binary_format.h
@@ -61,7 +61,9 @@
 }
 
 inline int BinaryFormat::getGroupCountAndForwardPointer(const uint8_t* const dict, int* pos) {
-    return dict[(*pos)++];
+    const int msb = dict[(*pos)++];
+    if (msb < 0x80) return msb;
+    return ((msb & 0x7F) << 8) | dict[(*pos)++];
 }
 
 inline uint8_t BinaryFormat::getFlagsAndForwardPointer(const uint8_t* const dict, int* pos) {
diff --git a/native/src/dictionary.h b/native/src/dictionary.h
index 79d377a..90d7148 100644
--- a/native/src/dictionary.h
+++ b/native/src/dictionary.h
@@ -56,16 +56,7 @@
 
     // public static utility methods
     // static inline methods should be defined in the header file
-    static unsigned short getChar(const unsigned char *dict, int *pos);
-    static int getCount(const unsigned char *dict, int *pos);
-    static bool getTerminal(const unsigned char *dict, int *pos);
-    static int getAddress(const unsigned char *dict, int *pos);
-    static int getFreq(const unsigned char *dict, const bool isLatestDictVersion, int *pos);
     static int wideStrLen(unsigned short *str);
-    // returns next sibling's position
-    static int setDictionaryValues(const unsigned char *dict, const bool isLatestDictVersion,
-            const int pos, unsigned short *c, int *childrenPosition,
-            bool *terminal, int *freq);
 
  private:
     bool hasBigram();
@@ -87,56 +78,6 @@
 
 // public static utility methods
 // static inline methods should be defined in the header file
-inline unsigned short Dictionary::getChar(const unsigned char *dict, int *pos) {
-    unsigned short ch = (unsigned short) (dict[(*pos)++] & 0xFF);
-    // If the code is 255, then actual 16 bit code follows (in big endian)
-    if (ch == 0xFF) {
-        ch = ((dict[*pos] & 0xFF) << 8) | (dict[*pos + 1] & 0xFF);
-        (*pos) += 2;
-    }
-    return ch;
-}
-
-inline int Dictionary::getCount(const unsigned char *dict, int *pos) {
-    return dict[(*pos)++] & 0xFF;
-}
-
-inline bool Dictionary::getTerminal(const unsigned char *dict, int *pos) {
-    return (dict[*pos] & FLAG_TERMINAL_MASK) > 0;
-}
-
-inline int Dictionary::getAddress(const unsigned char *dict, int *pos) {
-    int address = 0;
-    if ((dict[*pos] & FLAG_ADDRESS_MASK) == 0) {
-        *pos += 1;
-    } else {
-        address += (dict[*pos] & (ADDRESS_MASK >> 16)) << 16;
-        address += (dict[*pos + 1] & 0xFF) << 8;
-        address += (dict[*pos + 2] & 0xFF);
-        *pos += 3;
-    }
-    return address;
-}
-
-inline int Dictionary::getFreq(const unsigned char *dict,
-        const bool isLatestDictVersion, int *pos) {
-    int freq = dict[(*pos)++] & 0xFF;
-    if (isLatestDictVersion) {
-        // skipping bigram
-        int bigramExist = (dict[*pos] & FLAG_BIGRAM_READ);
-        if (bigramExist > 0) {
-            int nextBigramExist = 1;
-            while (nextBigramExist > 0) {
-                (*pos) += 3;
-                nextBigramExist = (dict[(*pos)++] & FLAG_BIGRAM_CONTINUED);
-            }
-        } else {
-            (*pos)++;
-        }
-    }
-    return freq;
-}
-
 inline int Dictionary::wideStrLen(unsigned short *str) {
     if (!str) return 0;
     unsigned short *end = str;
@@ -144,22 +85,6 @@
         end++;
     return end - str;
 }
-
-inline int Dictionary::setDictionaryValues(const unsigned char *dict,
-        const bool isLatestDictVersion, const int pos, unsigned short *c,int *childrenPosition,
-        bool *terminal, int *freq) {
-    int position = pos;
-    // -- at char
-    *c = Dictionary::getChar(dict, &position);
-    // -- at flag/add
-    *terminal = Dictionary::getTerminal(dict, &position);
-    *childrenPosition = Dictionary::getAddress(dict, &position);
-    // -- after address or flag
-    *freq = (*terminal) ? Dictionary::getFreq(dict, isLatestDictVersion, &position) : 1;
-    // returns next sibling's position
-    return position;
-}
-
 } // namespace latinime
 
 #endif // LATINIME_DICTIONARY_H
diff --git a/native/src/unigram_dictionary.cpp b/native/src/unigram_dictionary.cpp
index bbae5a8..ca7f0be 100644
--- a/native/src/unigram_dictionary.cpp
+++ b/native/src/unigram_dictionary.cpp
@@ -280,7 +280,7 @@
             doAutoCompletion, maxErrors);
     int rootPosition = ROOT_POS;
     // Get the number of children of root, then increment the position
-    int childCount = Dictionary::getCount(DICT_ROOT, &rootPosition);
+    int childCount = BinaryFormat::getGroupCountAndForwardPointer(DICT_ROOT, &rootPosition);
     int outputIndex = 0;
 
     correction->initCorrectionState(rootPosition, childCount, (inputLength <= 0));
@@ -507,9 +507,10 @@
     int maxFreq = -1;
     const uint8_t* const root = DICT_ROOT;
 
-    mStackChildCount[0] = root[0];
+    int startPos = 0;
+    mStackChildCount[0] = BinaryFormat::getGroupCountAndForwardPointer(root, &startPos);
     mStackInputIndex[0] = 0;
-    mStackSiblingPos[0] = 1;
+    mStackSiblingPos[0] = startPos;
     while (depth >= 0) {
         const int charGroupCount = mStackChildCount[depth];
         int pos = mStackSiblingPos[depth];
diff --git a/tests/src/com/android/inputmethod/keyboard/internal/KeyboardStateTests.java b/tests/src/com/android/inputmethod/keyboard/internal/KeyboardStateTests.java
index 006a572..a47e2e5 100644
--- a/tests/src/com/android/inputmethod/keyboard/internal/KeyboardStateTests.java
+++ b/tests/src/com/android/inputmethod/keyboard/internal/KeyboardStateTests.java
@@ -19,105 +19,8 @@
 import android.test.AndroidTestCase;
 
 import com.android.inputmethod.keyboard.Keyboard;
-import com.android.inputmethod.keyboard.internal.KeyboardState.SwitchActions;
 
 public class KeyboardStateTests extends AndroidTestCase {
-    private static final int ALPHABET_UNSHIFTED = 0;
-    private static final int ALPHABET_MANUAL_SHIFTED = 1;
-    private static final int ALPHABET_AUTOMATIC_SHIFTED = 2;
-    private static final int ALPHABET_SHIFT_LOCKED = 3;
-    private static final int SYMBOLS_UNSHIFTED = 4;
-    private static final int SYMBOLS_SHIFTED = 5;
-
-    static class MockKeyboardSwitcher implements KeyboardState.SwitchActions {
-        public int mLayout = ALPHABET_UNSHIFTED;
-
-        public boolean mAutoCaps = NO_AUTO_CAPS;
-
-        final KeyboardState mState = new KeyboardState(this);
-
-        @Override
-        public void setAlphabetKeyboard() {
-            mLayout = ALPHABET_UNSHIFTED;
-        }
-
-        @Override
-        public void setShifted(int shiftMode) {
-            if (shiftMode == SwitchActions.UNSHIFT) {
-                mLayout = ALPHABET_UNSHIFTED;
-            } else if (shiftMode == SwitchActions.MANUAL_SHIFT) {
-                mLayout = ALPHABET_MANUAL_SHIFTED;
-            } else if (shiftMode == SwitchActions.AUTOMATIC_SHIFT) {
-                mLayout = ALPHABET_AUTOMATIC_SHIFTED;
-            }
-        }
-
-        @Override
-        public void setShiftLocked(boolean shiftLocked) {
-            if (shiftLocked) {
-                mLayout = ALPHABET_SHIFT_LOCKED;
-            } else {
-                mLayout = ALPHABET_UNSHIFTED;
-            }
-        }
-
-        @Override
-        public void setSymbolsKeyboard() {
-            mLayout = SYMBOLS_UNSHIFTED;
-        }
-
-        @Override
-        public void setSymbolsShiftedKeyboard() {
-            mLayout = SYMBOLS_SHIFTED;
-        }
-
-        @Override
-        public void requestUpdatingShiftState() {
-            mState.onUpdateShiftState(mAutoCaps);
-        }
-
-        public void toggleCapsLock() {
-            mState.onToggleCapsLock();
-        }
-
-        public void updateShiftState() {
-            mState.onUpdateShiftState(mAutoCaps);
-        }
-
-        public void loadKeyboard(String layoutSwitchBackSymbols,
-                boolean hasDistinctMultitouch) {
-            mState.onLoadKeyboard(layoutSwitchBackSymbols, hasDistinctMultitouch);
-        }
-
-        public void onPressShift(boolean withSliding) {
-            mState.onPressShift(withSliding);
-        }
-
-        public void onReleaseShift(boolean withSliding) {
-            mState.onReleaseShift(withSliding);
-        }
-
-        public void onPressSymbol() {
-            mState.onPressSymbol();
-        }
-
-        public void onReleaseSymbol() {
-            mState.onReleaseSymbol();
-        }
-
-        public void onOtherKeyPressed() {
-            mState.onOtherKeyPressed();
-        }
-
-        public void onCodeInput(int code, boolean isSinglePointer) {
-            mState.onCodeInput(code, isSinglePointer, mAutoCaps);
-        }
-
-        public void onCancelInput(boolean isSinglePointer) {
-            mState.onCancelInput(isSinglePointer);
-        }
-    }
-
     private MockKeyboardSwitcher mSwitcher;
 
     @Override
@@ -138,31 +41,31 @@
     // Argument for KeyboardState.onCodeInput.
     private static final boolean SINGLE = true;
     private static final boolean MULTI = false;
-    private static final boolean NO_AUTO_CAPS = false;
+    static final boolean NO_AUTO_CAPS = false;
     private static final boolean AUTO_CAPS = true;
 
     private void assertAlphabetNormal() {
-        assertEquals(ALPHABET_UNSHIFTED, mSwitcher.mLayout);
+        assertTrue(mSwitcher.assertAlphabetNormal());
     }
 
     private void assertAlphabetManualShifted() {
-        assertEquals(ALPHABET_MANUAL_SHIFTED, mSwitcher.mLayout);
+        assertTrue(mSwitcher.assertAlphabetManualShifted());
     }
 
     private void assertAlphabetAutomaticShifted() {
-        assertEquals(ALPHABET_AUTOMATIC_SHIFTED, mSwitcher.mLayout);
+        assertTrue(mSwitcher.assertAlphabetAutomaticShifted());
     }
 
     private void assertAlphabetShiftLocked() {
-        assertEquals(ALPHABET_SHIFT_LOCKED, mSwitcher.mLayout);
+        assertTrue(mSwitcher.assertAlphabetShiftLocked());
     }
 
     private void assertSymbolsNormal() {
-        assertEquals(SYMBOLS_UNSHIFTED, mSwitcher.mLayout);
+        assertTrue(mSwitcher.assertSymbolsNormal());
     }
 
     private void assertSymbolsShifted() {
-        assertEquals(SYMBOLS_SHIFTED, mSwitcher.mLayout);
+        assertTrue(mSwitcher.assertSymbolsShifted());
     }
 
     // Initial state test.
@@ -231,7 +134,7 @@
 
     // Automatic upper case test
     public void testAutomaticUpperCase() {
-        mSwitcher.mAutoCaps = AUTO_CAPS;
+        mSwitcher.setAutoCapsMode(AUTO_CAPS);
         // Update shift state with auto caps enabled.
         mSwitcher.updateShiftState();
         assertAlphabetAutomaticShifted();
diff --git a/tests/src/com/android/inputmethod/keyboard/internal/MockKeyboardSwitcher.java b/tests/src/com/android/inputmethod/keyboard/internal/MockKeyboardSwitcher.java
new file mode 100644
index 0000000..d5c647c
--- /dev/null
+++ b/tests/src/com/android/inputmethod/keyboard/internal/MockKeyboardSwitcher.java
@@ -0,0 +1,148 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+
+package com.android.inputmethod.keyboard.internal;
+
+import com.android.inputmethod.keyboard.internal.KeyboardState.SwitchActions;
+
+public class MockKeyboardSwitcher implements KeyboardState.SwitchActions {
+    public static final String WORD_SEPARATORS = " ,.";
+
+    private static final int ALPHABET_UNSHIFTED = 0;
+    private static final int ALPHABET_MANUAL_SHIFTED = 1;
+    private static final int ALPHABET_AUTOMATIC_SHIFTED = 2;
+    private static final int ALPHABET_SHIFT_LOCKED = 3;
+    private static final int SYMBOLS_UNSHIFTED = 4;
+    private static final int SYMBOLS_SHIFTED = 5;
+
+    private int mLayout = ALPHABET_UNSHIFTED;
+
+    private boolean mAutoCapsMode = KeyboardStateTests.NO_AUTO_CAPS;
+    // Following InputConnection's behavior. Simulating InputType.TYPE_TEXT_FLAG_CAP_WORDS.
+    private boolean mAutoCapsState = true;
+
+    private final KeyboardState mState = new KeyboardState(this);
+
+    public boolean assertAlphabetNormal() {
+        return mLayout == ALPHABET_UNSHIFTED;
+    }
+
+    public boolean assertAlphabetManualShifted() {
+        return mLayout == ALPHABET_MANUAL_SHIFTED;
+    }
+
+    public boolean assertAlphabetAutomaticShifted() {
+        return mLayout == ALPHABET_AUTOMATIC_SHIFTED;
+    }
+
+    public boolean assertAlphabetShiftLocked() {
+        return mLayout == ALPHABET_SHIFT_LOCKED;
+    }
+
+    public boolean assertSymbolsNormal() {
+        return mLayout == SYMBOLS_UNSHIFTED;
+    }
+
+    public boolean assertSymbolsShifted() {
+        return mLayout == SYMBOLS_SHIFTED;
+    }
+
+    public void setAutoCapsMode(boolean autoCaps) {
+        mAutoCapsMode = autoCaps;
+    }
+
+    @Override
+    public void setAlphabetKeyboard() {
+        mLayout = ALPHABET_UNSHIFTED;
+    }
+
+    @Override
+    public void setShifted(int shiftMode) {
+        if (shiftMode == SwitchActions.UNSHIFT) {
+            mLayout = ALPHABET_UNSHIFTED;
+        } else if (shiftMode == SwitchActions.MANUAL_SHIFT) {
+            mLayout = ALPHABET_MANUAL_SHIFTED;
+        } else if (shiftMode == SwitchActions.AUTOMATIC_SHIFT) {
+            mLayout = ALPHABET_AUTOMATIC_SHIFTED;
+        }
+    }
+
+    @Override
+    public void setShiftLocked(boolean shiftLocked) {
+        if (shiftLocked) {
+            mLayout = ALPHABET_SHIFT_LOCKED;
+        } else {
+            mLayout = ALPHABET_UNSHIFTED;
+        }
+    }
+
+    @Override
+    public void setSymbolsKeyboard() {
+        mLayout = SYMBOLS_UNSHIFTED;
+    }
+
+    @Override
+    public void setSymbolsShiftedKeyboard() {
+        mLayout = SYMBOLS_SHIFTED;
+    }
+
+    @Override
+    public void requestUpdatingShiftState() {
+        mState.onUpdateShiftState(mAutoCapsMode && mAutoCapsState);
+    }
+
+    public void toggleCapsLock() {
+        mState.onToggleCapsLock();
+    }
+
+    public void updateShiftState() {
+        mState.onUpdateShiftState(mAutoCapsMode && mAutoCapsState);
+    }
+
+    public void loadKeyboard(String layoutSwitchBackSymbols,
+            boolean hasDistinctMultitouch) {
+        mState.onLoadKeyboard(layoutSwitchBackSymbols, hasDistinctMultitouch);
+    }
+
+    public void onPressShift(boolean withSliding) {
+        mState.onPressShift(withSliding);
+    }
+
+    public void onReleaseShift(boolean withSliding) {
+        mState.onReleaseShift(withSliding);
+    }
+
+    public void onPressSymbol() {
+        mState.onPressSymbol();
+    }
+
+    public void onReleaseSymbol() {
+        mState.onReleaseSymbol();
+    }
+
+    public void onOtherKeyPressed() {
+        mState.onOtherKeyPressed();
+    }
+
+    public void onCodeInput(int code, boolean isSinglePointer) {
+        mAutoCapsState = (WORD_SEPARATORS.indexOf(code) >= 0);
+        mState.onCodeInput(code, isSinglePointer, mAutoCapsMode && mAutoCapsState);
+    }
+
+    public void onCancelInput(boolean isSinglePointer) {
+        mState.onCancelInput(isSinglePointer);
+    }
+}
\ No newline at end of file