Merge "Use sorted keys index as virtual view id"
diff --git a/java/src/com/android/inputmethod/latin/BinaryDictionary.java b/java/src/com/android/inputmethod/latin/BinaryDictionary.java
index 38359fc..b88509f 100644
--- a/java/src/com/android/inputmethod/latin/BinaryDictionary.java
+++ b/java/src/com/android/inputmethod/latin/BinaryDictionary.java
@@ -41,6 +41,7 @@
 import java.util.Arrays;
 import java.util.HashMap;
 import java.util.Locale;
+import java.util.Map;
 
 /**
  * Implements a static, compacted, binary dictionary of standard words.
@@ -123,8 +124,7 @@
     }
 
     /**
-     * Constructor for the binary dictionary. This is supposed to be called from the
-     * dictionary factory.
+     * Constructs binary dictionary using existing dictionary file.
      * @param filename the name of the file to read through native code.
      * @param offset the offset of the dictionary data within the file.
      * @param length the length of the binary data.
@@ -145,12 +145,46 @@
         loadDictionary(filename, offset, length, isUpdatable);
     }
 
+    /**
+     * Constructs binary dictionary on memory.
+     * @param filename the name of the file used to flush.
+     * @param useFullEditDistance whether to use the full edit distance in suggestions
+     * @param dictType the dictionary type, as a human-readable string
+     * @param formatVersion the format version of the dictionary
+     * @param attributeMap the attributes of the dictionary
+     */
+    @UsedForTesting
+    public BinaryDictionary(final String filename, final boolean useFullEditDistance,
+            final Locale locale, final String dictType, final long formatVersion,
+            final Map<String, String> attributeMap) {
+        super(dictType);
+        mLocale = locale;
+        mDictSize = 0;
+        mDictFilePath = filename;
+        // On memory dictionary is always updatable.
+        mIsUpdatable = true;
+        mHasUpdated = false;
+        mNativeSuggestOptions.setUseFullEditDistance(useFullEditDistance);
+        final String[] keyArray = new String[attributeMap.size()];
+        final String[] valueArray = new String[attributeMap.size()];
+        int index = 0;
+        for (final String key : attributeMap.keySet()) {
+            keyArray[index] = key;
+            valueArray[index] = attributeMap.get(key);
+            index++;
+        }
+        mNativeDict = createOnMemoryNative(formatVersion, locale.toString(), keyArray, valueArray);
+    }
+
+
     static {
         JniUtils.loadNativeLibrary();
     }
 
     private static native long openNative(String sourceDir, long dictOffset, long dictSize,
             boolean isUpdatable);
+    private static native long createOnMemoryNative(long formatVersion,
+            String locale, String[] attributeKeyStringArray, String[] attributeValueStringArray);
     private static native void getHeaderInfoNative(long dict, int[] outHeaderSize,
             int[] outFormatVersion, ArrayList<int[]> outAttributeKeys,
             ArrayList<int[]> outAttributeValues);
