AutoText.java revision 9066cfe9886ac131c34d59ed0e2d287b0e3c0087
1/* 2 * Copyright (C) 2007 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of 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, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17package android.text; 18 19import android.content.res.Resources; 20import android.content.res.XmlResourceParser; 21import com.android.internal.util.XmlUtils; 22import android.view.View; 23 24import org.xmlpull.v1.XmlPullParser; 25import org.xmlpull.v1.XmlPullParserException; 26 27import java.io.IOException; 28import java.util.Locale; 29 30/** 31 * This class accesses a dictionary of corrections to frequent misspellings. 32 */ 33public class AutoText { 34 // struct trie { 35 // char c; 36 // int off; 37 // struct trie *child; 38 // struct trie *next; 39 // }; 40 41 private static final int TRIE_C = 0; 42 private static final int TRIE_OFF = 1; 43 private static final int TRIE_CHILD = 2; 44 private static final int TRIE_NEXT = 3; 45 46 private static final int TRIE_SIZEOF = 4; 47 private static final char TRIE_NULL = (char) -1; 48 private static final int TRIE_ROOT = 0; 49 50 private static final int INCREMENT = 1024; 51 52 private static final int DEFAULT = 14337; // Size of the Trie 13 Aug 2007 53 54 private static final int RIGHT = 9300; // Size of 'right' 13 Aug 2007 55 56 private static AutoText sInstance = new AutoText(Resources.getSystem()); 57 private static Object sLock = new Object(); 58 59 // TODO: 60 // 61 // Note the assumption that the destination strings total less than 62 // 64K characters and that the trie for the source side totals less 63 // than 64K chars/offsets/child pointers/next pointers. 64 // 65 // This seems very safe for English (currently 7K of destination, 66 // 14K of trie) but may need to be revisited. 67 68 private char[] mTrie; 69 private char mTrieUsed; 70 private String mText; 71 private Locale mLocale; 72 73 private AutoText(Resources resources) { 74 mLocale = resources.getConfiguration().locale; 75 init(resources); 76 } 77 78 /** 79 * Retrieves a possible spelling correction for the specified range 80 * of text. Returns null if no correction can be found. 81 * The View is used to get the current Locale and Resources. 82 */ 83 public static String get(CharSequence src, final int start, final int end, 84 View view) { 85 Resources res = view.getContext().getResources(); 86 Locale locale = res.getConfiguration().locale; 87 AutoText instance; 88 89 synchronized (sLock) { 90 instance = sInstance; 91 92 if (!locale.equals(instance.mLocale)) { 93 instance = new AutoText(res); 94 sInstance = instance; 95 } 96 } 97 98 return instance.lookup(src, start, end); 99 } 100 101 private String lookup(CharSequence src, final int start, final int end) { 102 int here = mTrie[TRIE_ROOT]; 103 104 for (int i = start; i < end; i++) { 105 char c = src.charAt(i); 106 107 for (; here != TRIE_NULL; here = mTrie[here + TRIE_NEXT]) { 108 if (c == mTrie[here + TRIE_C]) { 109 if ((i == end - 1) 110 && (mTrie[here + TRIE_OFF] != TRIE_NULL)) { 111 int off = mTrie[here + TRIE_OFF]; 112 int len = mText.charAt(off); 113 114 return mText.substring(off + 1, off + 1 + len); 115 } 116 117 here = mTrie[here + TRIE_CHILD]; 118 break; 119 } 120 } 121 122 if (here == TRIE_NULL) { 123 return null; 124 } 125 } 126 127 return null; 128 } 129 130 private void init(Resources r) { 131 XmlResourceParser parser = r.getXml(com.android.internal.R.xml.autotext); 132 133 StringBuilder right = new StringBuilder(RIGHT); 134 mTrie = new char[DEFAULT]; 135 mTrie[TRIE_ROOT] = TRIE_NULL; 136 mTrieUsed = TRIE_ROOT + 1; 137 138 try { 139 XmlUtils.beginDocument(parser, "words"); 140 String odest = ""; 141 char ooff = 0; 142 143 while (true) { 144 XmlUtils.nextElement(parser); 145 146 String element = parser.getName(); 147 if (element == null || !(element.equals("word"))) { 148 break; 149 } 150 151 String src = parser.getAttributeValue(null, "src"); 152 if (parser.next() == XmlPullParser.TEXT) { 153 String dest = parser.getText(); 154 char off; 155 156 if (dest.equals(odest)) { 157 off = ooff; 158 } else { 159 off = (char) right.length(); 160 right.append((char) dest.length()); 161 right.append(dest); 162 } 163 164 add(src, off); 165 } 166 } 167 168 // Don't let Resources cache a copy of all these strings. 169 r.flushLayoutCache(); 170 } catch (XmlPullParserException e) { 171 throw new RuntimeException(e); 172 } catch (IOException e) { 173 throw new RuntimeException(e); 174 } finally { 175 parser.close(); 176 } 177 178 mText = right.toString(); 179 } 180 181 private void add(String src, char off) { 182 int slen = src.length(); 183 int herep = TRIE_ROOT; 184 185 for (int i = 0; i < slen; i++) { 186 char c = src.charAt(i); 187 boolean found = false; 188 189 for (; mTrie[herep] != TRIE_NULL; 190 herep = mTrie[herep] + TRIE_NEXT) { 191 if (c == mTrie[mTrie[herep] + TRIE_C]) { 192 // There is a node for this letter, and this is the 193 // end, so fill in the right hand side fields. 194 195 if (i == slen - 1) { 196 mTrie[mTrie[herep] + TRIE_OFF] = off; 197 return; 198 } 199 200 // There is a node for this letter, and we need 201 // to go deeper into it to fill in the rest. 202 203 herep = mTrie[herep] + TRIE_CHILD; 204 found = true; 205 break; 206 } 207 } 208 209 if (!found) { 210 // No node for this letter yet. Make one. 211 212 char node = newTrieNode(); 213 mTrie[herep] = node; 214 215 mTrie[mTrie[herep] + TRIE_C] = c; 216 mTrie[mTrie[herep] + TRIE_OFF] = TRIE_NULL; 217 mTrie[mTrie[herep] + TRIE_NEXT] = TRIE_NULL; 218 mTrie[mTrie[herep] + TRIE_CHILD] = TRIE_NULL; 219 220 // If this is the end of the word, fill in the offset. 221 222 if (i == slen - 1) { 223 mTrie[mTrie[herep] + TRIE_OFF] = off; 224 return; 225 } 226 227 // Otherwise, step in deeper and go to the next letter. 228 229 herep = mTrie[herep] + TRIE_CHILD; 230 } 231 } 232 } 233 234 private char newTrieNode() { 235 if (mTrieUsed + TRIE_SIZEOF > mTrie.length) { 236 char[] copy = new char[mTrie.length + INCREMENT]; 237 System.arraycopy(mTrie, 0, copy, 0, mTrie.length); 238 mTrie = copy; 239 } 240 241 char ret = mTrieUsed; 242 mTrieUsed += TRIE_SIZEOF; 243 244 return ret; 245 } 246} 247