Merge "Fix writePlacedNode."
diff --git a/java/src/com/android/inputmethod/latin/LatinIME.java b/java/src/com/android/inputmethod/latin/LatinIME.java
index d67195b..3a2de59 100644
--- a/java/src/com/android/inputmethod/latin/LatinIME.java
+++ b/java/src/com/android/inputmethod/latin/LatinIME.java
@@ -73,6 +73,7 @@
 import com.android.inputmethod.keyboard.MainKeyboardView;
 import com.android.inputmethod.latin.SuggestedWords.SuggestedWordInfo;
 import com.android.inputmethod.latin.define.ProductionFlag;
+import com.android.inputmethod.latin.personalization.PersonalizationDictionary;
 import com.android.inputmethod.latin.personalization.PersonalizationDictionaryHelper;
 import com.android.inputmethod.latin.personalization.PersonalizationDictionarySessionRegister;
 import com.android.inputmethod.latin.personalization.PersonalizationPredictionDictionary;
@@ -173,6 +174,7 @@
     private UserBinaryDictionary mUserDictionary;
     private UserHistoryPredictionDictionary mUserHistoryPredictionDictionary;
     private PersonalizationPredictionDictionary mPersonalizationPredictionDictionary;
+    private PersonalizationDictionary mPersonalizationDictionary;
     private boolean mIsUserDictionaryAvailable;
 
     private LastComposedWord mLastComposedWord = LastComposedWord.NOT_A_COMPOSED_WORD;
@@ -567,6 +569,9 @@
         mUserHistoryPredictionDictionary = PersonalizationDictionaryHelper
                 .getUserHistoryPredictionDictionary(this, localeStr, prefs);
         newSuggest.setUserHistoryPredictionDictionary(mUserHistoryPredictionDictionary);
+        mPersonalizationDictionary = PersonalizationDictionaryHelper
+                .getPersonalizationDictionary(this, localeStr, prefs);
+        newSuggest.setPersonalizationDictionary(mPersonalizationDictionary);
         mPersonalizationPredictionDictionary = PersonalizationDictionaryHelper
                 .getPersonalizationPredictionDictionary(this, localeStr, prefs);
         newSuggest.setPersonalizationPredictionDictionary(mPersonalizationPredictionDictionary);
diff --git a/java/src/com/android/inputmethod/latin/Suggest.java b/java/src/com/android/inputmethod/latin/Suggest.java
index 8766e0f..c8a151a 100644
--- a/java/src/com/android/inputmethod/latin/Suggest.java
+++ b/java/src/com/android/inputmethod/latin/Suggest.java
@@ -24,6 +24,7 @@
 import com.android.inputmethod.annotations.UsedForTesting;
 import com.android.inputmethod.keyboard.ProximityInfo;
 import com.android.inputmethod.latin.SuggestedWords.SuggestedWordInfo;
+import com.android.inputmethod.latin.personalization.PersonalizationDictionary;
 import com.android.inputmethod.latin.personalization.PersonalizationPredictionDictionary;
 import com.android.inputmethod.latin.personalization.UserHistoryPredictionDictionary;
 import com.android.inputmethod.latin.settings.Settings;
@@ -200,6 +201,12 @@
                 personalizationPredictionDictionary);
     }
 
+    public void setPersonalizationDictionary(
+            final PersonalizationDictionary personalizationDictionary) {
+        addOrReplaceDictionaryInternal(Dictionary.TYPE_PERSONALIZATION,
+                personalizationDictionary);
+    }
+
     public void setAutoCorrectionThreshold(float threshold) {
         mAutoCorrectionThreshold = threshold;
     }
diff --git a/java/src/com/android/inputmethod/latin/personalization/DynamicPredictionDictionaryBase.java b/java/src/com/android/inputmethod/latin/personalization/DynamicPredictionDictionaryBase.java
index a41b951..d046da8 100644
--- a/java/src/com/android/inputmethod/latin/personalization/DynamicPredictionDictionaryBase.java
+++ b/java/src/com/android/inputmethod/latin/personalization/DynamicPredictionDictionaryBase.java
@@ -389,7 +389,7 @@
     }
 
     public void registerUpdateSession(PersonalizationDictionaryUpdateSession session) {
-        session.setPredictionDictionary(mLocale, this);
+        session.setPredictionDictionary(this);
         mSessions.add(session);
         session.onDictionaryReady();
     }