diff --git a/java/src/com/android/inputmethod/latin/utils/RunInLocale.java b/java/src/com/android/inputmethod/latin/utils/RunInLocale.java
index 3c632bb..1ea16e6 100644
--- a/java/src/com/android/inputmethod/latin/utils/RunInLocale.java
+++ b/java/src/com/android/inputmethod/latin/utils/RunInLocale.java
@@ -35,18 +35,18 @@
      */
     public T runInLocale(final Resources res, final Locale newLocale) {
         synchronized (sLockForRunInLocale) {
-            final Configuration savedConf = res.getConfiguration();
-            if (newLocale == null || newLocale.equals(savedConf.locale)) {
+            final Configuration conf = res.getConfiguration();
+            if (newLocale == null || newLocale.equals(conf.locale)) {
                 return job(res);
             }
-            final Configuration newConf = new Configuration();
-            newConf.setTo(savedConf);
-            newConf.setLocale(newLocale);
+            final Locale savedLocale = conf.locale;
             try {
-                res.updateConfiguration(newConf, null);
+                conf.locale = newLocale;
+                res.updateConfiguration(conf, null);
                 return job(res);
             } finally {
-                res.updateConfiguration(savedConf, null);
+                conf.locale = savedLocale;
+                res.updateConfiguration(conf, null);
             }
         }
     }
diff --git a/native/jni/com_android_inputmethod_latin_BinaryDictionary.cpp b/native/jni/com_android_inputmethod_latin_BinaryDictionary.cpp
index 0cb6614..9016cae 100644
--- a/native/jni/com_android_inputmethod_latin_BinaryDictionary.cpp
+++ b/native/jni/com_android_inputmethod_latin_BinaryDictionary.cpp
@@ -51,7 +51,7 @@
     env->GetStringUTFRegion(sourceDir, 0, env->GetStringLength(sourceDir), sourceDirChars);
     sourceDirChars[sourceDirUtf8Length] = '\0';
     DictionaryStructureWithBufferPolicy::StructurePolicyPtr dictionaryStructureWithBufferPolicy(
-            DictionaryStructureWithBufferPolicyFactory::newDictionaryStructureWithBufferPolicy(
+            DictionaryStructureWithBufferPolicyFactory::newPolicyForExistingDictFile(
                     sourceDirChars, static_cast<int>(dictOffset), static_cast<int>(dictSize),
                     isUpdatable == JNI_TRUE));
     if (!dictionaryStructureWithBufferPolicy) {
@@ -65,6 +65,34 @@
     return reinterpret_cast<jlong>(dictionary);
 }
 
+static jlong latinime_BinaryDictionary_createOnMemory(JNIEnv *env, jclass clazz,
+        jlong formatVersion, jstring locale, jobjectArray attributeKeyStringArray,
+        jobjectArray attributeValueStringArray) {
+    const jsize localeUtf8Length = env->GetStringUTFLength(locale);
+    char localeChars[localeUtf8Length + 1];
+    env->GetStringUTFRegion(locale, 0, env->GetStringLength(locale), localeChars);
+    localeChars[localeUtf8Length] = '\0';
+    std::vector<int> localeCodePoints;
+    HeaderReadWriteUtils::insertCharactersIntoVector(localeChars, &localeCodePoints);
+    const int keyCount = env->GetArrayLength(attributeKeyStringArray);
+    const int valueCount = env->GetArrayLength(attributeValueStringArray);
+    if (keyCount != valueCount) {
+        return false;
+    }
+    DictionaryHeaderStructurePolicy::AttributeMap attributeMap =
+            JniDataUtils::constructAttributeMap(env, attributeKeyStringArray,
+                    attributeValueStringArray);
+    DictionaryStructureWithBufferPolicy::StructurePolicyPtr dictionaryStructureWithBufferPolicy =
+            DictionaryStructureWithBufferPolicyFactory::newPolicyForOnMemoryDict(
+                    formatVersion, localeCodePoints, &attributeMap);
+    if (!dictionaryStructureWithBufferPolicy) {
+        return 0;
+    }
+    Dictionary *const dictionary =
+            new Dictionary(env, std::move(dictionaryStructureWithBufferPolicy));
+    return reinterpret_cast<jlong>(dictionary);
+}
+
 static void latinime_BinaryDictionary_flush(JNIEnv *env, jclass clazz, jlong dict,
         jstring filePath) {
     Dictionary *dictionary = reinterpret_cast<Dictionary *>(dict);
@@ -468,6 +496,11 @@
         reinterpret_cast<void *>(latinime_BinaryDictionary_open)
     },
     {
+        const_cast<char *>("createOnMemoryNative"),
+        const_cast<char *>("(JLjava/lang/String;[Ljava/lang/String;[Ljava/lang/String;)J"),
+        reinterpret_cast<void *>(latinime_BinaryDictionary_createOnMemory)
+    },
+    {
         const_cast<char *>("closeNative"),
         const_cast<char *>("(J)V"),
         reinterpret_cast<void *>(latinime_BinaryDictionary_close)
diff --git a/native/jni/com_android_inputmethod_latin_BinaryDictionaryUtils.cpp b/native/jni/com_android_inputmethod_latin_BinaryDictionaryUtils.cpp
index f723664..0a34b78 100644
--- a/native/jni/com_android_inputmethod_latin_BinaryDictionaryUtils.cpp
+++ b/native/jni/com_android_inputmethod_latin_BinaryDictionaryUtils.cpp
@@ -24,6 +24,7 @@
 #include "suggest/policyimpl/dictionary/utils/dict_file_writing_utils.h"
 #include "utils/autocorrection_threshold_utils.h"
 #include "utils/char_utils.h"
+#include "utils/jni_data_utils.h"
 #include "utils/time_keeper.h"
 
 namespace latinime {
@@ -35,39 +36,24 @@
     char filePathChars[filePathUtf8Length + 1];
     env->GetStringUTFRegion(filePath, 0, env->GetStringLength(filePath), filePathChars);
     filePathChars[filePathUtf8Length] = '\0';
-    jsize localeLength = env->GetStringLength(locale);
-    jchar localeCodePoints[localeLength];
-    env->GetStringRegion(locale, 0, localeLength, localeCodePoints);
+
+    const jsize localeUtf8Length = env->GetStringUTFLength(locale);
+    char localeChars[localeUtf8Length + 1];
+    env->GetStringUTFRegion(locale, 0, env->GetStringLength(locale), localeChars);
+    localeChars[localeUtf8Length] = '\0';
+    std::vector<int> localeCodePoints;
+    HeaderReadWriteUtils::insertCharactersIntoVector(localeChars, &localeCodePoints);
+
     const int keyCount = env->GetArrayLength(attributeKeyStringArray);
     const int valueCount = env->GetArrayLength(attributeValueStringArray);
     if (keyCount != valueCount) {
         return false;
     }
-
-    DictionaryHeaderStructurePolicy::AttributeMap attributeMap;
-    for (int i = 0; i < keyCount; i++) {
-        jstring keyString = static_cast<jstring>(
-                env->GetObjectArrayElement(attributeKeyStringArray, i));
-        const jsize keyUtf8Length = env->GetStringUTFLength(keyString);
-        char keyChars[keyUtf8Length + 1];
-        env->GetStringUTFRegion(keyString, 0, env->GetStringLength(keyString), keyChars);
-        keyChars[keyUtf8Length] = '\0';
-        DictionaryHeaderStructurePolicy::AttributeMap::key_type key;
-        HeaderReadWriteUtils::insertCharactersIntoVector(keyChars, &key);
-
-        jstring valueString = static_cast<jstring>(
-                env->GetObjectArrayElement(attributeValueStringArray, i));
-        const jsize valueUtf8Length = env->GetStringUTFLength(valueString);
-        char valueChars[valueUtf8Length + 1];
-        env->GetStringUTFRegion(valueString, 0, env->GetStringLength(valueString), valueChars);
-        valueChars[valueUtf8Length] = '\0';
-        DictionaryHeaderStructurePolicy::AttributeMap::mapped_type value;
-        HeaderReadWriteUtils::insertCharactersIntoVector(valueChars, &value);
-        attributeMap[key] = value;
-    }
-
+    DictionaryHeaderStructurePolicy::AttributeMap attributeMap =
+            JniDataUtils::constructAttributeMap(env, attributeKeyStringArray,
+                    attributeValueStringArray);
     return DictFileWritingUtils::createEmptyDictFile(filePathChars, static_cast<int>(dictVersion),
-            CharUtils::convertShortArrayToIntVector(localeCodePoints, localeLength), &attributeMap);
+            localeCodePoints, &attributeMap);
 }
 
 static jfloat latinime_BinaryDictionaryUtils_calcNormalizedScore(JNIEnv *env, jclass clazz,
diff --git a/native/jni/src/suggest/policyimpl/dictionary/header/header_policy.h b/native/jni/src/suggest/policyimpl/dictionary/header/header_policy.h
index f950cad..251a719 100644
--- a/native/jni/src/suggest/policyimpl/dictionary/header/header_policy.h
+++ b/native/jni/src/suggest/policyimpl/dictionary/header/header_policy.h
@@ -69,7 +69,7 @@
 
     // Constructs header information using an attribute map.
     HeaderPolicy(const FormatUtils::FORMAT_VERSION dictFormatVersion,
-            const std::vector<int> locale,
+            const std::vector<int> &locale,
             const DictionaryHeaderStructurePolicy::AttributeMap *const attributeMap)
             : mDictFormatVersion(dictFormatVersion),
               mDictionaryFlags(HeaderReadWriteUtils::createAndGetDictionaryFlagsUsingAttributeMap(
@@ -99,6 +99,27 @@
               mMaxBigramCount(HeaderReadWriteUtils::readIntAttributeValue(
                       &mAttributeMap, MAX_BIGRAM_COUNT_KEY, DEFAULT_MAX_BIGRAM_COUNT)) {}
 
+    // Copy header information
+    HeaderPolicy(const HeaderPolicy *const headerPolicy)
+            : mDictFormatVersion(headerPolicy->mDictFormatVersion),
+              mDictionaryFlags(headerPolicy->mDictionaryFlags), mSize(headerPolicy->mSize),
+              mAttributeMap(headerPolicy->mAttributeMap), mLocale(headerPolicy->mLocale),
+              mMultiWordCostMultiplier(headerPolicy->mMultiWordCostMultiplier),
+              mRequiresGermanUmlautProcessing(headerPolicy->mRequiresGermanUmlautProcessing),
+              mIsDecayingDict(headerPolicy->mIsDecayingDict),
+              mDate(headerPolicy->mDate), mLastDecayedTime(headerPolicy->mLastDecayedTime),
+              mUnigramCount(headerPolicy->mUnigramCount), mBigramCount(headerPolicy->mBigramCount),
+              mExtendedRegionSize(headerPolicy->mExtendedRegionSize),
+              mHasHistoricalInfoOfWords(headerPolicy->mHasHistoricalInfoOfWords),
+              mForgettingCurveOccurrencesToLevelUp(
+                      headerPolicy->mForgettingCurveOccurrencesToLevelUp),
+              mForgettingCurveProbabilityValuesTableId(
+                      headerPolicy->mForgettingCurveProbabilityValuesTableId),
+              mForgettingCurveDurationToLevelDown(
+                      headerPolicy->mForgettingCurveDurationToLevelDown),
+              mMaxUnigramCount(headerPolicy->mMaxUnigramCount),
+              mMaxBigramCount(headerPolicy->mMaxBigramCount) {}
+
     // Temporary dummy header.
     HeaderPolicy()
             : mDictFormatVersion(FormatUtils::UNKNOWN_VERSION), mDictionaryFlags(0), mSize(0),
diff --git a/native/jni/src/suggest/policyimpl/dictionary/structure/dictionary_structure_with_buffer_policy_factory.cpp b/native/jni/src/suggest/policyimpl/dictionary/structure/dictionary_structure_with_buffer_policy_factory.cpp
index 5f19534..be7e43b 100644
--- a/native/jni/src/suggest/policyimpl/dictionary/structure/dictionary_structure_with_buffer_policy_factory.cpp
+++ b/native/jni/src/suggest/policyimpl/dictionary/structure/dictionary_structure_with_buffer_policy_factory.cpp
@@ -19,10 +19,12 @@
 #include <climits>
 
 #include "defines.h"
+#include "suggest/policyimpl/dictionary/structure/pt_common/dynamic_pt_writing_utils.h"
 #include "suggest/policyimpl/dictionary/structure/v2/patricia_trie_policy.h"
 #include "suggest/policyimpl/dictionary/structure/v4/ver4_dict_buffers.h"
 #include "suggest/policyimpl/dictionary/structure/v4/ver4_dict_constants.h"
 #include "suggest/policyimpl/dictionary/structure/v4/ver4_patricia_trie_policy.h"
+#include "suggest/policyimpl/dictionary/utils/dict_file_writing_utils.h"
 #include "suggest/policyimpl/dictionary/utils/file_utils.h"
 #include "suggest/policyimpl/dictionary/utils/format_utils.h"
 #include "suggest/policyimpl/dictionary/utils/mmapped_buffer.h"
@@ -30,24 +32,50 @@
 namespace latinime {
 
 /* static */ DictionaryStructureWithBufferPolicy::StructurePolicyPtr
-        DictionaryStructureWithBufferPolicyFactory
-                ::newDictionaryStructureWithBufferPolicy(const char *const path,
-                        const int bufOffset, const int size, const bool isUpdatable) {
+        DictionaryStructureWithBufferPolicyFactory::newPolicyForExistingDictFile(
+                const char *const path, const int bufOffset, const int size,
+                const bool isUpdatable) {
     if (FileUtils::existsDir(path)) {
         // Given path represents a directory.
-        return newPolicyforDirectoryDict(path, isUpdatable);
+        return newPolicyForDirectoryDict(path, isUpdatable);
     } else {
         if (isUpdatable) {
             AKLOGE("One file dictionaries don't support updating. path: %s", path);
             ASSERT(false);
             return DictionaryStructureWithBufferPolicy::StructurePolicyPtr(nullptr);
         }
-        return newPolicyforFileDict(path, bufOffset, size);
+        return newPolicyForFileDict(path, bufOffset, size);
     }
 }
 
 /* static */ DictionaryStructureWithBufferPolicy::StructurePolicyPtr
-        DictionaryStructureWithBufferPolicyFactory::newPolicyforDirectoryDict(
+        DictionaryStructureWithBufferPolicyFactory:: newPolicyForOnMemoryDict(
+                const int formatVersion, const std::vector<int> &locale,
+                const DictionaryHeaderStructurePolicy::AttributeMap *const attributeMap) {
+    switch (formatVersion) {
+        case FormatUtils::VERSION_4: {
+            HeaderPolicy headerPolicy(FormatUtils::VERSION_4, locale, attributeMap);
+            Ver4DictBuffers::Ver4DictBuffersPtr dictBuffers =
+                    Ver4DictBuffers::createVer4DictBuffers(&headerPolicy,
+                            Ver4DictConstants::MAX_DICT_EXTENDED_REGION_SIZE);
+            if (!DynamicPtWritingUtils::writeEmptyDictionary(
+                    dictBuffers->getWritableTrieBuffer(), 0 /* rootPos */)) {
+                AKLOGE("Empty ver4 dictionary structure cannot be created on memory.");
+                return DictionaryStructureWithBufferPolicy::StructurePolicyPtr(nullptr);
+            }
+            return DictionaryStructureWithBufferPolicy::StructurePolicyPtr(
+                    new Ver4PatriciaTriePolicy(std::move(dictBuffers)));
+        }
+        default:
+            AKLOGE("DICT: dictionary format %d is not supported for on memory dictionary",
+                    formatVersion);
+            break;
+    }
+    return DictionaryStructureWithBufferPolicy::StructurePolicyPtr(nullptr);
+}
+
+/* static */ DictionaryStructureWithBufferPolicy::StructurePolicyPtr
+        DictionaryStructureWithBufferPolicyFactory::newPolicyForDirectoryDict(
                 const char *const path, const bool isUpdatable) {
     const int headerFilePathBufSize = PATH_MAX + 1 /* terminator */;
     char headerFilePath[headerFilePathBufSize];
@@ -93,7 +121,7 @@
 }
 
 /* static */ DictionaryStructureWithBufferPolicy::StructurePolicyPtr
-        DictionaryStructureWithBufferPolicyFactory::newPolicyforFileDict(
+        DictionaryStructureWithBufferPolicyFactory::newPolicyForFileDict(
                 const char *const path, const int bufOffset, const int size) {
     // Allocated buffer in MmapedBuffer::openBuffer() will be freed in the destructor of
     // MmappedBufferPtr if the instance has the responsibility.
diff --git a/native/jni/src/suggest/policyimpl/dictionary/structure/dictionary_structure_with_buffer_policy_factory.h b/native/jni/src/suggest/policyimpl/dictionary/structure/dictionary_structure_with_buffer_policy_factory.h
index 6053b7e..f71447e 100644
--- a/native/jni/src/suggest/policyimpl/dictionary/structure/dictionary_structure_with_buffer_policy_factory.h
+++ b/native/jni/src/suggest/policyimpl/dictionary/structure/dictionary_structure_with_buffer_policy_factory.h
@@ -17,7 +17,10 @@
 #ifndef LATINIME_DICTIONARY_STRUCTURE_WITH_BUFFER_POLICY_FACTORY_H
 #define LATINIME_DICTIONARY_STRUCTURE_WITH_BUFFER_POLICY_FACTORY_H
 
+#include <vector>
+
 #include "defines.h"
+#include "suggest/core/policy/dictionary_header_structure_policy.h"
 #include "suggest/core/policy/dictionary_structure_with_buffer_policy.h"
 
 namespace latinime {
@@ -25,17 +28,22 @@
 class DictionaryStructureWithBufferPolicyFactory {
  public:
     static DictionaryStructureWithBufferPolicy::StructurePolicyPtr
-            newDictionaryStructureWithBufferPolicy(const char *const path, const int bufOffset,
+            newPolicyForExistingDictFile(const char *const path, const int bufOffset,
                     const int size, const bool isUpdatable);
 
+    static DictionaryStructureWithBufferPolicy::StructurePolicyPtr
+            newPolicyForOnMemoryDict(const int formatVersion,
+                    const std::vector<int> &locale,
+                    const DictionaryHeaderStructurePolicy::AttributeMap *const attributeMap);
+
  private:
     DISALLOW_IMPLICIT_CONSTRUCTORS(DictionaryStructureWithBufferPolicyFactory);
 
     static DictionaryStructureWithBufferPolicy::StructurePolicyPtr
-            newPolicyforDirectoryDict(const char *const path, const bool isUpdatable);
+            newPolicyForDirectoryDict(const char *const path, const bool isUpdatable);
 
     static DictionaryStructureWithBufferPolicy::StructurePolicyPtr
-            newPolicyforFileDict(const char *const path, const int bufOffset, const int size);
+            newPolicyForFileDict(const char *const path, const int bufOffset, const int size);
 
     static void getHeaderFilePathInDictDir(const char *const dirPath,
             const int outHeaderFileBufSize, char *const outHeaderFilePath);
diff --git a/native/jni/src/suggest/policyimpl/dictionary/structure/v4/ver4_dict_buffers.cpp b/native/jni/src/suggest/policyimpl/dictionary/structure/v4/ver4_dict_buffers.cpp
index eda882d..95f6544 100644
--- a/native/jni/src/suggest/policyimpl/dictionary/structure/v4/ver4_dict_buffers.cpp
+++ b/native/jni/src/suggest/policyimpl/dictionary/structure/v4/ver4_dict_buffers.cpp
@@ -131,7 +131,7 @@
           mIsUpdatable(isUpdatable) {}
 
 Ver4DictBuffers::Ver4DictBuffers(const HeaderPolicy *const headerPolicy, const int maxTrieSize)
-        : mHeaderBuffer(nullptr), mDictBuffer(nullptr), mHeaderPolicy(),
+        : mHeaderBuffer(nullptr), mDictBuffer(nullptr), mHeaderPolicy(headerPolicy),
           mExpandableHeaderBuffer(Ver4DictConstants::MAX_DICTIONARY_SIZE),
           mExpandableTrieBuffer(maxTrieSize), mTerminalPositionLookupTable(),
           mProbabilityDictContent(headerPolicy->hasHistoricalInfoOfWords()),
diff --git a/native/jni/src/utils/char_utils.h b/native/jni/src/utils/char_utils.h
index 98b8966..239419d 100644
--- a/native/jni/src/utils/char_utils.h
+++ b/native/jni/src/utils/char_utils.h
@@ -86,13 +86,6 @@
         return spaceCount;
     }
 
-    static AK_FORCE_INLINE std::vector<int> convertShortArrayToIntVector(
-            const unsigned short *const source, const int length) {
-        std::vector<int> destination;
-        destination.insert(destination.end(), source, source + length);
-        return destination; // Copies the vector
-    }
-
     static unsigned short latin_tolower(const unsigned short c);
     static const std::vector<int> EMPTY_STRING;
 
diff --git a/native/jni/src/utils/jni_data_utils.h b/native/jni/src/utils/jni_data_utils.h
index e0bbdfd..2ce02dc 100644
--- a/native/jni/src/utils/jni_data_utils.h
+++ b/native/jni/src/utils/jni_data_utils.h
@@ -21,6 +21,8 @@
 
 #include "defines.h"
 #include "jni.h"
+#include "suggest/core/policy/dictionary_header_structure_policy.h"
+#include "suggest/policyimpl/dictionary/header/header_read_write_utils.h"
 
 namespace latinime {
 
@@ -36,6 +38,33 @@
         env->GetIntArrayRegion(array, 0 /* start */, arrayLength, outVector->data());
     }
 
+    static DictionaryHeaderStructurePolicy::AttributeMap constructAttributeMap(JNIEnv *env,
+            jobjectArray attributeKeyStringArray, jobjectArray attributeValueStringArray) {
+        DictionaryHeaderStructurePolicy::AttributeMap attributeMap;
+        const int keyCount = env->GetArrayLength(attributeKeyStringArray);
+        for (int i = 0; i < keyCount; i++) {
+            jstring keyString = static_cast<jstring>(
+                    env->GetObjectArrayElement(attributeKeyStringArray, i));
+            const jsize keyUtf8Length = env->GetStringUTFLength(keyString);
+            char keyChars[keyUtf8Length + 1];
+            env->GetStringUTFRegion(keyString, 0, env->GetStringLength(keyString), keyChars);
+            keyChars[keyUtf8Length] = '\0';
+            DictionaryHeaderStructurePolicy::AttributeMap::key_type key;
+            HeaderReadWriteUtils::insertCharactersIntoVector(keyChars, &key);
+
+            jstring valueString = static_cast<jstring>(
+                    env->GetObjectArrayElement(attributeValueStringArray, i));
+            const jsize valueUtf8Length = env->GetStringUTFLength(valueString);
+            char valueChars[valueUtf8Length + 1];
+            env->GetStringUTFRegion(valueString, 0, env->GetStringLength(valueString), valueChars);
+            valueChars[valueUtf8Length] = '\0';
+            DictionaryHeaderStructurePolicy::AttributeMap::mapped_type value;
+            HeaderReadWriteUtils::insertCharactersIntoVector(valueChars, &value);
+            attributeMap[key] = value;
+        }
+        return attributeMap;
+    }
+
  private:
     DISALLOW_IMPLICIT_CONSTRUCTORS(JniDataUtils);
 };
diff --git a/tests/src/com/android/inputmethod/latin/BinaryDictionaryTests.java b/tests/src/com/android/inputmethod/latin/BinaryDictionaryTests.java
index b476627..0fb0fa5 100644
--- a/tests/src/com/android/inputmethod/latin/BinaryDictionaryTests.java
+++ b/tests/src/com/android/inputmethod/latin/BinaryDictionaryTests.java
@@ -97,6 +97,37 @@
         binaryDictionary.close();
     }
 
+    public void testConstructingDictionaryOnMemory() {
+        testConstructingDictionaryOnMemory(FormatSpec.VERSION4);
+    }
+
+    private void testConstructingDictionaryOnMemory(final int formatVersion) {
+        File dictFile = null;
+        try {
+            dictFile = createEmptyDictionaryAndGetFile("TestBinaryDictionary", formatVersion);
+        } catch (IOException e) {
+            fail("IOException while writing an initial dictionary : " + e);
+        }
+        FileUtils.deleteRecursively(dictFile);
+        assertFalse(dictFile.exists());
+        BinaryDictionary binaryDictionary = new BinaryDictionary(dictFile.getAbsolutePath(),
+                true /* useFullEditDistance */, Locale.getDefault(), TEST_LOCALE, formatVersion,
+                new HashMap<String, String>());
+        assertTrue(binaryDictionary.isValidDictionary());
+        assertEquals(formatVersion, binaryDictionary.getFormatVersion());
+        final int probability = 100;
+        addUnigramWord(binaryDictionary, "word", probability);
+        assertEquals(probability, binaryDictionary.getFrequency("word"));
+        assertFalse(dictFile.exists());
+        binaryDictionary.flush();
+        assertTrue(dictFile.exists());
+        assertTrue(binaryDictionary.isValidDictionary());
+        assertEquals(formatVersion, binaryDictionary.getFormatVersion());
+        assertEquals(probability, binaryDictionary.getFrequency("word"));
+        binaryDictionary.close();
+        dictFile.delete();
+    }
+
     public void testAddTooLongWord() {
         testAddTooLongWord(FormatSpec.VERSION4);
     }