From a245d15da5d295af21ead9a01583c64796a31ad7 Mon Sep 17 00:00:00 2001
From: Jean Chalard <jchalard@google.com>
Date: Thu, 12 Dec 2013 15:08:10 +0900
Subject: [PATCH] Have dicttool use the native library to generate v4 dicts.

Yay !

Change-Id: Iea8ced9e81031b9ab7eff05ad9ef7215be248de9
---
 .../latin/AbstractDictionaryWriter.java       |   4 +-
 .../inputmethod/latin/BinaryDictionary.java   |   3 +-
 .../makedict/DynamicBinaryDictIOUtils.java    |  14 +-
 .../latin/makedict/FormatSpec.java            |   4 +-
 ...3DictDecoder.java => Ver2DictDecoder.java} |  19 +-
 ...3DictEncoder.java => Ver2DictEncoder.java} |   8 +-
 ...3DictUpdater.java => Ver2DictUpdater.java} |   6 +-
 .../latin/makedict/Ver4DictEncoder.java       | 427 ++----------------
 native/jni/Android.mk                         |  86 +---
 native/jni/CleanupNativeFileList.mk           |  17 +
 native/jni/NativeFileList.mk                  |  94 ++++
 ...tmethod_latin_makedict_Ver3DictDecoder.cpp |  47 --
 native/jni/jni_common.cpp                     |   9 -
 native/jni/src/defines.h                      |   9 +
 .../structure/v4/ver4_dict_constants.cpp      |   3 +-
 .../dictionary/utils/file_utils.cpp           |  17 +-
 .../policyimpl/dictionary/utils/file_utils.h  |   2 +
 .../latin/BinaryDictionaryDecayingTests.java  |   5 +-
 .../makedict/BinaryDictIOUtilsTests.java      |   4 +-
 .../latin/makedict/BinaryDictUtils.java       |   4 +-
 ...erTests.java => Ver2DictDecoderTests.java} |  10 +-
 .../utils/UserHistoryDictIOUtilsTests.java    |   6 +-
 tools/dicttool/Android.mk                     |  19 +-
 tools/dicttool/NativeLib.mk                   |  14 +-
 .../android/content/SharedPreferences.java    |  14 +-
 .../compat/android/graphics/Rect.java         |  20 +
 .../compat/android/text/TextUtils.java        | 107 +++++
 tools/dicttool/compat/android/util/Log.java   |  14 +-
 .../view/inputmethod/CompletionInfo.java      |  21 +
 .../android/view/inputmethod/EditorInfo.java  |  20 +
 .../com/android/inputmethod/keyboard/Key.java |  24 +
 .../inputmethod/keyboard/Keyboard.java        |  22 +
 .../inputmethod/keyboard/ProximityInfo.java   |  28 ++
 .../android/inputmethod/latin/LatinIME.java   |  20 +
 .../AdditionalFeaturesSettingUtils.java       |  21 +
 .../latin/settings/SettingsValues.java        |  23 +
 .../latin/dicttool/DictionaryMaker.java       |   4 +-
 .../BinaryDictOffdeviceUtilsTests.java        |   4 +-
 38 files changed, 581 insertions(+), 592 deletions(-)
 rename java/src/com/android/inputmethod/latin/makedict/{Ver3DictDecoder.java => Ver2DictDecoder.java} (95%)
 rename java/src/com/android/inputmethod/latin/makedict/{Ver3DictEncoder.java => Ver2DictEncoder.java} (97%)
 rename java/src/com/android/inputmethod/latin/makedict/{Ver3DictUpdater.java => Ver2DictUpdater.java} (93%)
 create mode 100644 native/jni/CleanupNativeFileList.mk
 create mode 100644 native/jni/NativeFileList.mk
 delete mode 100644 native/jni/com_android_inputmethod_latin_makedict_Ver3DictDecoder.cpp
 rename tests/src/com/android/inputmethod/latin/makedict/{Ver3DictDecoderTests.java => Ver2DictDecoderTests.java} (94%)
 rename native/jni/com_android_inputmethod_latin_makedict_Ver3DictDecoder.h => tools/dicttool/compat/android/content/SharedPreferences.java (67%)
 create mode 100644 tools/dicttool/compat/android/graphics/Rect.java
 create mode 100644 tools/dicttool/compat/android/text/TextUtils.java
 create mode 100644 tools/dicttool/compat/android/view/inputmethod/CompletionInfo.java
 create mode 100644 tools/dicttool/compat/android/view/inputmethod/EditorInfo.java
 create mode 100644 tools/dicttool/compat/com/android/inputmethod/keyboard/Key.java
 create mode 100644 tools/dicttool/compat/com/android/inputmethod/keyboard/Keyboard.java
 create mode 100644 tools/dicttool/compat/com/android/inputmethod/keyboard/ProximityInfo.java
 create mode 100644 tools/dicttool/compat/com/android/inputmethod/latin/LatinIME.java
 create mode 100644 tools/dicttool/compat/com/android/inputmethod/latin/settings/AdditionalFeaturesSettingUtils.java
 create mode 100644 tools/dicttool/compat/com/android/inputmethod/latin/settings/SettingsValues.java

diff --git a/java/src/com/android/inputmethod/latin/AbstractDictionaryWriter.java b/java/src/com/android/inputmethod/latin/AbstractDictionaryWriter.java
index e6fb9807ed..1aee22baf8 100644
--- a/java/src/com/android/inputmethod/latin/AbstractDictionaryWriter.java
+++ b/java/src/com/android/inputmethod/latin/AbstractDictionaryWriter.java
@@ -21,7 +21,7 @@ import android.util.Log;
 
 import com.android.inputmethod.latin.makedict.DictEncoder;
 import com.android.inputmethod.latin.makedict.UnsupportedFormatException;
-import com.android.inputmethod.latin.makedict.Ver3DictEncoder;
+import com.android.inputmethod.latin.makedict.Ver2DictEncoder;
 
 import java.io.File;
 import java.io.IOException;