diff --git a/java/src/com/android/inputmethod/latin/personalization/PersonalizationDictionary.java b/java/src/com/android/inputmethod/latin/personalization/PersonalizationDictionary.java
index 275ce2f..f257165 100644
--- a/java/src/com/android/inputmethod/latin/personalization/PersonalizationDictionary.java
+++ b/java/src/com/android/inputmethod/latin/personalization/PersonalizationDictionary.java
@@ -18,28 +18,32 @@
 
 import com.android.inputmethod.latin.Dictionary;
 import com.android.inputmethod.latin.ExpandableBinaryDictionary;
+import com.android.inputmethod.latin.utils.CollectionUtils;
 
 import android.content.Context;
+import android.content.SharedPreferences;
+
+import java.util.ArrayList;
 
 /**
  * This class is a dictionary for the personalized language model that uses binary dictionary.
  */
 public class PersonalizationDictionary extends ExpandableBinaryDictionary {
     private static final String NAME = "personalization";
-
-    public static void registerUpdateListener(PersonalizationDictionaryUpdateSession listener) {
-        // TODO: Implement
-    }
+    private final ArrayList<PersonalizationDictionaryUpdateSession> mSessions =
+            CollectionUtils.newArrayList();
 
     /** Locale for which this user history dictionary is storing words */
     private final String mLocale;
 
-    // Singleton
-    private PersonalizationDictionary(final Context context, final String locale) {
+    public PersonalizationDictionary(final Context context, final String locale,
+            final SharedPreferences prefs) {
         // TODO: Make isUpdatable true.
         super(context, getFilenameWithLocale(NAME, locale), Dictionary.TYPE_PERSONALIZATION,
                 false /* isUpdatable */);
         mLocale = locale;
+        // TODO: Restore last updated time
+        loadDictionary();
     }
 
     @Override
@@ -49,15 +53,21 @@
 
     @Override
     protected boolean hasContentChanged() {
-        // TODO: Implement
         return false;
     }
 
     @Override
     protected boolean needsToReloadBeforeWriting() {
-        // TODO: Implement
         return false;
     }
 
-    // TODO: Implement
+    public void registerUpdateSession(PersonalizationDictionaryUpdateSession session) {
+        session.setDictionary(this);
+        mSessions.add(session);
+        session.onDictionaryReady();
+    }
+
+    public void unRegisterUpdateSession(PersonalizationDictionaryUpdateSession session) {
+        mSessions.remove(session);
+    }
 }
diff --git a/java/src/com/android/inputmethod/latin/personalization/PersonalizationDictionaryHelper.java b/java/src/com/android/inputmethod/latin/personalization/PersonalizationDictionaryHelper.java
index b4fd250..6798f136 100644
--- a/java/src/com/android/inputmethod/latin/personalization/PersonalizationDictionaryHelper.java
+++ b/java/src/com/android/inputmethod/latin/personalization/PersonalizationDictionaryHelper.java
@@ -35,7 +35,13 @@
 
     private static final ConcurrentHashMap<String,
             SoftReference<PersonalizationPredictionDictionary>>
-                    sLangPersonalizationDictCache = CollectionUtils.newConcurrentHashMap();
+                    sLangPersonalizationPredictionDictCache =
+                            CollectionUtils.newConcurrentHashMap();
+
+    private static final ConcurrentHashMap<String,
+            SoftReference<PersonalizationDictionary>>
+                    sLangPersonalizationDictCache =
+                            CollectionUtils.newConcurrentHashMap();
 
     public static UserHistoryPredictionDictionary getUserHistoryPredictionDictionary(
             final Context context, final String locale, final SharedPreferences sp) {
@@ -60,20 +66,45 @@
     }
 
     public static void registerPersonalizationDictionaryUpdateSession(final Context context,
-            final PersonalizationDictionaryUpdateSession session) {
-        final PersonalizationPredictionDictionary dictionary =
-                getPersonalizationPredictionDictionary(context,
-                        context.getResources().getConfiguration().locale.toString(),
+            final PersonalizationDictionaryUpdateSession session, String locale) {
+        final PersonalizationPredictionDictionary predictionDictionary =
+                getPersonalizationPredictionDictionary(context, locale,
+                        PreferenceManager.getDefaultSharedPreferences(context));
+        predictionDictionary.registerUpdateSession(session);
+        final PersonalizationDictionary dictionary =
+                getPersonalizationDictionary(context, locale,
                         PreferenceManager.getDefaultSharedPreferences(context));
         dictionary.registerUpdateSession(session);
     }
 
-    public static PersonalizationPredictionDictionary getPersonalizationPredictionDictionary(
+    public static PersonalizationDictionary getPersonalizationDictionary(
             final Context context, final String locale, final SharedPreferences sp) {
         synchronized (sLangPersonalizationDictCache) {
             if (sLangPersonalizationDictCache.containsKey(locale)) {
-                final SoftReference<PersonalizationPredictionDictionary> ref =
+                final SoftReference<PersonalizationDictionary> ref =
                         sLangPersonalizationDictCache.get(locale);
+                final PersonalizationDictionary dict = ref == null ? null : ref.get();
+                if (dict != null) {
+                    if (DEBUG) {
+                        Log.w(TAG, "Use cached PersonalizationDictCache for " + locale);
+                    }
+                    return dict;
+                }
+            }
+            final PersonalizationDictionary dict =
+                    new PersonalizationDictionary(context, locale, sp);
+            sLangPersonalizationDictCache.put(
+                    locale, new SoftReference<PersonalizationDictionary>(dict));
+            return dict;
+        }
+    }
+
+    public static PersonalizationPredictionDictionary getPersonalizationPredictionDictionary(
+            final Context context, final String locale, final SharedPreferences sp) {
+        synchronized (sLangPersonalizationPredictionDictCache) {
+            if (sLangPersonalizationPredictionDictCache.containsKey(locale)) {
+                final SoftReference<PersonalizationPredictionDictionary> ref =
+                        sLangPersonalizationPredictionDictCache.get(locale);
                 final PersonalizationPredictionDictionary dict = ref == null ? null : ref.get();
                 if (dict != null) {
                     if (DEBUG) {
@@ -84,7 +115,7 @@
             }
             final PersonalizationPredictionDictionary dict =
                     new PersonalizationPredictionDictionary(context, locale, sp);
-            sLangPersonalizationDictCache.put(
+            sLangPersonalizationPredictionDictCache.put(
                     locale, new SoftReference<PersonalizationPredictionDictionary>(dict));
             return dict;
         }
diff --git a/java/src/com/android/inputmethod/latin/personalization/PersonalizationDictionaryUpdateSession.java b/java/src/com/android/inputmethod/latin/personalization/PersonalizationDictionaryUpdateSession.java
index 77f0cdb..9788049 100644
--- a/java/src/com/android/inputmethod/latin/personalization/PersonalizationDictionaryUpdateSession.java
+++ b/java/src/com/android/inputmethod/latin/personalization/PersonalizationDictionaryUpdateSession.java
@@ -16,6 +16,8 @@
 
 package com.android.inputmethod.latin.personalization;
 
+import com.android.inputmethod.latin.ExpandableBinaryDictionary;
+
 import android.content.Context;
 
 import java.lang.ref.WeakReference;
@@ -45,22 +47,41 @@
     }
 
     // TODO: Use a dynamic binary dictionary instead
+    public WeakReference<PersonalizationDictionary> mDictionary;
     public WeakReference<DynamicPredictionDictionaryBase> mPredictionDictionary;
-    public String mLocale;
+    public final String mLocale;
+    public PersonalizationDictionaryUpdateSession(String locale) {
+        mLocale = locale;
+    }
 
     public abstract void onDictionaryReady();
 
     public abstract void onDictionaryClosed(Context context);
 
-    public void setPredictionDictionary(String locale, DynamicPredictionDictionaryBase dictionary) {
+    public void setDictionary(PersonalizationDictionary dictionary) {
+        mDictionary = new WeakReference<PersonalizationDictionary>(dictionary);
+    }
+
+    public void setPredictionDictionary(DynamicPredictionDictionaryBase dictionary) {
         mPredictionDictionary = new WeakReference<DynamicPredictionDictionaryBase>(dictionary);
-        mLocale = locale;
+    }
+
+    protected PersonalizationDictionary getDictionary() {
+        return mDictionary == null ? null : mDictionary.get();
     }
 
     protected DynamicPredictionDictionaryBase getPredictionDictionary() {
         return mPredictionDictionary == null ? null : mPredictionDictionary.get();
     }
 
+    private void unsetDictionary() {
+        final PersonalizationDictionary dictionary = getDictionary();
+        if (dictionary == null) {
+            return;
+        }
+        dictionary.unRegisterUpdateSession(this);
+    }
+
     private void unsetPredictionDictionary() {
         final DynamicPredictionDictionaryBase dictionary = getPredictionDictionary();
         if (dictionary == null) {
@@ -78,6 +99,7 @@
     }
 
     public void closeSession(Context context) {
+        unsetDictionary();
         unsetPredictionDictionary();
         onDictionaryClosed(context);
     }
diff --git a/native/jni/Android.mk b/native/jni/Android.mk
index a51fe3c..bf18897 100644
--- a/native/jni/Android.mk
+++ b/native/jni/Android.mk
@@ -71,14 +71,16 @@
         header/header_policy.cpp \
         header/header_reading_utils.cpp \
         shortcut/shortcut_list_reading_utils.cpp \
-        utils/byte_array_utils.cpp \
-        utils/format_utils.cpp \
         dictionary_structure_with_buffer_policy_factory.cpp \
         dynamic_patricia_trie_node_reader.cpp \
         dynamic_patricia_trie_policy.cpp \
         dynamic_patricia_trie_reading_utils.cpp \
         patricia_trie_policy.cpp \
         patricia_trie_reading_utils.cpp) \
+    $(addprefix suggest/policyimpl/dictionary/utils/, \
+        byte_array_utils.cpp \
+        extendable_buffer.cpp \
+        format_utils.cpp) \
     suggest/policyimpl/gesture/gesture_suggest_policy_factory.cpp \
     $(addprefix suggest/policyimpl/typing/, \
         scoring_params.cpp \
diff --git a/native/jni/src/suggest/policyimpl/dictionary/dynamic_patricia_trie_policy.h b/native/jni/src/suggest/policyimpl/dictionary/dynamic_patricia_trie_policy.h
index 8ba057b..0e5920f 100644
--- a/native/jni/src/suggest/policyimpl/dictionary/dynamic_patricia_trie_policy.h
+++ b/native/jni/src/suggest/policyimpl/dictionary/dynamic_patricia_trie_policy.h
@@ -24,6 +24,7 @@
 #include "suggest/policyimpl/dictionary/bigram/bigram_list_policy.h"
 #include "suggest/policyimpl/dictionary/header/header_policy.h"
 #include "suggest/policyimpl/dictionary/shortcut/shortcut_list_policy.h"
+#include "suggest/policyimpl/dictionary/utils/extendable_buffer.h"
 #include "suggest/policyimpl/dictionary/utils/mmapped_buffer.h"
 
 namespace latinime {
@@ -34,7 +35,7 @@
 class DynamicPatriciaTriePolicy : public DictionaryStructureWithBufferPolicy {
  public:
     DynamicPatriciaTriePolicy(const MmappedBuffer *const buffer)
-            : mBuffer(buffer), mHeaderPolicy(mBuffer->getBuffer()),
+            : mBuffer(buffer), mExtendableBuffer(), mHeaderPolicy(mBuffer->getBuffer()),
               mDictRoot(mBuffer->getBuffer() + mHeaderPolicy.getSize()),
               mBigramListPolicy(mDictRoot), mShortcutListPolicy(mDictRoot) {}
 
@@ -87,8 +88,10 @@
     static const int MAX_CHILD_COUNT_TO_AVOID_INFINITE_LOOP;
 
     const MmappedBuffer *const mBuffer;
+    const ExtendableBuffer mExtendableBuffer;
     const HeaderPolicy mHeaderPolicy;
     // TODO: Consolidate mDictRoot.
+    // CAVEAT!: Be careful about array out of bound access with mDictRoot
     const uint8_t *const mDictRoot;
     const BigramListPolicy mBigramListPolicy;
     const ShortcutListPolicy mShortcutListPolicy;
diff --git a/native/jni/src/suggest/policyimpl/dictionary/utils/extendable_buffer.cpp b/native/jni/src/suggest/policyimpl/dictionary/utils/extendable_buffer.cpp
new file mode 100644
index 0000000..e55cc24
--- /dev/null
+++ b/native/jni/src/suggest/policyimpl/dictionary/utils/extendable_buffer.cpp
@@ -0,0 +1,25 @@
+/*
+ * 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.
+ */
+
+#include "suggest/policyimpl/dictionary/utils/extendable_buffer.h"
+
+namespace latinime {
+
+const size_t ExtendableBuffer::INITIAL_BUFFER_SIZE = 16 * 1024;
+const size_t ExtendableBuffer::MAX_BUFFER_SIZE = 1024 * 1024;
+const size_t ExtendableBuffer::EXTEND_BUFFER_SIZE_STEP = 16 * 1024;
+
+}
diff --git a/native/jni/src/suggest/policyimpl/dictionary/utils/extendable_buffer.h b/native/jni/src/suggest/policyimpl/dictionary/utils/extendable_buffer.h
new file mode 100644
index 0000000..d902d19
--- /dev/null
+++ b/native/jni/src/suggest/policyimpl/dictionary/utils/extendable_buffer.h
@@ -0,0 +1,70 @@
+/*
+ * 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.
+ */
+
+#ifndef LATINIME_EXTENDABLE_BUFFER_H
+#define LATINIME_EXTENDABLE_BUFFER_H
+
+#include <cstddef>
+#include <stdint.h>
+#include <vector>
+
+#include "defines.h"
+
+namespace latinime {
+
+// This is used as a buffer that can be extended for updatable dictionaries.
+class ExtendableBuffer {
+ public:
+    ExtendableBuffer() : mBuffer(INITIAL_BUFFER_SIZE), mUsedSize(0) {}
+
+   AK_FORCE_INLINE uint8_t *getBuffer() {
+        return  &mBuffer[0];
+    }
+
+    // Return if the buffer is successfully extended or not.
+   AK_FORCE_INLINE bool extendBuffer() {
+        if (mBuffer.size() + EXTEND_BUFFER_SIZE_STEP > MAX_BUFFER_SIZE) {
+            return false;
+        }
+        mBuffer.resize(mBuffer.size() + EXTEND_BUFFER_SIZE_STEP);
+        return true;
+    }
+
+    AK_FORCE_INLINE int getAllocatedSize() const {
+        return mBuffer.size();
+    }
+
+    AK_FORCE_INLINE int getUsedSize() const {
+        return mUsedSize;
+    }
+
+    AK_FORCE_INLINE void clear() {
+        mUsedSize = 0;
+        mBuffer.clear();
+    }
+
+ private:
+    DISALLOW_COPY_AND_ASSIGN(ExtendableBuffer);
+
+    static const size_t INITIAL_BUFFER_SIZE;
+    static const size_t MAX_BUFFER_SIZE;
+    static const size_t EXTEND_BUFFER_SIZE_STEP;
+
+    std::vector<uint8_t> mBuffer;
+    int mUsedSize;
+};
+}
+#endif /* LATINIME_MMAPED_BUFFER_H */