Merge "Store suggestions for each input length for missing space algorithm etc."
diff --git a/java/src/com/android/inputmethod/keyboard/KeyboardSwitcher.java b/java/src/com/android/inputmethod/keyboard/KeyboardSwitcher.java
index 2add292..54d842f 100644
--- a/java/src/com/android/inputmethod/keyboard/KeyboardSwitcher.java
+++ b/java/src/com/android/inputmethod/keyboard/KeyboardSwitcher.java
@@ -324,6 +324,12 @@
         setKeyboard(mKeyboardSet.getSymbolsShiftedKeyboard());
     }
 
+    // Implements {@link KeyboardState.SwitchActions}.
+    @Override
+    public void requestUpdatingShiftState() {
+        mState.onUpdateShiftState(mInputMethodService.getCurrentAutoCapsState());
+    }
+
     public boolean isInMomentarySwitchState() {
         return mState.isInMomentarySwitchState();
     }
diff --git a/java/src/com/android/inputmethod/keyboard/internal/KeyboardState.java b/java/src/com/android/inputmethod/keyboard/internal/KeyboardState.java
index f54bdbb..42f069c 100644
--- a/java/src/com/android/inputmethod/keyboard/internal/KeyboardState.java
+++ b/java/src/com/android/inputmethod/keyboard/internal/KeyboardState.java
@@ -53,6 +53,11 @@
         public void setSymbolsKeyboard();
 
         public void setSymbolsShiftedKeyboard();
+
+        /**
+         * Request to call back {@link KeyboardState#onUpdateShiftState(boolean)}.
+         */
+        public void requestUpdatingShiftState();
     }
 
     private KeyboardShiftState mKeyboardShiftState = new KeyboardShiftState();
@@ -212,6 +217,7 @@
         mSwitchState = SWITCH_STATE_ALPHA;
         setShiftLocked(mPrevMainKeyboardWasShiftLocked);
         mPrevMainKeyboardWasShiftLocked = false;
+        mSwitchActions.requestUpdatingShiftState();
     }
 
     private void setSymbolsKeyboard() {
diff --git a/java/src/com/android/inputmethod/latin/LatinIME.java b/java/src/com/android/inputmethod/latin/LatinIME.java
index 59de798..e60f550 100644
--- a/java/src/com/android/inputmethod/latin/LatinIME.java
+++ b/java/src/com/android/inputmethod/latin/LatinIME.java
@@ -1019,7 +1019,10 @@
 
     @Override
     public boolean onEvaluateFullscreenMode() {
-        return super.onEvaluateFullscreenMode() && mSettingsValues.mUseFullScreenMode;
+        // Reread resource value here, because this method is called by framework anytime as needed.
+        final boolean isFullscreenModeAllowed =
+                mSettingsValues.isFullscreenModeAllowed(getResources());
+        return super.onEvaluateFullscreenMode() && isFullscreenModeAllowed;
     }
 
     @Override
diff --git a/java/src/com/android/inputmethod/latin/SettingsValues.java b/java/src/com/android/inputmethod/latin/SettingsValues.java
index 83b27f7..0ae28d3 100644
--- a/java/src/com/android/inputmethod/latin/SettingsValues.java
+++ b/java/src/com/android/inputmethod/latin/SettingsValues.java
@@ -41,7 +41,6 @@
     private final String mSymbolsExcludedFromWordSeparators;
     public final String mWordSeparators;
     public final CharSequence mHintToSaveText;
-    public final boolean mUseFullScreenMode;
 
     // From preferences, in the same order as xml/prefs.xml:
     public final boolean mAutoCap;
@@ -107,7 +106,6 @@
         mWordSeparators = createWordSeparators(mMagicSpaceStrippers, mMagicSpaceSwappers,
                 mSymbolsExcludedFromWordSeparators, res);
         mHintToSaveText = context.getText(R.string.hint_add_to_dictionary);
-        mUseFullScreenMode = res.getBoolean(R.bool.config_use_fullscreen_mode);
 
         // Get the settings preferences
         mAutoCap = prefs.getBoolean(Settings.PREF_AUTO_CAP, true);
@@ -294,6 +292,10 @@
         return mVoiceKeyOnMain;
     }
 
+    public boolean isFullscreenModeAllowed(Resources res) {
+        return res.getBoolean(R.bool.config_use_fullscreen_mode);
+    }
+
     // Accessed from the settings interface, hence public
     public static float getCurrentKeypressSoundVolume(final SharedPreferences sp,
                 final Resources res) {
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 1cff4d8..85e7e9b 100644
--- a/native/src/unigram_dictionary.cpp
+++ b/native/src/unigram_dictionary.cpp
@@ -277,7 +277,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));
@@ -523,9 +523,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 9dc1fac..a47e2e5 100644
--- a/tests/src/com/android/inputmethod/keyboard/internal/KeyboardStateTests.java
+++ b/tests/src/com/android/inputmethod/keyboard/internal/KeyboardStateTests.java
@@ -19,101 +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;
-        }
-
-        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
@@ -134,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.
@@ -227,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
diff --git a/tools/makedict/src/com/android/inputmethod/latin/BinaryDictInputOutput.java b/tools/makedict/src/com/android/inputmethod/latin/BinaryDictInputOutput.java
index fcbb645..ae54c5d 100644
--- a/tools/makedict/src/com/android/inputmethod/latin/BinaryDictInputOutput.java
+++ b/tools/makedict/src/com/android/inputmethod/latin/BinaryDictInputOutput.java
@@ -144,7 +144,6 @@
 
     private static final int GROUP_CHARACTERS_TERMINATOR = 0x1F;
 
