From a562767a14c7bbac95b25e69e360fc28d6ce9e33 Mon Sep 17 00:00:00 2001 From: Jean Chalard <jchalard@google.com> Date: Tue, 16 Aug 2011 17:37:18 +0900 Subject: [PATCH] Have a pool of dictionaries to check spelling. The dictionaries and proximities are not thread-safe. In order to be able to check spelling in parallel, make a dictionary pool to call upon when a spelling check is necessary. Bug: 5156851 Change-Id: Ie3796164187dd7b7abf5ccd5d014073d43d74408 --- .../inputmethod/keyboard/ProximityInfo.java | 6 +- .../AndroidSpellCheckerService.java | 61 +++++++++++-------- .../latin/spellcheck/DictAndProximity.java | 32 ++++++++++ .../latin/spellcheck/DictionaryPool.java | 55 +++++++++++++++++ 4 files changed, 127 insertions(+), 27 deletions(-) create mode 100644 java/src/com/android/inputmethod/latin/spellcheck/DictAndProximity.java create mode 100644 java/src/com/android/inputmethod/latin/spellcheck/DictionaryPool.java diff --git a/java/src/com/android/inputmethod/keyboard/ProximityInfo.java b/java/src/com/android/inputmethod/keyboard/ProximityInfo.java index 5e73d6300e..7190b051dc 100644 --- a/java/src/com/android/inputmethod/keyboard/ProximityInfo.java +++ b/java/src/com/android/inputmethod/keyboard/ProximityInfo.java @@ -56,12 +56,12 @@ public class ProximityInfo { computeNearestNeighbors(keyWidth, keys); } - public static ProximityInfo getDummyProximityInfo() { + public static ProximityInfo createDummyProximityInfo() { return new ProximityInfo(1, 1, 1, 1, 1, Collections.<Key>emptyList()); } - public static ProximityInfo getSpellCheckerProximityInfo() { - final ProximityInfo spellCheckerProximityInfo = getDummyProximityInfo(); + public static ProximityInfo createSpellCheckerProximityInfo() { + final ProximityInfo spellCheckerProximityInfo = createDummyProximityInfo(); spellCheckerProximityInfo.mNativeProximityInfo = spellCheckerProximityInfo.setProximityInfoNative( SpellCheckerProximityInfo.ROW_SIZE, diff --git a/java/src/com/android/inputmethod/latin/spellcheck/AndroidSpellCheckerService.java b/java/src/com/android/inputmethod/latin/spellcheck/AndroidSpellCheckerService.java index d2b6bcdf20..c71841042e 100644 --- a/java/src/com/android/inputmethod/latin/spellcheck/AndroidSpellCheckerService.java +++ b/java/src/com/android/inputmethod/latin/spellcheck/AndroidSpellCheckerService.java @@ -19,6 +19,7 @@ package com.android.inputmethod.latin.spellcheck; import android.content.res.Resources; import android.service.textservice.SpellCheckerService; import android.service.textservice.SpellCheckerService.Session; +import android.util.Log; import android.view.textservice.SuggestionsInfo; import android.view.textservice.TextInfo; @@ -34,8 +35,6 @@ import com.android.inputmethod.latin.WordComposer; import java.util.Arrays; import java.util.Collections; -import java.util.List; -import java.util.LinkedList; import java.util.Locale; import java.util.Map; import java.util.TreeMap; @@ -45,12 +44,12 @@ import java.util.TreeMap; */ public class AndroidSpellCheckerService extends SpellCheckerService { private static final String TAG = AndroidSpellCheckerService.class.getSimpleName(); - private static final boolean DBG = true; + private static final boolean DBG = false; + private static final int POOL_SIZE = 2; private final static String[] emptyArray = new String[0]; - private final ProximityInfo mProximityInfo = ProximityInfo.getSpellCheckerProximityInfo(); - private final Map<String, Dictionary> mDictionaries = - Collections.synchronizedMap(new TreeMap<String, Dictionary>()); + private final Map<String, DictionaryPool> mDictionaryPools = + Collections.synchronizedMap(new TreeMap<String, DictionaryPool>()); @Override public Session createSession() { @@ -105,22 +104,32 @@ public class AndroidSpellCheckerService extends SpellCheckerService { } } - private Dictionary getDictionary(final String locale) { - Dictionary dictionary = mDictionaries.get(locale); - if (null == dictionary) { - final Resources resources = getResources(); - final int fallbackResourceId = Utils.getMainDictionaryResourceId(resources); + private DictionaryPool getDictionaryPool(final String locale) { + DictionaryPool pool = mDictionaryPools.get(locale); + if (null == pool) { final Locale localeObject = Utils.constructLocaleFromString(locale); - dictionary = DictionaryFactory.createDictionaryFromManager(this, localeObject, - fallbackResourceId); - mDictionaries.put(locale, dictionary); + pool = new DictionaryPool(POOL_SIZE, this, localeObject); + mDictionaryPools.put(locale, pool); } - return dictionary; + return pool; + } + + public DictAndProximity createDictAndProximity(final Locale locale) { + final ProximityInfo proximityInfo = ProximityInfo.createSpellCheckerProximityInfo(); + final Resources resources = getResources(); + final int fallbackResourceId = Utils.getMainDictionaryResourceId(resources); + final Dictionary dictionary = + DictionaryFactory.createDictionaryFromManager(this, locale, fallbackResourceId); + return new DictAndProximity(dictionary, proximityInfo); } private class AndroidSpellCheckerSession extends Session { + // Immutable, but need the locale which is not available in the constructor yet + DictionaryPool mDictionaryPool; + @Override public void onCreate() { + mDictionaryPool = getDictionaryPool(getLocale()); } // Note : this must be reentrant @@ -132,8 +141,6 @@ public class AndroidSpellCheckerService extends SpellCheckerService { @Override public SuggestionsInfo onGetSuggestions(final TextInfo textInfo, final int suggestionsLimit) { - final String locale = getLocale(); - final Dictionary dictionary = getDictionary(locale); final String text = textInfo.getText(); final SuggestionsGatherer suggestionsGatherer = @@ -153,15 +160,21 @@ public class AndroidSpellCheckerService extends SpellCheckerService { composer.add(character, proximities, WordComposer.NOT_A_COORDINATE, WordComposer.NOT_A_COORDINATE); } - final boolean isInDict; - final String[] suggestions; - synchronized(dictionary) { - // TODO: make the dictionary reentrant so that we don't have to synchronize here - dictionary.getWords(composer, suggestionsGatherer, mProximityInfo); - isInDict = dictionary.isValidWord(text); - suggestions = suggestionsGatherer.getGatheredSuggestions(); + + boolean isInDict = true; + try { + final DictAndProximity dictInfo = mDictionaryPool.take(); + dictInfo.mDictionary.getWords(composer, suggestionsGatherer, + dictInfo.mProximityInfo); + isInDict = dictInfo.mDictionary.isValidWord(text); + mDictionaryPool.offer(dictInfo); + } catch (InterruptedException e) { + // I don't think this can happen. + return new SuggestionsInfo(0, new String[0]); } + final String[] suggestions = suggestionsGatherer.getGatheredSuggestions(); + final int flags = (isInDict ? SuggestionsInfo.RESULT_ATTR_IN_THE_DICTIONARY : 0) | (null != suggestions diff --git a/java/src/com/android/inputmethod/latin/spellcheck/DictAndProximity.java b/java/src/com/android/inputmethod/latin/spellcheck/DictAndProximity.java new file mode 100644 index 0000000000..3dbbd40cdc --- /dev/null +++ b/java/src/com/android/inputmethod/latin/spellcheck/DictAndProximity.java @@ -0,0 +1,32 @@ +/* + * Copyright (C) 2011 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.spellcheck; + +import com.android.inputmethod.latin.Dictionary; +import com.android.inputmethod.keyboard.ProximityInfo; + +/** + * A simple container for both a Dictionary and a ProximityInfo. + */ +public class DictAndProximity { + public final Dictionary mDictionary; + public final ProximityInfo mProximityInfo; + public DictAndProximity(final Dictionary dictionary, final ProximityInfo proximityInfo) { + mDictionary = dictionary; + mProximityInfo = proximityInfo; + } +} diff --git a/java/src/com/android/inputmethod/latin/spellcheck/DictionaryPool.java b/java/src/com/android/inputmethod/latin/spellcheck/DictionaryPool.java new file mode 100644 index 0000000000..dfbfcc7f61 --- /dev/null +++ b/java/src/com/android/inputmethod/latin/spellcheck/DictionaryPool.java @@ -0,0 +1,55 @@ +/* + * Copyright (C) 2011 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.spellcheck; + +import android.content.Context; + +import java.util.Locale; +import java.util.concurrent.LinkedBlockingQueue; + +/** + * A blocking queue that creates dictionaries up to a certain limit as necessary. + */ +public class DictionaryPool extends LinkedBlockingQueue<DictAndProximity> { + private final AndroidSpellCheckerService mService; + private final int mMaxSize; + private final Locale mLocale; + private int mSize = 0; + + public DictionaryPool(final int maxSize, final AndroidSpellCheckerService service, + final Locale locale) { + super(); + mMaxSize = maxSize; + mService = service; + mLocale = locale; + } + + @Override + public DictAndProximity take() throws InterruptedException { + final DictAndProximity dict = poll(); + if (null != dict) return dict; + synchronized(this) { + if (mSize >= mMaxSize) { + // Our pool is already full. Wait until some dictionary is ready. + return super.take(); + } else { + ++mSize; + return mService.createDictAndProximity(mLocale); + } + } + } +} -- GitLab