AndroidSpellCheckerService.java revision 3234123fba901243990972158d023a5d1c273316
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.content.res.Resources; 20import android.service.textservice.SpellCheckerService; 21import android.util.Log; 22import android.view.textservice.SuggestionsInfo; 23import android.view.textservice.TextInfo; 24 25import com.android.inputmethod.compat.ArraysCompatUtils; 26import com.android.inputmethod.keyboard.Key; 27import com.android.inputmethod.keyboard.ProximityInfo; 28import com.android.inputmethod.latin.Dictionary; 29import com.android.inputmethod.latin.Dictionary.DataType; 30import com.android.inputmethod.latin.Dictionary.WordCallback; 31import com.android.inputmethod.latin.DictionaryFactory; 32import com.android.inputmethod.latin.Utils; 33import com.android.inputmethod.latin.WordComposer; 34 35import java.util.Collections; 36import java.util.List; 37import java.util.LinkedList; 38import java.util.Locale; 39import java.util.Map; 40import java.util.TreeMap; 41 42/** 43 * Service for spell checking, using LatinIME's dictionaries and mechanisms. 44 */ 45public class AndroidSpellCheckerService extends SpellCheckerService { 46 private static final String TAG = AndroidSpellCheckerService.class.getSimpleName(); 47 private static final boolean DBG = true; 48 49 private final static String[] emptyArray = new String[0]; 50 private final ProximityInfo mProximityInfo = ProximityInfo.getDummyProximityInfo(); 51 private final Map<String, Dictionary> mDictionaries = 52 Collections.synchronizedMap(new TreeMap<String, Dictionary>()); 53 54 private static class SuggestionsGatherer implements WordCallback { 55 private final int DEFAULT_SUGGESTION_LENGTH = 16; 56 private final String[] mSuggestions; 57 private final int[] mScores; 58 private final int mMaxLength; 59 private int mLength = 0; 60 61 SuggestionsGatherer(final int maxLength) { 62 mMaxLength = maxLength; 63 mSuggestions = new String[mMaxLength]; 64 mScores = new int[mMaxLength]; 65 } 66 67 @Override 68 synchronized public boolean addWord(char[] word, int wordOffset, int wordLength, int score, 69 int dicTypeId, DataType dataType) { 70 final int positionIndex = ArraysCompatUtils.binarySearch(mScores, 0, mLength, score); 71 // binarySearch returns the index if the element exists, and -<insertion index> - 1 72 // if it doesn't. See documentation for binarySearch. 73 final int insertIndex = positionIndex >= 0 ? positionIndex : -positionIndex - 1; 74 75 if (mLength < mMaxLength) { 76 final int copyLen = mLength - insertIndex; 77 ++mLength; 78 System.arraycopy(mScores, insertIndex, mScores, insertIndex + 1, copyLen); 79 System.arraycopy(mSuggestions, insertIndex, mSuggestions, insertIndex + 1, copyLen); 80 } else { 81 if (insertIndex == 0) return true; 82 System.arraycopy(mScores, 1, mScores, 0, insertIndex); 83 System.arraycopy(mSuggestions, 1, mSuggestions, 0, insertIndex); 84 } 85 mScores[insertIndex] = score; 86 mSuggestions[insertIndex] = new String(word, wordOffset, wordLength); 87 88 return true; 89 } 90 91 public String[] getGatheredSuggestions() { 92 if (0 == mLength) return null; 93 94 final String[] results = new String[mLength]; 95 for (int i = mLength - 1; i >= 0; --i) { 96 results[mLength - i - 1] = mSuggestions[i]; 97 } 98 return results; 99 } 100 } 101 102 private Dictionary getDictionary(final String locale) { 103 Dictionary dictionary = mDictionaries.get(locale); 104 if (null == dictionary) { 105 final Resources resources = getResources(); 106 final int fallbackResourceId = Utils.getMainDictionaryResourceId(resources); 107 final Locale localeObject = Utils.constructLocaleFromString(locale); 108 dictionary = DictionaryFactory.createDictionaryFromManager(this, localeObject, 109 fallbackResourceId); 110 mDictionaries.put(locale, dictionary); 111 } 112 return dictionary; 113 } 114 115 // Note : this must be reentrant 116 /** 117 * Gets a list of suggestions for a specific string. 118 * 119 * This returns a list of possible corrections for the text passed as an 120 * arguments. It may split or group words, and even perform grammatical 121 * analysis. 122 */ 123 @Override 124 public SuggestionsInfo getSuggestions(final TextInfo textInfo, final int suggestionsLimit, 125 final String locale) { 126 final Dictionary dictionary = getDictionary(locale); 127 final String text = textInfo.getText(); 128 129 final SuggestionsGatherer suggestionsGatherer = new SuggestionsGatherer(suggestionsLimit); 130 final WordComposer composer = new WordComposer(); 131 final int length = text.length(); 132 for (int i = 0; i < length; ++i) { 133 int character = text.codePointAt(i); 134 composer.add(character, new int[] { character }, 135 WordComposer.NOT_A_COORDINATE, WordComposer.NOT_A_COORDINATE); 136 } 137 dictionary.getWords(composer, suggestionsGatherer, mProximityInfo); 138 final boolean isInDict = dictionary.isValidWord(text); 139 final String[] suggestions = suggestionsGatherer.getGatheredSuggestions(); 140 141 final int flags = (isInDict ? SuggestionsInfo.RESULT_ATTR_IN_THE_DICTIONARY : 0) 142 | (null != suggestions ? SuggestionsInfo.RESULT_ATTR_LOOKS_LIKE_TYPO : 0); 143 return new SuggestionsInfo(flags, suggestions); 144 } 145} 146