-    private static final int GROUP_COUNT_SIZE = 1;
     private static final int GROUP_TERMINATOR_SIZE = 1;
     private static final int GROUP_FLAGS_SIZE = 1;
     private static final int GROUP_FREQUENCY_SIZE = 1;
@@ -155,9 +154,8 @@
     private static final int NO_CHILDREN_ADDRESS = Integer.MIN_VALUE;
     private static final int INVALID_CHARACTER = -1;
 
-    // Limiting to 127 for upward compatibility
-    // TODO: implement a scheme to be able to shoot 256 chargroups in a node
-    private static final int MAX_CHARGROUPS_IN_A_NODE = 127;
+    private static final int MAX_CHARGROUPS_FOR_ONE_BYTE_CHARGROUP_COUNT = 0x7F; // 127
+    private static final int MAX_CHARGROUPS_IN_A_NODE = 0x7FFF; // 32767
 
     private static final int MAX_TERMINAL_FREQUENCY = 255;
 
@@ -267,6 +265,22 @@
     }
 
     /**
+     * Compute the binary size of the group count for a node
+     * @param node the node
+     * @return the size of the group count, either 1 or 2 bytes.
+     */
+    private static int getGroupCountSize(final Node node) {
+        if (MAX_CHARGROUPS_FOR_ONE_BYTE_CHARGROUP_COUNT >= node.mData.size()) {
+            return 1;
+        } else if (MAX_CHARGROUPS_IN_A_NODE >= node.mData.size()) {
+            return 2;
+        } else {
+            throw new RuntimeException("Can't have more than " + MAX_CHARGROUPS_IN_A_NODE
+                    + " groups in a node (found " + node.mData.size() +")");
+        }
+    }
+
+    /**
      * Compute the maximum size of a CharGroup, assuming 3-byte addresses for everything.
      *
      * @param group the CharGroup to compute the size of.
@@ -295,7 +309,7 @@
      * @param node the node to compute the maximum size of.
      */
     private static void setNodeMaximumSize(Node node) {
-        int size = GROUP_COUNT_SIZE;
+        int size = getGroupCountSize(node);
         for (CharGroup g : node.mData) {
             final int groupSize = getCharGroupMaximumSize(g);
             g.mCachedSize = groupSize;
@@ -394,7 +408,7 @@
      * @param dict the dictionary in which the word/attributes are to be found.
      */
     private static void computeActualNodeSize(Node node, FusionDictionary dict) {
-        int size = GROUP_COUNT_SIZE;
+        int size = getGroupCountSize(node);
         for (CharGroup group : node.mData) {
             int groupSize = GROUP_FLAGS_SIZE + getGroupCharactersSize(group);
             if (group.isTerminal()) groupSize += GROUP_FREQUENCY_SIZE;
@@ -437,12 +451,13 @@
         int nodeOffset = 0;
         for (Node n : flatNodes) {
             n.mCachedAddress = nodeOffset;
+            int groupCountSize = getGroupCountSize(n);
             int groupOffset = 0;
             for (CharGroup g : n.mData) {
-                g.mCachedAddress = GROUP_COUNT_SIZE + nodeOffset + groupOffset;
+                g.mCachedAddress = groupCountSize + nodeOffset + groupOffset;
                 groupOffset += g.mCachedSize;
             }
-            if (groupOffset + GROUP_COUNT_SIZE != n.mCachedSize) {
+            if (groupOffset + groupCountSize != n.mCachedSize) {
                 throw new RuntimeException("Bug : Stored and computed node size differ");
             }
             nodeOffset += n.mCachedSize;
@@ -629,13 +644,20 @@
     private static int writePlacedNode(FusionDictionary dict, byte[] buffer, Node node) {
         int index = node.mCachedAddress;
 
-        final int size = node.mData.size();
-        if (size > MAX_CHARGROUPS_IN_A_NODE)
-            throw new RuntimeException("A node has a group count over 127 (" + size + ").");
-
-        buffer[index++] = (byte)size;
+        final int groupCount = node.mData.size();
+        final int countSize = getGroupCountSize(node);
+        if (1 == countSize) {
+            buffer[index++] = (byte)groupCount;
+        } else if (2 == countSize) {
+            // We need to signal 2-byte size by setting the top bit of the MSB to 1, so
+            // we | 0x80 to do this.
+            buffer[index++] = (byte)((groupCount >> 8) | 0x80);
+            buffer[index++] = (byte)(groupCount & 0xFF);
+        } else {
+            throw new RuntimeException("Strange size from getGroupCountSize : " + countSize);
+        }
         int groupAddress = index;
-        for (int i = 0; i < size; ++i) {
+        for (int i = 0; i < groupCount; ++i) {
             CharGroup group = node.mData.get(i);
             if (index != group.mCachedAddress) throw new RuntimeException("Bug: write index is not "
                     + "the same as the cached address of the group");
diff --git a/tools/makedict/src/com/android/inputmethod/latin/FusionDictionary.java b/tools/makedict/src/com/android/inputmethod/latin/FusionDictionary.java
index 2327e19..3ab206d 100644
--- a/tools/makedict/src/com/android/inputmethod/latin/FusionDictionary.java
+++ b/tools/makedict/src/com/android/inputmethod/latin/FusionDictionary.java
@@ -435,6 +435,16 @@
     }
 
     /**
+     * Helper method to find out whether a word is in the dict or not.
+     */
+    public boolean hasWord(final String s) {
+        if (null == s || "".equals(s)) {
+            throw new RuntimeException("Can't search for a null or empty string");
+        }
+        return null != findWordInTree(mRoot, s);
+    }
+
+    /**
      * Recursively count the number of character groups in a given branch of the trie.
      *
      * @param node the parent node.