DictionaryPool.java revision 5f282ea9e4a4590fcbab6e27d5fca7dacbb40a6a
1/* 2 * Copyright (C) 2011 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); you may not 5 * use this file except in compliance with the License. You may obtain a copy of 6 * the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 12 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 13 * License for the specific language governing permissions and limitations under 14 * the License. 15 */ 16 17package com.android.inputmethod.latin.spellcheck; 18 19import android.util.Log; 20 21import com.android.inputmethod.keyboard.ProximityInfo; 22import com.android.inputmethod.latin.CollectionUtils; 23import com.android.inputmethod.latin.Dictionary; 24import com.android.inputmethod.latin.SuggestedWords.SuggestedWordInfo; 25import com.android.inputmethod.latin.WordComposer; 26 27import java.util.ArrayList; 28import java.util.Locale; 29import java.util.concurrent.LinkedBlockingQueue; 30import java.util.concurrent.TimeUnit; 31 32/** 33 * A blocking queue that creates dictionaries up to a certain limit as necessary. 34 * As a deadlock-detecting device, if waiting for more than TIMEOUT = 3 seconds, we 35 * will clear the queue and generate its contents again. This is transparent for 36 * the client code, but may help with sloppy clients. 37 */ 38@SuppressWarnings("serial") 39public class DictionaryPool extends LinkedBlockingQueue<DictAndProximity> { 40 private final static String TAG = DictionaryPool.class.getSimpleName(); 41 // How many seconds we wait for a dictionary to become available. Past this delay, we give up in 42 // fear some bug caused a deadlock, and reset the whole pool. 43 private final static int TIMEOUT = 3; 44 private final AndroidSpellCheckerService mService; 45 private final int mMaxSize; 46 private final Locale mLocale; 47 private int mSize; 48 private volatile boolean mClosed; 49 final static ArrayList<SuggestedWordInfo> noSuggestions = CollectionUtils.newArrayList(); 50 private final static DictAndProximity dummyDict = new DictAndProximity( 51 new Dictionary(Dictionary.TYPE_MAIN) { 52 @Override 53 public ArrayList<SuggestedWordInfo> getSuggestions(final WordComposer composer, 54 final CharSequence prevWord, final ProximityInfo proximityInfo) { 55 return noSuggestions; 56 } 57 @Override 58 public boolean isValidWord(CharSequence word) { 59 // This is never called. However if for some strange reason it ever gets 60 // called, returning true is less destructive (it will not underline the 61 // word in red). 62 return true; 63 } 64 }, null); 65 66 static public boolean isAValidDictionary(final DictAndProximity dictInfo) { 67 return null != dictInfo && dummyDict != dictInfo; 68 } 69 70 public DictionaryPool(final int maxSize, final AndroidSpellCheckerService service, 71 final Locale locale) { 72 super(); 73 mMaxSize = maxSize; 74 mService = service; 75 mLocale = locale; 76 mSize = 0; 77 mClosed = false; 78 } 79 80 @Override 81 public DictAndProximity poll(final long timeout, final TimeUnit unit) 82 throws InterruptedException { 83 final DictAndProximity dict = poll(); 84 if (null != dict) return dict; 85 synchronized(this) { 86 if (mSize >= mMaxSize) { 87 // Our pool is already full. Wait until some dictionary is ready, or TIMEOUT 88 // expires to avoid a deadlock. 89 final DictAndProximity result = super.poll(timeout, unit); 90 if (null == result) { 91 Log.e(TAG, "Deadlock detected ! Resetting dictionary pool"); 92 clear(); 93 mSize = 1; 94 return mService.createDictAndProximity(mLocale); 95 } else { 96 return result; 97 } 98 } else { 99 ++mSize; 100 return mService.createDictAndProximity(mLocale); 101 } 102 } 103 } 104 105 // Convenience method 106 public DictAndProximity pollWithDefaultTimeout() { 107 try { 108 return poll(TIMEOUT, TimeUnit.SECONDS); 109 } catch (InterruptedException e) { 110 return null; 111 } 112 } 113 114 public void close() { 115 synchronized(this) { 116 mClosed = true; 117 for (DictAndProximity dict : this) { 118 dict.mDictionary.close(); 119 } 120 clear(); 121 } 122 } 123 124 @Override 125 public boolean offer(final DictAndProximity dict) { 126 if (mClosed) { 127 dict.mDictionary.close(); 128 return super.offer(dummyDict); 129 } else { 130 return super.offer(dict); 131 } 132 } 133} 134