DictionaryPool.java revision 653dbfb8ed4dfa17b2d10865a04185b1e15f4e43
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.Dictionary; 23import com.android.inputmethod.latin.SuggestedWords.SuggestedWordInfo; 24import com.android.inputmethod.latin.WordComposer; 25 26import java.util.ArrayList; 27import java.util.Locale; 28import java.util.concurrent.LinkedBlockingQueue; 29import java.util.concurrent.TimeUnit; 30 31/** 32 * A blocking queue that creates dictionaries up to a certain limit as necessary. 33 * As a deadlock-detecting device, if waiting for more than TIMEOUT = 3 seconds, we 34 * will clear the queue and generate its contents again. This is transparent for 35 * the client code, but may help with sloppy clients. 36 */ 37@SuppressWarnings("serial") 38public class DictionaryPool extends LinkedBlockingQueue<DictAndProximity> { 39 private final static String TAG = DictionaryPool.class.getSimpleName(); 40 // How many seconds we wait for a dictionary to become available. Past this delay, we give up in 41 // fear some bug caused a deadlock, and reset the whole pool. 42 private final static int TIMEOUT = 3; 43 private final AndroidSpellCheckerService mService; 44 private final int mMaxSize; 45 private final Locale mLocale; 46 private int mSize; 47 private volatile boolean mClosed; 48 final static ArrayList<SuggestedWordInfo> noSuggestions = new ArrayList<SuggestedWordInfo>(); 49 private final static DictAndProximity dummyDict = new DictAndProximity( 50 new Dictionary(Dictionary.TYPE_MAIN) { 51 @Override 52 public ArrayList<SuggestedWordInfo> getSuggestions(final WordComposer composer, 53 final CharSequence prevWord, final ProximityInfo proximityInfo) { 54 return noSuggestions; 55 } 56 @Override 57 public boolean isValidWord(CharSequence word) { 58 // This is never called. However if for some strange reason it ever gets 59 // called, returning true is less destructive (it will not underline the 60 // word in red). 61 return true; 62 } 63 }, null); 64 65 static public boolean isAValidDictionary(final DictAndProximity dictInfo) { 66 return null != dictInfo && dummyDict != dictInfo; 67 } 68 69 public DictionaryPool(final int maxSize, final AndroidSpellCheckerService service, 70 final Locale locale) { 71 super(); 72 mMaxSize = maxSize; 73 mService = service; 74 mLocale = locale; 75 mSize = 0; 76 mClosed = false; 77 } 78 79 @Override 80 public DictAndProximity poll(final long timeout, final TimeUnit unit) 81 throws InterruptedException { 82 final DictAndProximity dict = poll(); 83 if (null != dict) return dict; 84 synchronized(this) { 85 if (mSize >= mMaxSize) { 86 // Our pool is already full. Wait until some dictionary is ready, or TIMEOUT 87 // expires to avoid a deadlock. 88 final DictAndProximity result = super.poll(timeout, unit); 89 if (null == result) { 90 Log.e(TAG, "Deadlock detected ! Resetting dictionary pool"); 91 clear(); 92 mSize = 1; 93 return mService.createDictAndProximity(mLocale); 94 } else { 95 return result; 96 } 97 } else { 98 ++mSize; 99 return mService.createDictAndProximity(mLocale); 100 } 101 } 102 } 103 104 // Convenience method 105 public DictAndProximity pollWithDefaultTimeout() { 106 try { 107 return poll(TIMEOUT, TimeUnit.SECONDS); 108 } catch (InterruptedException e) { 109 return null; 110 } 111 } 112 113 public void close() { 114 synchronized(this) { 115 mClosed = true; 116 for (DictAndProximity dict : this) { 117 dict.mDictionary.close(); 118 } 119 clear(); 120 } 121 } 122 123 @Override 124 public boolean offer(final DictAndProximity dict) { 125 if (mClosed) { 126 dict.mDictionary.close(); 127 return super.offer(dummyDict); 128 } else { 129 return super.offer(dict); 130 } 131 } 132} 133