@@ -64,7 +64,7 @@ abstract public class AbstractDictionaryWriter {
         final String tempFilePath = file.getAbsolutePath() + ".temp";
         final File tempFile = new File(tempFilePath);
         try {
-            final DictEncoder dictEncoder = new Ver3DictEncoder(tempFile);
+            final DictEncoder dictEncoder = new Ver2DictEncoder(tempFile);
             writeDictionary(dictEncoder, attributeMap);
             tempFile.renameTo(file);
         } catch (IOException e) {
diff --git a/java/src/com/android/inputmethod/latin/BinaryDictionary.java b/java/src/com/android/inputmethod/latin/BinaryDictionary.java
index db4234c63e..95ac3e203c 100644
--- a/java/src/com/android/inputmethod/latin/BinaryDictionary.java
+++ b/java/src/com/android/inputmethod/latin/BinaryDictionary.java
@@ -367,6 +367,7 @@ public final class BinaryDictionary extends Dictionary {
     public static class LanguageModelParam {
         public final int[] mWord0;
         public final int[] mWord1;
+        // TODO: this needs to be a list of shortcuts
         public final int[] mShortcutTarget;
         public final int mUnigramProbability;
         public final int mBigramProbability;
@@ -375,7 +376,7 @@ public final class BinaryDictionary extends Dictionary {
         public final boolean mIsBlacklisted;
         public final int mTimestamp;
 
-        // Constructor for unigram.
+        // Constructor for unigram. TODO: support shortcuts
         public LanguageModelParam(final String word, final int unigramProbability,
                 final int timestamp) {
             mWord0 = null;
diff --git a/java/src/com/android/inputmethod/latin/makedict/DynamicBinaryDictIOUtils.java b/java/src/com/android/inputmethod/latin/makedict/DynamicBinaryDictIOUtils.java
index ff03190a38..97ad667a6f 100644
--- a/java/src/com/android/inputmethod/latin/makedict/DynamicBinaryDictIOUtils.java
+++ b/java/src/com/android/inputmethod/latin/makedict/DynamicBinaryDictIOUtils.java
@@ -55,7 +55,7 @@ public final class DynamicBinaryDictIOUtils {
      * @param newParentAddress the absolute address of the parent.
      * @param formatOptions file format options.
      */
-    private static void updateParentAddress(final Ver3DictUpdater dictUpdater,
+    private static void updateParentAddress(final Ver2DictUpdater dictUpdater,
             final int ptNodeOriginAddress, final int newParentAddress,
             final FormatOptions formatOptions) {
         final DictBuffer dictBuffer = dictUpdater.getDictBuffer();
@@ -88,7 +88,7 @@ public final class DynamicBinaryDictIOUtils {
      * @param newParentAddress the address to be written.
      * @param formatOptions file format options.
      */
-    private static void updateParentAddresses(final Ver3DictUpdater dictUpdater,
+    private static void updateParentAddresses(final Ver2DictUpdater dictUpdater,
             final int ptNodeOriginAddress, final int newParentAddress,
             final FormatOptions formatOptions) {
         final int originalPosition = dictUpdater.getPosition();
@@ -114,7 +114,7 @@ public final class DynamicBinaryDictIOUtils {
      * @param newChildrenAddress the absolute address of the child.
      * @param formatOptions file format options.
      */
-    private static void updateChildrenAddress(final Ver3DictUpdater dictUpdater,
+    private static void updateChildrenAddress(final Ver2DictUpdater dictUpdater,
             final int ptNodeOriginAddress, final int newChildrenAddress,
             final FormatOptions formatOptions) {
         final DictBuffer dictBuffer = dictUpdater.getDictBuffer();
@@ -134,7 +134,7 @@ public final class DynamicBinaryDictIOUtils {
      * Helper method to move a PtNode to the tail of the file.
      */
     private static int movePtNode(final OutputStream destination,
-            final Ver3DictUpdater dictUpdater, final PtNodeInfo info,
+            final Ver2DictUpdater dictUpdater, final PtNodeInfo info,
             final int nodeArrayOriginAddress, final int oldNodeAddress,
             final FormatOptions formatOptions) throws IOException {
         final DictBuffer dictBuffer = dictUpdater.getDictBuffer();
@@ -151,7 +151,7 @@ public final class DynamicBinaryDictIOUtils {
     }
 
     @SuppressWarnings("unused")
-    private static void updateForwardLink(final Ver3DictUpdater dictUpdater,
+    private static void updateForwardLink(final Ver2DictUpdater dictUpdater,
             final int nodeArrayOriginAddress, final int newNodeArrayAddress,
             final FormatOptions formatOptions) {
         final DictBuffer dictBuffer = dictUpdater.getDictBuffer();
@@ -199,7 +199,7 @@ public final class DynamicBinaryDictIOUtils {
             final int length, final int flags, final int frequency, final int parentAddress,
             final ArrayList<WeightedString> shortcutTargets,
             final ArrayList<PendingAttribute> bigrams, final OutputStream destination,
-            final Ver3DictUpdater dictUpdater, final int oldPtNodeArrayOrigin,
+            final Ver2DictUpdater dictUpdater, final int oldPtNodeArrayOrigin,
             final int oldPtNodeOrigin, final FormatOptions formatOptions) throws IOException {
         int size = 0;
         final int newPtNodeOrigin = fileEndAddress + 1;
@@ -252,7 +252,7 @@ public final class DynamicBinaryDictIOUtils {
     // TODO: Support batch insertion.
     // TODO: Remove @UsedForTesting once UserHistoryDictionary is implemented by BinaryDictionary.
     @UsedForTesting
-    public static void insertWord(final Ver3DictUpdater dictUpdater,
+    public static void insertWord(final Ver2DictUpdater dictUpdater,
             final OutputStream destination, final String word, final int frequency,
             final ArrayList<WeightedString> bigramStrings,
             final ArrayList<WeightedString> shortcuts, final boolean isNotAWord,
diff --git a/java/src/com/android/inputmethod/latin/makedict/FormatSpec.java b/java/src/com/android/inputmethod/latin/makedict/FormatSpec.java
index 20ddba836b..f23fe4656f 100644
--- a/java/src/com/android/inputmethod/latin/makedict/FormatSpec.java
+++ b/java/src/com/android/inputmethod/latin/makedict/FormatSpec.java
@@ -401,7 +401,7 @@ public final class FormatSpec {
         if (dictFile.isDirectory()) {
             return new Ver4DictDecoder(dictFile, bufferType);
         } else if (dictFile.isFile()) {
-            return new Ver3DictDecoder(dictFile, bufferType);
+            return new Ver2DictDecoder(dictFile, bufferType);
         }
         return null;
     }
@@ -411,7 +411,7 @@ public final class FormatSpec {
         if (dictFile.isDirectory()) {
             return new Ver4DictDecoder(dictFile, factory);
         } else if (dictFile.isFile()) {
-            return new Ver3DictDecoder(dictFile, factory);
+            return new Ver2DictDecoder(dictFile, factory);
         }
         return null;
     }
diff --git a/java/src/com/android/inputmethod/latin/makedict/Ver3DictDecoder.java b/java/src/com/android/inputmethod/latin/makedict/Ver2DictDecoder.java
similarity index 95%
rename from java/src/com/android/inputmethod/latin/makedict/Ver3DictDecoder.java
rename to java/src/com/android/inputmethod/latin/makedict/Ver2DictDecoder.java
index acab4f8a5f..e9667ab0ba 100644
--- a/java/src/com/android/inputmethod/latin/makedict/Ver3DictDecoder.java
+++ b/java/src/com/android/inputmethod/latin/makedict/Ver2DictDecoder.java
@@ -34,18 +34,11 @@ import java.util.ArrayList;
 import java.util.Arrays;
 
 /**
- * An implementation of DictDecoder for version 3 binary dictionary.
+ * An implementation of DictDecoder for version 2 binary dictionary.
  */
 @UsedForTesting
-public class Ver3DictDecoder extends AbstractDictDecoder {
-    private static final String TAG = Ver3DictDecoder.class.getSimpleName();
-
-    static {
-        JniUtils.loadNativeLibrary();
-    }
-
-    // TODO: implement something sensical instead of just a phony method
-    private static native int doNothing();
+public class Ver2DictDecoder extends AbstractDictDecoder {
+    private static final String TAG = Ver2DictDecoder.class.getSimpleName();
 
     protected static class PtNodeReader extends AbstractDictDecoder.PtNodeReader {
         private static int readFrequency(final DictBuffer dictBuffer) {
@@ -57,7 +50,7 @@ public class Ver3DictDecoder extends AbstractDictDecoder {
     private final DictionaryBufferFactory mBufferFactory;
     protected DictBuffer mDictBuffer;
 
-    /* package */ Ver3DictDecoder(final File file, final int factoryFlag) {
+    /* package */ Ver2DictDecoder(final File file, final int factoryFlag) {
         mDictionaryBinaryFile = file;
         mDictBuffer = null;
 
@@ -72,7 +65,7 @@ public class Ver3DictDecoder extends AbstractDictDecoder {
         }
     }
 
-    /* package */ Ver3DictDecoder(final File file, final DictionaryBufferFactory factory) {
+    /* package */ Ver2DictDecoder(final File file, final DictionaryBufferFactory factory) {
         mDictionaryBinaryFile = file;
         mBufferFactory = factory;
     }
@@ -166,7 +159,7 @@ public class Ver3DictDecoder extends AbstractDictDecoder {
         final ArrayList<PendingAttribute> bigrams;
         if (0 != (flags & FormatSpec.FLAG_HAS_BIGRAMS)) {
             bigrams = new ArrayList<PendingAttribute>();
-            addressPointer += PtNodeReader.readBigramAddresses(mDictBuffer, bigrams, 
+            addressPointer += PtNodeReader.readBigramAddresses(mDictBuffer, bigrams,
                     addressPointer);
             if (bigrams.size() >= FormatSpec.MAX_BIGRAMS_IN_A_PTNODE) {
                 throw new RuntimeException("Too many bigrams in a PtNode (" + bigrams.size()
diff --git a/java/src/com/android/inputmethod/latin/makedict/Ver3DictEncoder.java b/java/src/com/android/inputmethod/latin/makedict/Ver2DictEncoder.java
similarity index 97%
rename from java/src/com/android/inputmethod/latin/makedict/Ver3DictEncoder.java
rename to java/src/com/android/inputmethod/latin/makedict/Ver2DictEncoder.java
index 92eb861d63..665544228d 100644
--- a/java/src/com/android/inputmethod/latin/makedict/Ver3DictEncoder.java
+++ b/java/src/com/android/inputmethod/latin/makedict/Ver2DictEncoder.java
@@ -31,16 +31,16 @@ import java.util.ArrayList;
 import java.util.Iterator;
 
 /**
- * An implementation of DictEncoder for version 3 binary dictionary.
+ * An implementation of DictEncoder for version 2 binary dictionary.
  */
-public class Ver3DictEncoder implements DictEncoder {
+public class Ver2DictEncoder implements DictEncoder {
 
     private final File mDictFile;
     private OutputStream mOutStream;
     private byte[] mBuffer;
     private int mPosition;
 
-    public Ver3DictEncoder(final File dictFile) {
+    public Ver2DictEncoder(final File dictFile) {
         mDictFile = dictFile;
         mOutStream = null;
         mBuffer = null;
@@ -49,7 +49,7 @@ public class Ver3DictEncoder implements DictEncoder {
     // This constructor is used only by BinaryDictOffdeviceUtilsTests.
     // If you want to use this in the production code, you should consider keeping consistency of
     // the interface of Ver3DictDecoder by using factory.
-    public Ver3DictEncoder(final OutputStream outStream) {
+    public Ver2DictEncoder(final OutputStream outStream) {
         mDictFile = null;
         mOutStream = outStream;
     }
diff --git a/java/src/com/android/inputmethod/latin/makedict/Ver3DictUpdater.java b/java/src/com/android/inputmethod/latin/makedict/Ver2DictUpdater.java
similarity index 93%
rename from java/src/com/android/inputmethod/latin/makedict/Ver3DictUpdater.java
rename to java/src/com/android/inputmethod/latin/makedict/Ver2DictUpdater.java
index 07adda6252..6419340ff8 100644
--- a/java/src/com/android/inputmethod/latin/makedict/Ver3DictUpdater.java
+++ b/java/src/com/android/inputmethod/latin/makedict/Ver2DictUpdater.java
@@ -27,14 +27,14 @@ import java.io.OutputStream;
 import java.util.ArrayList;
 
 /**
- * An implementation of DictUpdater for version 3 binary dictionary.
+ * An implementation of DictUpdater for version 2 binary dictionary.
  */
 @UsedForTesting
-public class Ver3DictUpdater extends Ver3DictDecoder implements DictUpdater {
+public class Ver2DictUpdater extends Ver2DictDecoder implements DictUpdater {
     private OutputStream mOutStream;
 
     @UsedForTesting
-    public Ver3DictUpdater(final File dictFile, final int factoryType) {
+    public Ver2DictUpdater(final File dictFile, final int factoryType) {
         // DictUpdater must have an updatable DictBuffer.
         super(dictFile, ((factoryType & MASK_DICTBUFFER) == USE_BYTEARRAY)
                 ? USE_BYTEARRAY : USE_WRITABLE_BYTEBUFFER);
diff --git a/java/src/com/android/inputmethod/latin/makedict/Ver4DictEncoder.java b/java/src/com/android/inputmethod/latin/makedict/Ver4DictEncoder.java
index 8b80ebe63b..a746f99456 100644
--- a/java/src/com/android/inputmethod/latin/makedict/Ver4DictEncoder.java
+++ b/java/src/com/android/inputmethod/latin/makedict/Ver4DictEncoder.java
@@ -1,4 +1,3 @@
-/*
 /*
  * Copyright (C) 2013 The Android Open Source Project
  *
@@ -18,25 +17,15 @@
 package com.android.inputmethod.latin.makedict;
 
 import com.android.inputmethod.annotations.UsedForTesting;
-import com.android.inputmethod.latin.makedict.BinaryDictDecoderUtils.CharEncoding;
-import com.android.inputmethod.latin.makedict.FormatSpec.FileHeader;
+import com.android.inputmethod.latin.BinaryDictionary;
+import com.android.inputmethod.latin.Dictionary;
 import com.android.inputmethod.latin.makedict.FormatSpec.FormatOptions;
-import com.android.inputmethod.latin.makedict.FusionDictionary.DictionaryOptions;
 import com.android.inputmethod.latin.makedict.FusionDictionary.PtNode;
-import com.android.inputmethod.latin.makedict.FusionDictionary.PtNodeArray;
 import com.android.inputmethod.latin.makedict.FusionDictionary.WeightedString;
-import com.android.inputmethod.latin.utils.CollectionUtils;
-import com.android.inputmethod.latin.utils.FileUtils;
+import com.android.inputmethod.latin.utils.LocaleUtils;
 
 import java.io.File;
-import java.io.FileNotFoundException;
-import java.io.FileOutputStream;
 import java.io.IOException;
-import java.io.OutputStream;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.Comparator;
-import java.util.Iterator;
 
 /**
  * An implementation of DictEncoder for version 4 binary dictionary.
@@ -44,197 +33,19 @@ import java.util.Iterator;
 @UsedForTesting
 public class Ver4DictEncoder implements DictEncoder {
     private final File mDictPlacedDir;
-    private byte[] mTrieBuf;
-    private int mTriePos;
-    private OutputStream mTrieOutStream;
-    private OutputStream mHeaderOutStream;
-    private OutputStream mFreqOutStream;
-    private OutputStream mUnigramTimestampOutStream;
-    private OutputStream mTerminalAddressTableOutStream;
-    private File mDictDir;
-    private String mBaseFilename;
-    private BigramContentWriter mBigramWriter;
-    private ShortcutContentWriter mShortcutWriter;
 
     @UsedForTesting
     public Ver4DictEncoder(final File dictPlacedDir) {
         mDictPlacedDir = dictPlacedDir;
     }
 
-    private static class BigramContentWriter extends SparseTableContentWriter {
-        private final boolean mWriteTimestamp;
-
-        public BigramContentWriter(final String name, final int initialCapacity,
-                final File baseDir, final boolean writeTimestamp) {
-            super(name + FormatSpec.BIGRAM_FILE_EXTENSION, initialCapacity,
-                    FormatSpec.BIGRAM_ADDRESS_TABLE_BLOCK_SIZE, baseDir,
-                    getContentFilenames(name, writeTimestamp), getContentIds(writeTimestamp));
-            mWriteTimestamp = writeTimestamp;
-        }
-
-        private static String[] getContentFilenames(final String name,
-                final boolean writeTimestamp) {
-            final String[] contentFilenames;
-            if (writeTimestamp) {
-                contentFilenames = new String[] { name + FormatSpec.BIGRAM_FILE_EXTENSION,
-                        name + FormatSpec.BIGRAM_FILE_EXTENSION };
-            } else {
-                contentFilenames = new String[] { name + FormatSpec.BIGRAM_FILE_EXTENSION };
-            }
-            return contentFilenames;
-        }
-
-        private static String[] getContentIds(final boolean writeTimestamp) {
-            final String[] contentIds;
-            if (writeTimestamp) {
-                contentIds = new String[] { FormatSpec.BIGRAM_FREQ_CONTENT_ID,
-                        FormatSpec.BIGRAM_TIMESTAMP_CONTENT_ID };
-            } else {
-                contentIds = new String[] { FormatSpec.BIGRAM_FREQ_CONTENT_ID };
-            }
-            return contentIds;
-        }
-
-        public void writeBigramsForOneWord(final int terminalId, final int bigramCount,
-                final Iterator<WeightedString> bigramIterator, final FusionDictionary dict)
-                        throws IOException {
-            write(FormatSpec.BIGRAM_FREQ_CONTENT_INDEX, terminalId,
-                    new SparseTableContentWriterInterface() {
-                        @Override
-                        public void write(final OutputStream outStream) throws IOException {
-                            writeBigramsForOneWordInternal(outStream, bigramIterator, dict);
-                        }});
-            if (mWriteTimestamp) {
-                write(FormatSpec.BIGRAM_TIMESTAMP_CONTENT_INDEX, terminalId,
-                        new SparseTableContentWriterInterface() {
-                            @Override
-                            public void write(final OutputStream outStream) throws IOException {
-                                initBigramTimestampsCountersAndLevelsForOneWordInternal(outStream,
-                                        bigramCount);
-                            }});
-            }
-        }
-
-        private void writeBigramsForOneWordInternal(final OutputStream outStream,
-                final Iterator<WeightedString> bigramIterator, final FusionDictionary dict)
-                        throws IOException {
-            while (bigramIterator.hasNext()) {
-                final WeightedString bigram = bigramIterator.next();
-                final PtNode target =
-                        FusionDictionary.findWordInTree(dict.mRootNodeArray, bigram.mWord);
-                final int unigramFrequencyForThisWord = target.mFrequency;
-                final int bigramFlags = BinaryDictEncoderUtils.makeBigramFlags(
-                        bigramIterator.hasNext(), 0, bigram.mFrequency,
-                        unigramFrequencyForThisWord, bigram.mWord);
-                BinaryDictEncoderUtils.writeUIntToStream(outStream, bigramFlags,
-                        FormatSpec.PTNODE_ATTRIBUTE_FLAGS_SIZE);
-                BinaryDictEncoderUtils.writeUIntToStream(outStream, target.mTerminalId,
-                        FormatSpec.PTNODE_ATTRIBUTE_MAX_ADDRESS_SIZE);
-            }
-        }
-
-        private void initBigramTimestampsCountersAndLevelsForOneWordInternal(
-                final OutputStream outStream, final int bigramCount) throws IOException {
-            for (int i = 0; i < bigramCount; ++i) {
-                // TODO: Figure out what initial values should be.
-                BinaryDictEncoderUtils.writeUIntToStream(outStream, 0 /* value */,
-                        FormatSpec.BIGRAM_TIMESTAMP_SIZE);
-                BinaryDictEncoderUtils.writeUIntToStream(outStream, 0 /* value */,
-                        FormatSpec.BIGRAM_COUNTER_SIZE);
-                BinaryDictEncoderUtils.writeUIntToStream(outStream, 0 /* value */,
-                        FormatSpec.BIGRAM_LEVEL_SIZE);
-            }
-        }
-    }
-
-    private static class ShortcutContentWriter extends SparseTableContentWriter {
-        public ShortcutContentWriter(final String name, final int initialCapacity,
-                final File baseDir) {
-            super(name + FormatSpec.SHORTCUT_FILE_EXTENSION, initialCapacity,
-                    FormatSpec.SHORTCUT_ADDRESS_TABLE_BLOCK_SIZE, baseDir,
-                    new String[] { name + FormatSpec.SHORTCUT_FILE_EXTENSION },
-                    new String[] { FormatSpec.SHORTCUT_CONTENT_ID });
-        }
-
-        public void writeShortcutForOneWord(final int terminalId,
-                final Iterator<WeightedString> shortcutIterator) throws IOException {
-            write(FormatSpec.SHORTCUT_CONTENT_INDEX, terminalId,
-                    new SparseTableContentWriterInterface() {
-                        @Override
-                        public void write(final OutputStream outStream) throws IOException {
-                            writeShortcutForOneWordInternal(outStream, shortcutIterator);
-                        }
-                    });
-        }
-
-        private void writeShortcutForOneWordInternal(final OutputStream outStream,
-                final Iterator<WeightedString> shortcutIterator) throws IOException {
-            while (shortcutIterator.hasNext()) {
-                final WeightedString target = shortcutIterator.next();
-                final int shortcutFlags = BinaryDictEncoderUtils.makeShortcutFlags(
-                        shortcutIterator.hasNext(), target.mFrequency);
-                BinaryDictEncoderUtils.writeUIntToStream(outStream, shortcutFlags,
-                        FormatSpec.PTNODE_ATTRIBUTE_FLAGS_SIZE);
-                CharEncoding.writeString(outStream, target.mWord);
-            }
-        }
-    }
-
-    private void openStreams(final FormatOptions formatOptions, final DictionaryOptions dictOptions)
-            throws FileNotFoundException, IOException {
-        final FileHeader header = new FileHeader(0, dictOptions, formatOptions);
-        mBaseFilename = header.getId() + "." + header.getVersion();
-        mDictDir = new File(mDictPlacedDir, mBaseFilename);
-        final File trieFile = new File(mDictDir, mBaseFilename + FormatSpec.TRIE_FILE_EXTENSION);
-        final File headerFile = new File(mDictDir,
-                mBaseFilename + FormatSpec.HEADER_FILE_EXTENSION);
-        final File freqFile = new File(mDictDir, mBaseFilename + FormatSpec.FREQ_FILE_EXTENSION);
-        final File timestampFile = new File(mDictDir,
-                mBaseFilename + FormatSpec.UNIGRAM_TIMESTAMP_FILE_EXTENSION);
-        final File terminalAddressTableFile = new File(mDictDir,
-                mBaseFilename + FormatSpec.TERMINAL_ADDRESS_TABLE_FILE_EXTENSION);
-        if (!mDictDir.isDirectory()) {
-            if (mDictDir.exists()) {
-                FileUtils.deleteRecursively(mDictDir);
-            }
-            mDictDir.mkdirs();
-        }
-        mTrieOutStream = new FileOutputStream(trieFile);
-        mHeaderOutStream = new FileOutputStream(headerFile);
-        mFreqOutStream = new FileOutputStream(freqFile);
-        mTerminalAddressTableOutStream = new FileOutputStream(terminalAddressTableFile);
-        if (formatOptions.mHasTimestamp) {
-            mUnigramTimestampOutStream = new FileOutputStream(timestampFile);
-        }
-    }
-
-    private void close() throws IOException {
-        try {
-            if (mTrieOutStream != null) {
-                mTrieOutStream.close();
-            }
-            if (mHeaderOutStream != null) {
-                mHeaderOutStream.close();
-            }
-            if (mFreqOutStream != null) {
-                mFreqOutStream.close();
-            }
-            if (mTerminalAddressTableOutStream != null) {
-                mTerminalAddressTableOutStream.close();
-            }
-            if (mUnigramTimestampOutStream != null) {
-                mUnigramTimestampOutStream.close();
-            }
-        } finally {
-            mTrieOutStream = null;
-            mHeaderOutStream = null;
-            mFreqOutStream = null;
-            mTerminalAddressTableOutStream = null;
-        }
-    }
-
+    // TODO: This builds a FusionDictionary first and iterates it to add words to the binary
+    // dictionary. However, it is possible to just add words directly to the binary dictionary
+    // instead.
+    // In the long run, when we stop supporting version 2, FusionDictionary will become deprecated
+    // and we can remove it. Then we'll be able to just call BinaryDictionary directly.
     @Override
-    public void writeDictionary(final FusionDictionary dict, final FormatOptions formatOptions)
+    public void writeDictionary(FusionDictionary dict, FormatOptions formatOptions)
             throws IOException, UnsupportedFormatException {
         if (formatOptions.mVersion != FormatSpec.VERSION4) {
             throw new UnsupportedFormatException("File header has a wrong version number : "
@@ -243,208 +54,70 @@ public class Ver4DictEncoder implements DictEncoder {
         if (!mDictPlacedDir.isDirectory()) {
             throw new UnsupportedFormatException("Given path is not a directory.");
         }
-
-        if (mTrieOutStream == null) {
-            openStreams(formatOptions, dict.mOptions);
-        }
-
-        BinaryDictEncoderUtils.writeDictionaryHeader(mHeaderOutStream, dict, formatOptions);
-
-        MakedictLog.i("Flattening the tree...");
-        ArrayList<PtNodeArray> flatNodes = BinaryDictEncoderUtils.flattenTree(dict.mRootNodeArray);
-        int terminalCount = 0;
-        final ArrayList<PtNode> nodes = CollectionUtils.newArrayList();
-        for (final PtNodeArray array : flatNodes) {
-            for (final PtNode node : array.mData) {
-                if (node.isTerminal()) {
-                    nodes.add(node);
-                    node.mTerminalId = terminalCount++;
+        if (!BinaryDictionary.createEmptyDictFile(mDictPlacedDir.getAbsolutePath(),
+                FormatSpec.VERSION4, dict.mOptions.mAttributes)) {
+            throw new IOException("Cannot create dictionary file");
+        }
+        final BinaryDictionary binaryDict = new BinaryDictionary(mDictPlacedDir.getAbsolutePath(),
+                0l, mDictPlacedDir.length(), true /* useFullEditDistance */,
+                LocaleUtils.constructLocaleFromString(dict.mOptions.mAttributes.get(
+                        FormatSpec.FileHeader.DICTIONARY_LOCALE_ATTRIBUTE)),
+                Dictionary.TYPE_USER /* Dictionary type. Does not matter for us */,
+                true /* isUpdatable */);
+        if (!binaryDict.isValidDictionary()) {
+            // Somehow createEmptyDictFile returned true, but the file was not created correctly
+            throw new IOException("Cannot create dictionary file");
+        }
+        for (final Word word : dict) {
+            // TODO: switch to addMultipleDictionaryEntries when they support shortcuts
+            if (null == word.mShortcutTargets || word.mShortcutTargets.isEmpty()) {
+                binaryDict.addUnigramWord(word.mWord, word.mFrequency,
+                        null /* shortcutTarget */, 0 /* shortcutProbability */,
+                        word.mIsNotAWord, word.mIsBlacklistEntry, 0 /* timestamp */);
+            } else {
+                for (final WeightedString shortcutTarget : word.mShortcutTargets) {
+                    binaryDict.addUnigramWord(word.mWord, word.mFrequency,
+                            shortcutTarget.mWord, shortcutTarget.mFrequency,
+                            word.mIsNotAWord, word.mIsBlacklistEntry, 0 /* timestamp */);
                 }
             }
-        }
-        Collections.sort(nodes, new Comparator<PtNode>() {
-            @Override
-            public int compare(final PtNode lhs, final PtNode rhs) {
-                if (lhs.mFrequency != rhs.mFrequency) {
-                    return lhs.mFrequency < rhs.mFrequency ? -1 : 1;
-                }
-                if (lhs.mTerminalId < rhs.mTerminalId) return -1;
-                if (lhs.mTerminalId > rhs.mTerminalId) return 1;
-                return 0;
+            if (binaryDict.needsToRunGC(true /* mindsBlockByGC */)) {
+                binaryDict.flushWithGC();
             }
-        });
-        int count = 0;
-        for (final PtNode node : nodes) {
-            node.mTerminalId = count++;
-        }
-
-        MakedictLog.i("Computing addresses...");
-        BinaryDictEncoderUtils.computeAddresses(dict, flatNodes, formatOptions);
-        if (MakedictLog.DBG) BinaryDictEncoderUtils.checkFlatPtNodeArrayList(flatNodes);
-
-        writeTerminalData(flatNodes, terminalCount);
-        if (formatOptions.mHasTimestamp) {
-            initUnigramTimestamps(terminalCount);
-        }
-        mBigramWriter = new BigramContentWriter(mBaseFilename, terminalCount, mDictDir,
-                formatOptions.mHasTimestamp);
-        writeBigrams(flatNodes, dict);
-        mShortcutWriter = new ShortcutContentWriter(mBaseFilename, terminalCount, mDictDir);
-        writeShortcuts(flatNodes);
-
-        final PtNodeArray lastNodeArray = flatNodes.get(flatNodes.size() - 1);
-        final int bufferSize = lastNodeArray.mCachedAddressAfterUpdate + lastNodeArray.mCachedSize;
-        mTrieBuf = new byte[bufferSize];
-
-        MakedictLog.i("Writing file...");
-        for (PtNodeArray nodeArray : flatNodes) {
-            BinaryDictEncoderUtils.writePlacedPtNodeArray(dict, this, nodeArray, formatOptions);
         }
-        if (MakedictLog.DBG) {
-            BinaryDictEncoderUtils.showStatistics(flatNodes);
-            MakedictLog.i("has " + terminalCount + " terminals.");
+        for (final Word word0 : dict) {
+            if (null == word0.mBigrams) continue;
+            for (final WeightedString word1 : word0.mBigrams) {
+                binaryDict.addBigramWords(word0.mWord, word1.mWord, word1.mFrequency,
+                        0 /* timestamp */);
+            }
+            if (binaryDict.needsToRunGC(true /* mindsBlockByGC */)) {
+                binaryDict.flushWithGC();
+            }
         }
-        mTrieOutStream.write(mTrieBuf);
-
-        MakedictLog.i("Done");
-        close();
+        binaryDict.flushWithGC();
+        binaryDict.close();
     }
 
     @Override
     public void setPosition(int position) {
-        if (mTrieBuf == null || position < 0 || position > mTrieBuf.length) return;
-        mTriePos = position;
     }
 
     @Override
     public int getPosition() {
-        return mTriePos;
+        return 0;
     }
 
     @Override
     public void writePtNodeCount(int ptNodeCount) {
-        final int countSize = BinaryDictIOUtils.getPtNodeCountSize(ptNodeCount);
-        // ptNodeCount must fit on one byte or two bytes.
-        // Please see comments in FormatSpec
-        if (countSize != 1 && countSize != 2) {
-            throw new RuntimeException("Strange size from getPtNodeCountSize : " + countSize);
-        }
-        final int encodedPtNodeCount = (countSize == 2) ?
-                (ptNodeCount | FormatSpec.LARGE_PTNODE_ARRAY_SIZE_FIELD_SIZE_FLAG) : ptNodeCount;
-        mTriePos = BinaryDictEncoderUtils.writeUIntToBuffer(mTrieBuf, mTriePos, encodedPtNodeCount,
-                countSize);
-    }
-
-    private void writePtNodeFlags(final PtNode ptNode, final FormatOptions formatOptions) {
-        final int childrenPos = BinaryDictEncoderUtils.getChildrenPosition(ptNode, formatOptions);
-        mTriePos = BinaryDictEncoderUtils.writeUIntToBuffer(mTrieBuf, mTriePos,
-                BinaryDictEncoderUtils.makePtNodeFlags(ptNode, childrenPos, formatOptions),
-                FormatSpec.PTNODE_FLAGS_SIZE);
-    }
-
-    private void writeParentPosition(int parentPos, final PtNode ptNode,
-            final FormatOptions formatOptions) {
-        if (parentPos != FormatSpec.NO_PARENT_ADDRESS) {
-            parentPos -= ptNode.mCachedAddressAfterUpdate;
-        }
-        mTriePos = BinaryDictEncoderUtils.writeParentAddress(mTrieBuf, mTriePos, parentPos,
-                formatOptions);
-    }
-
-    private void writeCharacters(final int[] characters, final boolean hasSeveralChars) {
-        mTriePos = CharEncoding.writeCharArray(characters, mTrieBuf, mTriePos);
-        if (hasSeveralChars) {
-            mTrieBuf[mTriePos++] = FormatSpec.PTNODE_CHARACTERS_TERMINATOR;
-        }
-    }
-
-    private void writeTerminalId(final int terminalId) {
-        mTriePos = BinaryDictEncoderUtils.writeUIntToBuffer(mTrieBuf, mTriePos, terminalId,
-                FormatSpec.PTNODE_TERMINAL_ID_SIZE);
-    }
-
-    private void writeChildrenPosition(PtNode ptNode, FormatOptions formatOptions) {
-        final int childrenPos = BinaryDictEncoderUtils.getChildrenPosition(ptNode, formatOptions);
-        if (formatOptions.supportsDynamicUpdate()) {
-            mTriePos += BinaryDictEncoderUtils.writeSignedChildrenPosition(mTrieBuf,
-                    mTriePos, childrenPos);
-        } else {
-            mTriePos += BinaryDictEncoderUtils.writeChildrenPosition(mTrieBuf,
-                    mTriePos, childrenPos);
-        }
-    }
-
-    private void writeBigrams(final ArrayList<PtNodeArray> flatNodes, final FusionDictionary dict)
-            throws IOException {
-        mBigramWriter.openStreams();
-        for (final PtNodeArray nodeArray : flatNodes) {
-            for (final PtNode ptNode : nodeArray.mData) {
-                if (ptNode.mBigrams != null) {
-                    mBigramWriter.writeBigramsForOneWord(ptNode.mTerminalId, ptNode.mBigrams.size(),
-                            ptNode.mBigrams.iterator(), dict);
-                }
-            }
-        }
-        mBigramWriter.closeStreams();
-    }
-
-    private void writeShortcuts(final ArrayList<PtNodeArray> flatNodes) throws IOException {
-        mShortcutWriter.openStreams();
-        for (final PtNodeArray nodeArray : flatNodes) {
-            for (final PtNode ptNode : nodeArray.mData) {
-                if (ptNode.mShortcutTargets != null && !ptNode.mShortcutTargets.isEmpty()) {
-                    mShortcutWriter.writeShortcutForOneWord(ptNode.mTerminalId,
-                            ptNode.mShortcutTargets.iterator());
-                }
-            }
-        }
-        mShortcutWriter.closeStreams();
     }
 
     @Override
     public void writeForwardLinkAddress(int forwardLinkAddress) {
-        mTriePos = BinaryDictEncoderUtils.writeUIntToBuffer(mTrieBuf, mTriePos,
-                forwardLinkAddress, FormatSpec.FORWARD_LINK_ADDRESS_SIZE);
     }
 
     @Override
-    public void writePtNode(final PtNode ptNode, final int parentPosition,
-            final FormatOptions formatOptions, final FusionDictionary dict) {
-        writePtNodeFlags(ptNode, formatOptions);
-        writeParentPosition(parentPosition, ptNode, formatOptions);
-        writeCharacters(ptNode.mChars, ptNode.hasSeveralChars());
-        if (ptNode.isTerminal()) {
-            writeTerminalId(ptNode.mTerminalId);
-        }
-        writeChildrenPosition(ptNode, formatOptions);
-    }
-
-    private void writeTerminalData(final ArrayList<PtNodeArray> flatNodes,
-          final int terminalCount) throws IOException {
-        final byte[] freqBuf = new byte[terminalCount * FormatSpec.FREQUENCY_AND_FLAGS_SIZE];
-        final byte[] terminalAddressTableBuf =
-                new byte[terminalCount * FormatSpec.TERMINAL_ADDRESS_TABLE_ADDRESS_SIZE];
-        for (final PtNodeArray nodeArray : flatNodes) {
-            for (final PtNode ptNode : nodeArray.mData) {
-                if (ptNode.isTerminal()) {
-                    BinaryDictEncoderUtils.writeUIntToBuffer(freqBuf,
-                            ptNode.mTerminalId * FormatSpec.FREQUENCY_AND_FLAGS_SIZE,
-                            ptNode.mFrequency, FormatSpec.FREQUENCY_AND_FLAGS_SIZE);
-                    BinaryDictEncoderUtils.writeUIntToBuffer(terminalAddressTableBuf,
-                            ptNode.mTerminalId * FormatSpec.TERMINAL_ADDRESS_TABLE_ADDRESS_SIZE,
-                            ptNode.mCachedAddressAfterUpdate,
-                            FormatSpec.TERMINAL_ADDRESS_TABLE_ADDRESS_SIZE);
-                }
-            }
-        }
-        mFreqOutStream.write(freqBuf);
-        mTerminalAddressTableOutStream.write(terminalAddressTableBuf);
-    }
-
-    private void initUnigramTimestamps(final int terminalCount) throws IOException {
-        // Initial value of time stamps for each word is 0.
-        final byte[] unigramTimestampBuf =
-                new byte[terminalCount * FormatSpec.UNIGRAM_TIMESTAMP_SIZE];
-        mUnigramTimestampOutStream.write(unigramTimestampBuf);
+    public void writePtNode(
+            PtNode ptNode, int parentPosition, FormatOptions formatOptions, FusionDictionary dict) {
     }
 }
diff --git a/native/jni/Android.mk b/native/jni/Android.mk
index e11e706f34..7827db302c 100644
--- a/native/jni/Android.mk
+++ b/native/jni/Android.mk
@@ -39,87 +39,7 @@ endif # TARGET_ARCH
 # To suppress compiler warnings for unused variables/functions used for debug features etc.
 LOCAL_CFLAGS += -Wno-unused-parameter -Wno-unused-function
 
-LATIN_IME_JNI_SRC_FILES := \
-    com_android_inputmethod_keyboard_ProximityInfo.cpp \
-    com_android_inputmethod_latin_BinaryDictionary.cpp \
-    com_android_inputmethod_latin_DicTraverseSession.cpp \
-    com_android_inputmethod_latin_makedict_Ver3DictDecoder.cpp \
-    jni_common.cpp
-
-LATIN_IME_CORE_SRC_FILES := \
-    suggest/core/suggest.cpp \
-    $(addprefix suggest/core/dicnode/, \
-        dic_node.cpp \
-        dic_node_utils.cpp \
-        dic_nodes_cache.cpp) \
-    $(addprefix suggest/core/dictionary/, \
-        bigram_dictionary.cpp \
-        bloom_filter.cpp \
-        dictionary.cpp \
-        digraph_utils.cpp \
-        error_type_utils.cpp \
-        multi_bigram_map.cpp \
-        unigram_property.cpp) \
-    $(addprefix suggest/core/layout/, \
-        additional_proximity_chars.cpp \
-        proximity_info.cpp \
-        proximity_info_params.cpp \
-        proximity_info_state.cpp \
-        proximity_info_state_utils.cpp) \
-    suggest/core/policy/weighting.cpp \
-    suggest/core/session/dic_traverse_session.cpp \
-    $(addprefix suggest/policyimpl/dictionary/, \
-        header/header_policy.cpp \
-        header/header_read_write_utils.cpp \
-        shortcut/shortcut_list_reading_utils.cpp \
-        structure/dictionary_structure_with_buffer_policy_factory.cpp) \
-    $(addprefix suggest/policyimpl/dictionary/bigram/, \
-        bigram_list_read_write_utils.cpp \
-        ver4_bigram_list_policy.cpp) \
-    $(addprefix suggest/policyimpl/dictionary/structure/pt_common/, \
-        dynamic_pt_gc_event_listeners.cpp \
-        dynamic_pt_reading_helper.cpp \
-        dynamic_pt_reading_utils.cpp \
-        dynamic_pt_updating_helper.cpp \
-        dynamic_pt_writing_utils.cpp) \
-    $(addprefix suggest/policyimpl/dictionary/structure/v2/, \
-        patricia_trie_policy.cpp \
-        patricia_trie_reading_utils.cpp) \
-    $(addprefix suggest/policyimpl/dictionary/structure/v4/, \
-        ver4_dict_buffers.cpp \
-        ver4_dict_constants.cpp \
-        ver4_patricia_trie_node_reader.cpp \
-        ver4_patricia_trie_node_writer.cpp \
-        ver4_patricia_trie_policy.cpp \
-        ver4_patricia_trie_reading_utils.cpp \
-        ver4_patricia_trie_writing_helper.cpp) \
-    $(addprefix suggest/policyimpl/dictionary/structure/v4/content/, \
-        bigram_dict_content.cpp \
-        probability_dict_content.cpp \
-        shortcut_dict_content.cpp \
-        sparse_table_dict_content.cpp \
-        terminal_position_lookup_table.cpp) \
-    $(addprefix suggest/policyimpl/dictionary/utils/, \
-        buffer_with_extendable_buffer.cpp \
-        byte_array_utils.cpp \
-        dict_file_writing_utils.cpp \
-        file_utils.cpp \
-        forgetting_curve_utils.cpp \
-        format_utils.cpp \
-        mmapped_buffer.cpp \
-        sparse_table.cpp) \
-    suggest/policyimpl/gesture/gesture_suggest_policy_factory.cpp \
-    $(addprefix suggest/policyimpl/typing/, \
-        scoring_params.cpp \
-        typing_scoring.cpp \
-        typing_suggest_policy.cpp \
-        typing_traversal.cpp \
-        typing_weighting.cpp) \
-    $(addprefix utils/, \
-        autocorrection_threshold_utils.cpp \
-        char_utils.cpp \
-        log_utils.cpp \
-        time_keeper.cpp)
+include $(LOCAL_PATH)/NativeFileList.mk
 
 LOCAL_SRC_FILES := \
     $(LATIN_IME_JNI_SRC_FILES) \
@@ -172,6 +92,4 @@ LOCAL_LDFLAGS += -ldl
 include $(BUILD_SHARED_LIBRARY)
 
 #################### Clean up the tmp vars
-LATIN_IME_CORE_SRC_FILES :=
-LATIN_IME_JNI_SRC_FILES :=
-LATIN_IME_SRC_DIR :=
+include $(LOCAL_PATH)/CleanupNativeFileList.mk
diff --git a/native/jni/CleanupNativeFileList.mk b/native/jni/CleanupNativeFileList.mk
new file mode 100644
index 0000000000..5420f16f91
--- /dev/null
+++ b/native/jni/CleanupNativeFileList.mk
@@ -0,0 +1,17 @@
+# Copyright (C) 2013 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.
+
+LATIN_IME_CORE_SRC_FILES :=
+LATIN_IME_JNI_SRC_FILES :=
+LATIN_IME_SRC_DIR :=
diff --git a/native/jni/NativeFileList.mk b/native/jni/NativeFileList.mk
new file mode 100644
index 0000000000..d80a1115f8
--- /dev/null
+++ b/native/jni/NativeFileList.mk
@@ -0,0 +1,94 @@
+# Copyright (C) 2013 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.
+
+LATIN_IME_JNI_SRC_FILES := \
+    com_android_inputmethod_keyboard_ProximityInfo.cpp \
+    com_android_inputmethod_latin_BinaryDictionary.cpp \
+    com_android_inputmethod_latin_DicTraverseSession.cpp \
+    jni_common.cpp
+
+LATIN_IME_CORE_SRC_FILES := \
+    suggest/core/suggest.cpp \
+    $(addprefix suggest/core/dicnode/, \
+        dic_node.cpp \
+        dic_node_utils.cpp \
+        dic_nodes_cache.cpp) \
+    $(addprefix suggest/core/dictionary/, \
+        bigram_dictionary.cpp \
+        bloom_filter.cpp \
+        dictionary.cpp \
+        digraph_utils.cpp \
+        error_type_utils.cpp \
+        multi_bigram_map.cpp \
+        unigram_property.cpp) \
+    $(addprefix suggest/core/layout/, \
+        additional_proximity_chars.cpp \
+        proximity_info.cpp \
+        proximity_info_params.cpp \
+        proximity_info_state.cpp \
+        proximity_info_state_utils.cpp) \
+    suggest/core/policy/weighting.cpp \
+    suggest/core/session/dic_traverse_session.cpp \
+    $(addprefix suggest/policyimpl/dictionary/, \
+        header/header_policy.cpp \
+        header/header_read_write_utils.cpp \
+        shortcut/shortcut_list_reading_utils.cpp \
+        structure/dictionary_structure_with_buffer_policy_factory.cpp) \
+    $(addprefix suggest/policyimpl/dictionary/bigram/, \
+        bigram_list_read_write_utils.cpp \
+        ver4_bigram_list_policy.cpp) \
+    $(addprefix suggest/policyimpl/dictionary/structure/pt_common/, \
+        dynamic_pt_gc_event_listeners.cpp \
+        dynamic_pt_reading_helper.cpp \
+        dynamic_pt_reading_utils.cpp \
+        dynamic_pt_updating_helper.cpp \
+        dynamic_pt_writing_utils.cpp) \
+    $(addprefix suggest/policyimpl/dictionary/structure/v2/, \
+        patricia_trie_policy.cpp \
+        patricia_trie_reading_utils.cpp) \
+    $(addprefix suggest/policyimpl/dictionary/structure/v4/, \
+        ver4_dict_buffers.cpp \
+        ver4_dict_constants.cpp \
+        ver4_patricia_trie_node_reader.cpp \
+        ver4_patricia_trie_node_writer.cpp \
+        ver4_patricia_trie_policy.cpp \
+        ver4_patricia_trie_reading_utils.cpp \
+        ver4_patricia_trie_writing_helper.cpp) \
+    $(addprefix suggest/policyimpl/dictionary/structure/v4/content/, \
+        bigram_dict_content.cpp \
+        probability_dict_content.cpp \
+        shortcut_dict_content.cpp \
+        sparse_table_dict_content.cpp \
+        terminal_position_lookup_table.cpp) \
+    $(addprefix suggest/policyimpl/dictionary/utils/, \
+        buffer_with_extendable_buffer.cpp \
+        byte_array_utils.cpp \
+        dict_file_writing_utils.cpp \
+        file_utils.cpp \
+        forgetting_curve_utils.cpp \
+        format_utils.cpp \
+        mmapped_buffer.cpp \
+        sparse_table.cpp) \
+    suggest/policyimpl/gesture/gesture_suggest_policy_factory.cpp \
+    $(addprefix suggest/policyimpl/typing/, \
+        scoring_params.cpp \
+        typing_scoring.cpp \
+        typing_suggest_policy.cpp \
+        typing_traversal.cpp \
+        typing_weighting.cpp) \
+    $(addprefix utils/, \
+        autocorrection_threshold_utils.cpp \
+        char_utils.cpp \
+        log_utils.cpp \
+        time_keeper.cpp)
diff --git a/native/jni/com_android_inputmethod_latin_makedict_Ver3DictDecoder.cpp b/native/jni/com_android_inputmethod_latin_makedict_Ver3DictDecoder.cpp
deleted file mode 100644
index 15088b65a9..0000000000
--- a/native/jni/com_android_inputmethod_latin_makedict_Ver3DictDecoder.cpp
+++ /dev/null
@@ -1,47 +0,0 @@
-/*
- * Copyright (C) 2013 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.
- */
-
-#define LOG_TAG "LatinIME: jni: Ver3DictDecoder"
-
-#include "com_android_inputmethod_latin_makedict_Ver3DictDecoder.h"
-
-#include "defines.h"
-#include "jni.h"
-#include "jni_common.h"
-
-namespace latinime {
-static int latinime_Ver3DictDecoder_doNothing(JNIEnv *env, jclass clazz) {
-    // This is a phony method for test - it does nothing. It just returns some value
-    // unlikely to be in memory by chance for testing purposes.
-    // TODO: remove this method.
-    return 2097;
-}
-
-static const JNINativeMethod sMethods[] = {
-    {
-        // TODO: remove this entry when we have one useful method in here
-        const_cast<char *>("doNothing"),
-        const_cast<char *>("()I"),
-        reinterpret_cast<void *>(latinime_Ver3DictDecoder_doNothing)
-    },
-};
-
-int register_Ver3DictDecoder(JNIEnv *env) {
-    const char *const kClassPathName =
-            "com/android/inputmethod/latin/makedict/Ver3DictDecoder";
-    return registerNativeMethods(env, kClassPathName, sMethods, NELEMS(sMethods));
-}
-} // namespace latinime
diff --git a/native/jni/jni_common.cpp b/native/jni/jni_common.cpp
index 3a8f4362d9..f2867d7c3e 100644
--- a/native/jni/jni_common.cpp
+++ b/native/jni/jni_common.cpp
@@ -18,12 +18,9 @@
 
 #include "jni_common.h"
 
-#ifndef HOST_TOOL
 #include "com_android_inputmethod_keyboard_ProximityInfo.h"
 #include "com_android_inputmethod_latin_BinaryDictionary.h"
 #include "com_android_inputmethod_latin_DicTraverseSession.h"
-#endif
-#include "com_android_inputmethod_latin_makedict_Ver3DictDecoder.h"
 #include "defines.h"
 
 /*
@@ -41,7 +38,6 @@ jint JNI_OnLoad(JavaVM *vm, void *reserved) {
         AKLOGE("ERROR: JNIEnv is invalid");
         return -1;
     }
-#ifndef HOST_TOOL
     if (!latinime::register_BinaryDictionary(env)) {
         AKLOGE("ERROR: BinaryDictionary native registration failed");
         return -1;
@@ -54,11 +50,6 @@ jint JNI_OnLoad(JavaVM *vm, void *reserved) {
         AKLOGE("ERROR: ProximityInfo native registration failed");
         return -1;
     }
-#endif
-    if (!latinime::register_Ver3DictDecoder(env)) {
-        AKLOGE("ERROR: Ver3DictDecoder native registration failed");
-        return -1;
-    }
     /* success -- return valid version number */
     return JNI_VERSION_1_6;
 }
diff --git a/native/jni/src/defines.h b/native/jni/src/defines.h
index 9a26fe051f..1969ebae06 100644
--- a/native/jni/src/defines.h
+++ b/native/jni/src/defines.h
@@ -87,12 +87,21 @@ AK_FORCE_INLINE static int intArrayToCharArray(const int *const source, const in
 }
 
 #if defined(FLAG_DO_PROFILE) || defined(FLAG_DBG)
+#if defined(__ANDROID__)
 #include <android/log.h>
+#endif // defined(__ANDROID__)
 #ifndef LOG_TAG
 #define LOG_TAG "LatinIME: "
 #endif // LOG_TAG
+
+#if defined(HOST_TOOL)
+#include <stdio.h>
+#define AKLOGE(fmt, ...) printf(fmt "\n", ##__VA_ARGS__)
+#define AKLOGI(fmt, ...) printf(fmt "\n", ##__VA_ARGS__)
+#else // defined(HOST_TOOL)
 #define AKLOGE(fmt, ...) __android_log_print(ANDROID_LOG_ERROR, LOG_TAG, fmt, ##__VA_ARGS__)
 #define AKLOGI(fmt, ...) __android_log_print(ANDROID_LOG_INFO, LOG_TAG, fmt, ##__VA_ARGS__)
+#endif // defined(HOST_TOOL)
 
 #define DUMP_RESULT(words, frequencies) do { dumpResult(words, frequencies); } while (0)
 #define DUMP_WORD(word, length) do { dumpWord(word, length); } while (0)
diff --git a/native/jni/src/suggest/policyimpl/dictionary/structure/v4/ver4_dict_constants.cpp b/native/jni/src/suggest/policyimpl/dictionary/structure/v4/ver4_dict_constants.cpp
index 34fecc25ff..9afb5f221e 100644
--- a/native/jni/src/suggest/policyimpl/dictionary/structure/v4/ver4_dict_constants.cpp
+++ b/native/jni/src/suggest/policyimpl/dictionary/structure/v4/ver4_dict_constants.cpp
@@ -33,8 +33,7 @@ const char *const Ver4DictConstants::SHORTCUT_CONTENT_TABLE_FILE_EXTENSION =
         ".shortcut_index_shortcut";
 
 // Version 4 dictionary size is implicitly limited to 8MB due to 3-byte offsets.
-// TODO: Make MAX_DICTIONARY_SIZE 8MB.
-const int Ver4DictConstants::MAX_DICTIONARY_SIZE = 2 * 1024 * 1024;
+const int Ver4DictConstants::MAX_DICTIONARY_SIZE = 8 * 1024 * 1024;
 // Extended region size, which is not GCed region size in dict file + additional buffer size, is
 // limited to 1MB to prevent from inefficient traversing.
 const int Ver4DictConstants::MAX_DICT_EXTENDED_REGION_SIZE = 1 * 1024 * 1024;
diff --git a/native/jni/src/suggest/policyimpl/dictionary/utils/file_utils.cpp b/native/jni/src/suggest/policyimpl/dictionary/utils/file_utils.cpp
index 1f25cfa1eb..9441a75fcb 100644
--- a/native/jni/src/suggest/policyimpl/dictionary/utils/file_utils.cpp
+++ b/native/jni/src/suggest/policyimpl/dictionary/utils/file_utils.cpp
@@ -53,6 +53,11 @@ namespace latinime {
 
 // Remove a directory and all files in the directory.
 /* static */ bool FileUtils::removeDirAndFiles(const char *const dirPath) {
+    return removeDirAndFiles(dirPath, 5 /* maxTries */);
+}
+
+// Remove a directory and all files in the directory, trying up to maxTimes.
+/* static */ bool FileUtils::removeDirAndFiles(const char *const dirPath, const int maxTries) {
     DIR *const dir = opendir(dirPath);
     if (dir == NULL) {
         AKLOGE("Cannot open dir %s.", dirPath);
@@ -60,7 +65,7 @@ namespace latinime {
     }
     struct dirent *dirent;
     while ((dirent = readdir(dir)) != NULL) {
-        if (dirent->d_type != DT_REG) {
+        if (dirent->d_type == DT_DIR) {
             continue;
         }
         const int filePathBufSize = getFilePathBufSize(dirPath, dirent->d_name);
@@ -74,8 +79,14 @@ namespace latinime {
     }
     closedir(dir);
     if (remove(dirPath) != 0) {
-        AKLOGE("Cannot remove directory %s.", dirPath);
-        return false;
+        if (maxTries > 0) {
+            // On NFS, deleting files sometimes creates new files. I'm not sure what the
+            // correct way of dealing with this is, but for the time being, this seems to work.
+            removeDirAndFiles(dirPath, maxTries - 1);
+        } else {
+            AKLOGE("Cannot remove directory %s.", dirPath);
+            return false;
+        }
     }
     return true;
 }
diff --git a/native/jni/src/suggest/policyimpl/dictionary/utils/file_utils.h b/native/jni/src/suggest/policyimpl/dictionary/utils/file_utils.h
index 3e84a3038d..4f1b93a6ab 100644
--- a/native/jni/src/suggest/policyimpl/dictionary/utils/file_utils.h
+++ b/native/jni/src/suggest/policyimpl/dictionary/utils/file_utils.h
@@ -53,6 +53,8 @@ class FileUtils {
 
  private:
     DISALLOW_IMPLICIT_CONSTRUCTORS(FileUtils);
+
+    static bool removeDirAndFiles(const char *const dirPath, const int maxTries);
 };
 } // namespace latinime
 #endif /* LATINIME_FILE_UTILS_H */
diff --git a/tests/src/com/android/inputmethod/latin/BinaryDictionaryDecayingTests.java b/tests/src/com/android/inputmethod/latin/BinaryDictionaryDecayingTests.java
index c7bc55d965..098b211d9d 100644
--- a/tests/src/com/android/inputmethod/latin/BinaryDictionaryDecayingTests.java
+++ b/tests/src/com/android/inputmethod/latin/BinaryDictionaryDecayingTests.java
@@ -119,13 +119,12 @@ public class BinaryDictionaryDecayingTests extends AndroidTestCase {
                 FormatSpec.FileHeader.ATTRIBUTE_VALUE_TRUE);
         attributeMap.put(FormatSpec.FileHeader.HAS_HISTORICAL_INFO_ATTRIBUTE,
                 FormatSpec.FileHeader.ATTRIBUTE_VALUE_TRUE);
-        final String headerFileName = file.getName() + FormatSpec.HEADER_FILE_EXTENSION;
         if (BinaryDictionary.createEmptyDictFile(file.getAbsolutePath(),
                 FormatSpec.VERSION4, attributeMap)) {
             return file;
         } else {
-            throw new IOException("Empty dictionary " + file.getAbsolutePath() + " "
-                    + headerFileName + " cannot be created.");
+            throw new IOException("Empty dictionary " + file.getAbsolutePath()
+                    + " cannot be created.");
         }
     }
 
diff --git a/tests/src/com/android/inputmethod/latin/makedict/BinaryDictIOUtilsTests.java b/tests/src/com/android/inputmethod/latin/makedict/BinaryDictIOUtilsTests.java
index cdbcc832f1..ba1b6b33ca 100644
--- a/tests/src/com/android/inputmethod/latin/makedict/BinaryDictIOUtilsTests.java
+++ b/tests/src/com/android/inputmethod/latin/makedict/BinaryDictIOUtilsTests.java
@@ -103,7 +103,7 @@ public class BinaryDictIOUtilsTests extends AndroidTestCase {
         Log.d(TAG, "    end address = " + info.mEndAddress);
     }
 
-    private static void printNode(final Ver3DictDecoder dictDecoder,
+    private static void printNode(final Ver2DictDecoder dictDecoder,
             final FormatSpec.FormatOptions formatOptions) {
         final DictBuffer dictBuffer = dictDecoder.getDictBuffer();
         Log.d(TAG, "Node at " + dictBuffer.position());
@@ -121,7 +121,7 @@ public class BinaryDictIOUtilsTests extends AndroidTestCase {
     }
 
     @SuppressWarnings("unused")
-    private static void printBinaryFile(final Ver3DictDecoder dictDecoder)
+    private static void printBinaryFile(final Ver2DictDecoder dictDecoder)
             throws IOException, UnsupportedFormatException {
         final FileHeader fileHeader = dictDecoder.readHeader();
         final DictBuffer dictBuffer = dictDecoder.getDictBuffer();
diff --git a/tests/src/com/android/inputmethod/latin/makedict/BinaryDictUtils.java b/tests/src/com/android/inputmethod/latin/makedict/BinaryDictUtils.java
index 34226dcd36..208cb99b57 100644
--- a/tests/src/com/android/inputmethod/latin/makedict/BinaryDictUtils.java
+++ b/tests/src/com/android/inputmethod/latin/makedict/BinaryDictUtils.java
@@ -65,7 +65,7 @@ public class BinaryDictUtils {
             return new Ver4DictEncoder(cacheDir);
         } else if (formatOptions.mVersion == FormatSpec.VERSION3
                 || formatOptions.mVersion == FormatSpec.VERSION2) {
-            return new Ver3DictEncoder(file);
+            return new Ver2DictEncoder(file);
         } else {
             throw new RuntimeException("The format option has a wrong version : "
                     + formatOptions.mVersion);
@@ -77,7 +77,7 @@ public class BinaryDictUtils {
         if (formatOptions.mVersion == FormatSpec.VERSION4) {
             return new Ver4DictUpdater(file, DictDecoder.USE_WRITABLE_BYTEBUFFER);
         } else if (formatOptions.mVersion == FormatSpec.VERSION3) {
-            return new Ver3DictUpdater(file, DictDecoder.USE_WRITABLE_BYTEBUFFER);
+            return new Ver2DictUpdater(file, DictDecoder.USE_WRITABLE_BYTEBUFFER);
         } else {
             throw new UnsupportedFormatException("The format option has a wrong version : "
                     + formatOptions.mVersion);
diff --git a/tests/src/com/android/inputmethod/latin/makedict/Ver3DictDecoderTests.java b/tests/src/com/android/inputmethod/latin/makedict/Ver2DictDecoderTests.java
similarity index 94%
rename from tests/src/com/android/inputmethod/latin/makedict/Ver3DictDecoderTests.java
rename to tests/src/com/android/inputmethod/latin/makedict/Ver2DictDecoderTests.java
index 9611599b92..a85753e6b5 100644
--- a/tests/src/com/android/inputmethod/latin/makedict/Ver3DictDecoderTests.java
+++ b/tests/src/com/android/inputmethod/latin/makedict/Ver2DictDecoderTests.java
@@ -32,10 +32,10 @@ import java.io.FileOutputStream;
 import java.io.IOException;
 
 /**
- * Unit tests for Ver3DictDecoder
+ * Unit tests for Ver2DictDecoder
  */
-public class Ver3DictDecoderTests extends AndroidTestCase {
-    private static final String TAG = Ver3DictDecoderTests.class.getSimpleName();
+public class Ver2DictDecoderTests extends AndroidTestCase {
+    private static final String TAG = Ver2DictDecoderTests.class.getSimpleName();
 
     private final byte[] data = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
 
@@ -68,7 +68,7 @@ public class Ver3DictDecoderTests extends AndroidTestCase {
         }
 
         assertNotNull(testFile);
-        final Ver3DictDecoder dictDecoder = new Ver3DictDecoder(testFile, factory);
+        final Ver2DictDecoder dictDecoder = new Ver2DictDecoder(testFile, factory);
         try {
             dictDecoder.openDictBuffer();
         } catch (Exception e) {
@@ -110,7 +110,7 @@ public class Ver3DictDecoderTests extends AndroidTestCase {
             Log.e(TAG, "IOException while the creating temporary file", e);
         }
 
-        final Ver3DictDecoder dictDecoder = new Ver3DictDecoder(testFile, factory);
+        final Ver2DictDecoder dictDecoder = new Ver2DictDecoder(testFile, factory);
 
         // the default return value of getBuffer() must be null.
         assertNull("the default return value of getBuffer() is not null",
diff --git a/tests/src/com/android/inputmethod/latin/utils/UserHistoryDictIOUtilsTests.java b/tests/src/com/android/inputmethod/latin/utils/UserHistoryDictIOUtilsTests.java
index fc921b4fd7..ae08b49d7b 100644
--- a/tests/src/com/android/inputmethod/latin/utils/UserHistoryDictIOUtilsTests.java
+++ b/tests/src/com/android/inputmethod/latin/utils/UserHistoryDictIOUtilsTests.java
@@ -28,8 +28,8 @@ import com.android.inputmethod.latin.makedict.FormatSpec.FileHeader;
 import com.android.inputmethod.latin.makedict.FusionDictionary;
 import com.android.inputmethod.latin.makedict.FusionDictionary.PtNode;
 import com.android.inputmethod.latin.makedict.UnsupportedFormatException;
-import com.android.inputmethod.latin.makedict.Ver3DictDecoder;
-import com.android.inputmethod.latin.makedict.Ver3DictEncoder;
+import com.android.inputmethod.latin.makedict.Ver2DictDecoder;
+import com.android.inputmethod.latin.makedict.Ver2DictEncoder;
 import com.android.inputmethod.latin.personalization.UserHistoryDictionaryBigramList;
 import com.android.inputmethod.latin.utils.UserHistoryDictIOUtils.BigramDictionaryInterface;
 import com.android.inputmethod.latin.utils.UserHistoryDictIOUtils.OnAddWordListener;
@@ -146,7 +146,7 @@ public class UserHistoryDictIOUtilsTests extends AndroidTestCase
 
     private void writeDictToFile(final File file,
             final UserHistoryDictionaryBigramList bigramList) {
-        final DictEncoder dictEncoder = new Ver3DictEncoder(file);
+        final DictEncoder dictEncoder = new Ver2DictEncoder(file);
         UserHistoryDictIOUtils.writeDictionary(dictEncoder, this, bigramList, FORMAT_OPTIONS,
                 HEADER_OPTIONS);
     }
diff --git a/tools/dicttool/Android.mk b/tools/dicttool/Android.mk
index 895e4a231d..95afb4cc10 100644
--- a/tools/dicttool/Android.mk
+++ b/tools/dicttool/Android.mk
@@ -27,11 +27,28 @@ LATINIME_BASE_SOURCE_DIRECTORY := $(LATINIME_LOCAL_DIR)/java/src/com/android/inp
 LATINIME_ANNOTATIONS_SOURCE_DIRECTORY := $(LATINIME_BASE_SOURCE_DIRECTORY)/annotations
 LATINIME_CORE_SOURCE_DIRECTORY := $(LATINIME_BASE_SOURCE_DIRECTORY)/latin
 MAKEDICT_CORE_SOURCE_DIRECTORY := $(LATINIME_CORE_SOURCE_DIRECTORY)/makedict
+
+# Dependencies for Dicttool. Most of these files are needed by BinaryDictionary.java. Note that
+# a significant part of the dependencies are mocked in the compat/ directory, with empty or
+# nearly-empty implementations, for parts that we don't use in Dicttool.
 USED_TARGETTED_UTILS := \
+        $(LATINIME_CORE_SOURCE_DIRECTORY)/BinaryDictionary.java \
+        $(LATINIME_CORE_SOURCE_DIRECTORY)/DicTraverseSession.java \
+        $(LATINIME_CORE_SOURCE_DIRECTORY)/Dictionary.java \
+        $(LATINIME_CORE_SOURCE_DIRECTORY)/InputPointers.java \
+        $(LATINIME_CORE_SOURCE_DIRECTORY)/LastComposedWord.java \
+        $(LATINIME_CORE_SOURCE_DIRECTORY)/LatinImeLogger.java \
+        $(LATINIME_CORE_SOURCE_DIRECTORY)/SuggestedWords.java \
+        $(LATINIME_CORE_SOURCE_DIRECTORY)/WordComposer.java \
+        $(LATINIME_CORE_SOURCE_DIRECTORY)/settings/NativeSuggestOptions.java \
         $(LATINIME_CORE_SOURCE_DIRECTORY)/utils/ByteArrayDictBuffer.java \
         $(LATINIME_CORE_SOURCE_DIRECTORY)/utils/CollectionUtils.java \
         $(LATINIME_CORE_SOURCE_DIRECTORY)/utils/FileUtils.java \
-        $(LATINIME_CORE_SOURCE_DIRECTORY)/utils/JniUtils.java
+        $(LATINIME_CORE_SOURCE_DIRECTORY)/utils/JniUtils.java \
+        $(LATINIME_CORE_SOURCE_DIRECTORY)/utils/LocaleUtils.java \
+        $(LATINIME_CORE_SOURCE_DIRECTORY)/utils/ResizableIntArray.java \
+        $(LATINIME_CORE_SOURCE_DIRECTORY)/utils/StringUtils.java \
+        $(LATINIME_CORE_SOURCE_DIRECTORY)/utils/UnigramProperty.java
 
 DICTTOOL_ONDEVICE_TESTS_DIRECTORY := \
         $(LATINIME_LOCAL_DIR)/tests/src/com/android/inputmethod/latin/makedict/
diff --git a/tools/dicttool/NativeLib.mk b/tools/dicttool/NativeLib.mk
index a3d3c02957..05e5841d35 100644
--- a/tools/dicttool/NativeLib.mk
+++ b/tools/dicttool/NativeLib.mk
@@ -20,12 +20,17 @@ include $(CLEAR_VARS)
 
 LATINIME_DIR_RELATIVE_TO_DICTTOOL := ../..
 
+ifeq ($(FLAG_DBG), true)
+    $(warning Making debug version of native library)
+    LOCAL_CFLAGS += -DFLAG_DBG -funwind-tables -fno-inline
+endif #FLAG_DBG
+
 ifneq ($(strip $(HOST_JDK_IS_64BIT_VERSION)),)
 LOCAL_CFLAGS += -m64
 LOCAL_LDFLAGS += -m64
 endif #HOST_JDK_IS_64BIT_VERSION
 
-LOCAL_CFLAGS += -DHOST_TOOL -fPIC
+LOCAL_CFLAGS += -DHOST_TOOL -fPIC -Wno-deprecated
 LOCAL_NO_DEFAULT_COMPILER_FLAGS := true
 
 LATINIME_NATIVE_JNI_DIR := $(LATINIME_DIR_RELATIVE_TO_DICTTOOL)/native/jni
@@ -33,11 +38,7 @@ LATINIME_NATIVE_SRC_DIR := $(LATINIME_DIR_RELATIVE_TO_DICTTOOL)/native/jni/src
 LOCAL_C_INCLUDES := $(LOCAL_PATH)/$(LATINIME_NATIVE_SRC_DIR)
 # Used in jni_common.cpp to avoid registering useless methods.
 
-LATIN_IME_JNI_SRC_FILES := \
-    com_android_inputmethod_latin_makedict_Ver3DictDecoder.cpp \
-    jni_common.cpp
-
-LATIN_IME_CORE_SRC_FILES :=
+include $(LOCAL_PATH)/$(LATINIME_NATIVE_JNI_DIR)/NativeFileList.mk
 
 LOCAL_SRC_FILES := \
     $(addprefix $(LATINIME_NATIVE_JNI_DIR)/, $(LATIN_IME_JNI_SRC_FILES)) \
@@ -48,4 +49,5 @@ LOCAL_MODULE := $(LATINIME_HOST_NATIVE_LIBNAME)
 include $(BUILD_HOST_SHARED_LIBRARY)
 
 # Clear our private variables
+include $(LOCAL_PATH)/$(LATINIME_NATIVE_JNI_DIR)/CleanupNativeFileList.mk
 LATINIME_DIR_RELATIVE_TO_DICTTOOL := ../..
diff --git a/native/jni/com_android_inputmethod_latin_makedict_Ver3DictDecoder.h b/tools/dicttool/compat/android/content/SharedPreferences.java
similarity index 67%
rename from native/jni/com_android_inputmethod_latin_makedict_Ver3DictDecoder.h
rename to tools/dicttool/compat/android/content/SharedPreferences.java
index 07e80f1d8f..cfeb1532d1 100644
--- a/native/jni/com_android_inputmethod_latin_makedict_Ver3DictDecoder.h
+++ b/tools/dicttool/compat/android/content/SharedPreferences.java
@@ -14,12 +14,10 @@
  * limitations under the License.
  */
 
-#ifndef _COM_ANDROID_INPUTMETHOD_LATIN_MAKEDICT_VER3DICTDECODER_H
-#define _COM_ANDROID_INPUTMETHOD_LATIN_MAKEDICT_VER3DICTDECODER_H
+package android.content;
 
-#include "jni.h"
-
-namespace latinime {
-int register_Ver3DictDecoder(JNIEnv *env);
-} // namespace latinime
-#endif // _COM_ANDROID_INPUTMETHOD_LATIN_MAKEDICT_VER3DICTDECODER_H
+public class SharedPreferences {
+    public interface OnSharedPreferenceChangeListener {
+        public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key);
+    }
+}
diff --git a/tools/dicttool/compat/android/graphics/Rect.java b/tools/dicttool/compat/android/graphics/Rect.java
new file mode 100644
index 0000000000..c7b61d7590
--- /dev/null
+++ b/tools/dicttool/compat/android/graphics/Rect.java
@@ -0,0 +1,20 @@
+/*
+ * Copyright (C) 2013 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 android.graphics;
+
+public class Rect {
+}
diff --git a/tools/dicttool/compat/android/text/TextUtils.java b/tools/dicttool/compat/android/text/TextUtils.java
new file mode 100644
index 0000000000..5a94b7d4cf
--- /dev/null
+++ b/tools/dicttool/compat/android/text/TextUtils.java
@@ -0,0 +1,107 @@
+/*
+ * Copyright (C) 2013 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 android.text;
+
+public class TextUtils {
+    private TextUtils() { /* cannot be instantiated */ }
+
+    /**
+     * Returns true if the string is null or 0-length.
+     * @param str the string to be examined
+     * @return true if str is null or zero length
+     */
+    public static boolean isEmpty(CharSequence str) {
+        if (str == null || str.length() == 0)
+            return true;
+        else
+            return false;
+    }
+
+    /**
+     * Returns true if a and b are equal, including if they are both null.
+     * <p><i>Note: In platform versions 1.1 and earlier, this method only worked well if
+     * both the arguments were instances of String.</i></p>
+     * @param a first CharSequence to check
+     * @param b second CharSequence to check
+     * @return true if a and b are equal
+     */
+    public static boolean equals(CharSequence a, CharSequence b) {
+        if (a == b) return true;
+        int length;
+        if (a != null && b != null && (length = a.length()) == b.length()) {
+            if (a instanceof String && b instanceof String) {
+                return a.equals(b);
+            } else {
+                for (int i = 0; i < length; i++) {
+                    if (a.charAt(i) != b.charAt(i)) return false;
+                }
+                return true;
+            }
+        }
+        return false;
+    }
+
+    /**
+     * Returns list of multiple {@link CharSequence} joined into a single
+     * {@link CharSequence} separated by localized delimiter such as ", ".
+     *
+     * @hide
+     */
+    public static CharSequence join(Iterable<CharSequence> list) {
+        final CharSequence delimiter = ", ";
+        return join(delimiter, list);
+    }
+
+    /**
+     * Returns a string containing the tokens joined by delimiters.
+     * @param tokens an array objects to be joined. Strings will be formed from
+     *     the objects by calling object.toString().
+     */
+    public static String join(CharSequence delimiter, Object[] tokens) {
+        StringBuilder sb = new StringBuilder();
+        boolean firstTime = true;
+        for (Object token: tokens) {
+            if (firstTime) {
+                firstTime = false;
+            } else {
+                sb.append(delimiter);
+            }
+            sb.append(token);
+        }
+        return sb.toString();
+    }
+
+    /**
+     * Returns a string containing the tokens joined by delimiters.
+     * @param tokens an array objects to be joined. Strings will be formed from
+     *     the objects by calling object.toString().
+     */
+    public static String join(CharSequence delimiter, Iterable tokens) {
+        StringBuilder sb = new StringBuilder();
+        boolean firstTime = true;
+        for (Object token: tokens) {
+            if (firstTime) {
+                firstTime = false;
+            } else {
+                sb.append(delimiter);
+            }
+            sb.append(token);
+        }
+        return sb.toString();
+    }
+
+}
diff --git a/tools/dicttool/compat/android/util/Log.java b/tools/dicttool/compat/android/util/Log.java
index b3b6dd847f..9410e74a2f 100644
--- a/tools/dicttool/compat/android/util/Log.java
+++ b/tools/dicttool/compat/android/util/Log.java
@@ -25,13 +25,19 @@ public class Log {
     public static void d(final String tag, final String message) {
         System.out.println(tag + " : " + message);
     }
-    public static void d(final String tag, final String message, final Throwable e) {
-        System.out.println(tag + " : " + message + " : " + e);
+    public static void d(final String tag, final String message, final Throwable t) {
+        System.out.println(tag + " : " + message + " : " + t);
     }
     public static void e(final String tag, final String message) {
         d(tag, message);
     }
-    public static void e(final String tag, final String message, final Throwable e) {
-        d(tag, message, e);
+    public static void e(final String tag, final String message, final Throwable t) {
+        d(tag, message, t);
+    }
+    public static void w(final String tag, final String message) {
+        d(tag, message);
+    }
+    public static void w(final String tag, final String message, final Throwable t) {
+        d(tag, message, t);
     }
 }
diff --git a/tools/dicttool/compat/android/view/inputmethod/CompletionInfo.java b/tools/dicttool/compat/android/view/inputmethod/CompletionInfo.java
new file mode 100644
index 0000000000..fbce72556a
--- /dev/null
+++ b/tools/dicttool/compat/android/view/inputmethod/CompletionInfo.java
@@ -0,0 +1,21 @@
+/*
+ * Copyright (C) 2013 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 android.view.inputmethod;
+
+public class CompletionInfo {
+    public final String getText() { return ""; }
+}
diff --git a/tools/dicttool/compat/android/view/inputmethod/EditorInfo.java b/tools/dicttool/compat/android/view/inputmethod/EditorInfo.java
new file mode 100644
index 0000000000..9c71181189
--- /dev/null
+++ b/tools/dicttool/compat/android/view/inputmethod/EditorInfo.java
@@ -0,0 +1,20 @@
+/*
+ * Copyright (C) 2013 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 android.view.inputmethod;
+
+public class EditorInfo {
+}
diff --git a/tools/dicttool/compat/com/android/inputmethod/keyboard/Key.java b/tools/dicttool/compat/com/android/inputmethod/keyboard/Key.java
new file mode 100644
index 0000000000..1e63bb5266
--- /dev/null
+++ b/tools/dicttool/compat/com/android/inputmethod/keyboard/Key.java
@@ -0,0 +1,24 @@
+/*
+ * Copyright (C) 2013 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;
+
+public class Key {
+    public final int getX() { return 0; }
+    public final int getY() { return 0; }
+    public final int getWidth() { return 0; }
+    public final int getHeight() { return 0; }
+}
diff --git a/tools/dicttool/compat/com/android/inputmethod/keyboard/Keyboard.java b/tools/dicttool/compat/com/android/inputmethod/keyboard/Keyboard.java
new file mode 100644
index 0000000000..61b209f4da
--- /dev/null
+++ b/tools/dicttool/compat/com/android/inputmethod/keyboard/Keyboard.java
@@ -0,0 +1,22 @@
+/*
+ * Copyright (C) 2013 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;
+
+public class Keyboard {
+    private final Key KEY = new Key();
+    public final Key getKey(final int i) { return KEY; }
+}
diff --git a/tools/dicttool/compat/com/android/inputmethod/keyboard/ProximityInfo.java b/tools/dicttool/compat/com/android/inputmethod/keyboard/ProximityInfo.java
new file mode 100644
index 0000000000..561b6637c1
--- /dev/null
+++ b/tools/dicttool/compat/com/android/inputmethod/keyboard/ProximityInfo.java
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 2013 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;
+
+public class ProximityInfo {
+    public long getNativeProximityInfo() { return 0l; }
+    private static native long setProximityInfoNative(String locale,
+            int displayWidth, int displayHeight, int gridWidth, int gridHeight,
+            int mostCommonKeyWidth, int mostCommonKeyHeight, int[] proximityCharsArray,
+            int keyCount, int[] keyXCoordinates, int[] keyYCoordinates, int[] keyWidths,
+            int[] keyHeights, int[] keyCharCodes, float[] sweetSpotCenterXs,
+            float[] sweetSpotCenterYs, float[] sweetSpotRadii);
+    private static native void releaseProximityInfoNative(long nativeProximityInfo);
+}
diff --git a/tools/dicttool/compat/com/android/inputmethod/latin/LatinIME.java b/tools/dicttool/compat/com/android/inputmethod/latin/LatinIME.java
new file mode 100644
index 0000000000..e7aa340fb0
--- /dev/null
+++ b/tools/dicttool/compat/com/android/inputmethod/latin/LatinIME.java
@@ -0,0 +1,20 @@
+/*
+ * Copyright (C) 2013 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.latin;
+
+public class LatinIME {
+}
diff --git a/tools/dicttool/compat/com/android/inputmethod/latin/settings/AdditionalFeaturesSettingUtils.java b/tools/dicttool/compat/com/android/inputmethod/latin/settings/AdditionalFeaturesSettingUtils.java
new file mode 100644
index 0000000000..6a430d57d3
--- /dev/null
+++ b/tools/dicttool/compat/com/android/inputmethod/latin/settings/AdditionalFeaturesSettingUtils.java
@@ -0,0 +1,21 @@
+/*
+ * Copyright (C) 2013 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.latin.settings;
+
+public class AdditionalFeaturesSettingUtils {
+    public static final int ADDITIONAL_FEATURES_SETTINGS_SIZE = 0;
+}
diff --git a/tools/dicttool/compat/com/android/inputmethod/latin/settings/SettingsValues.java b/tools/dicttool/compat/com/android/inputmethod/latin/settings/SettingsValues.java
new file mode 100644
index 0000000000..0a84cdeeef
--- /dev/null
+++ b/tools/dicttool/compat/com/android/inputmethod/latin/settings/SettingsValues.java
@@ -0,0 +1,23 @@
+/*
+ * Copyright (C) 2013 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.latin.settings;
+
+public class SettingsValues {
+    public boolean isWordCodePoint(final int code) {
+        return Character.isLetter(code);
+    }
+}
diff --git a/tools/dicttool/src/com/android/inputmethod/latin/dicttool/DictionaryMaker.java b/tools/dicttool/src/com/android/inputmethod/latin/dicttool/DictionaryMaker.java
index b3543a0c2c..eb419fc286 100644
--- a/tools/dicttool/src/com/android/inputmethod/latin/dicttool/DictionaryMaker.java
+++ b/tools/dicttool/src/com/android/inputmethod/latin/dicttool/DictionaryMaker.java
@@ -23,7 +23,7 @@ import com.android.inputmethod.latin.makedict.FormatSpec;
 import com.android.inputmethod.latin.makedict.FusionDictionary;
 import com.android.inputmethod.latin.makedict.MakedictLog;
 import com.android.inputmethod.latin.makedict.UnsupportedFormatException;
-import com.android.inputmethod.latin.makedict.Ver3DictEncoder;
+import com.android.inputmethod.latin.makedict.Ver2DictEncoder;
 import com.android.inputmethod.latin.makedict.Ver4DictEncoder;
 
 import java.io.BufferedWriter;
@@ -361,7 +361,7 @@ public class DictionaryMaker {
         if (version == FormatSpec.VERSION4) {
             dictEncoder = new Ver4DictEncoder(outputFile);
         } else {
-            dictEncoder = new Ver3DictEncoder(outputFile);
+            dictEncoder = new Ver2DictEncoder(outputFile);
         }
         dictEncoder.writeDictionary(dict, formatOptions);
     }
diff --git a/tools/dicttool/tests/com/android/inputmethod/latin/dicttool/BinaryDictOffdeviceUtilsTests.java b/tools/dicttool/tests/com/android/inputmethod/latin/dicttool/BinaryDictOffdeviceUtilsTests.java
index 2b0a504642..9e397f19d8 100644
--- a/tools/dicttool/tests/com/android/inputmethod/latin/dicttool/BinaryDictOffdeviceUtilsTests.java
+++ b/tools/dicttool/tests/com/android/inputmethod/latin/dicttool/BinaryDictOffdeviceUtilsTests.java
@@ -24,7 +24,7 @@ import com.android.inputmethod.latin.makedict.FusionDictionary;
 import com.android.inputmethod.latin.makedict.FusionDictionary.DictionaryOptions;
 import com.android.inputmethod.latin.makedict.FusionDictionary.PtNodeArray;
 import com.android.inputmethod.latin.makedict.UnsupportedFormatException;
-import com.android.inputmethod.latin.makedict.Ver3DictEncoder;
+import com.android.inputmethod.latin.makedict.Ver2DictEncoder;
 
 import junit.framework.TestCase;
 
@@ -58,7 +58,7 @@ public class BinaryDictOffdeviceUtilsTests extends TestCase {
                 Compress.getCompressedStream(
                         Compress.getCompressedStream(
                                 new BufferedOutputStream(new FileOutputStream(dst)))));
-        final DictEncoder dictEncoder = new Ver3DictEncoder(out);
+        final DictEncoder dictEncoder = new Ver2DictEncoder(out);
         dictEncoder.writeDictionary(dict, new FormatOptions(2, false));
 
         // Test for an actually compressed dictionary and its contents
-- 
GitLab