blob: 862305b260918ffc7690d3a20bf64dc99af11406 [file] [log] [blame]
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001/*
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;
Tom Taylord4a47292009-12-21 13:59:18 -080021import com.android.common.XmlUtils;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080022import 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;
Amith Yamasania565e5e2009-03-26 10:19:09 -070072 private int mSize;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080073
74 private AutoText(Resources resources) {
75 mLocale = resources.getConfiguration().locale;
76 init(resources);
77 }
78
79 /**
Amith Yamasania565e5e2009-03-26 10:19:09 -070080 * Returns the instance of AutoText. If the locale has changed, it will create a new
81 * instance of AutoText for the locale.
82 * @param view to get the resources from
83 * @return the single instance of AutoText
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080084 */
Amith Yamasania565e5e2009-03-26 10:19:09 -070085 private static AutoText getInstance(View view) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080086 Resources res = view.getContext().getResources();
87 Locale locale = res.getConfiguration().locale;
88 AutoText instance;
89
90 synchronized (sLock) {
91 instance = sInstance;
92
93 if (!locale.equals(instance.mLocale)) {
94 instance = new AutoText(res);
95 sInstance = instance;
96 }
97 }
Amith Yamasania565e5e2009-03-26 10:19:09 -070098
99 return instance;
100 }
101
102 /**
103 * Retrieves a possible spelling correction for the specified range
104 * of text. Returns null if no correction can be found.
105 * The View is used to get the current Locale and Resources.
106 */
107 public static String get(CharSequence src, final int start, final int end,
108 View view) {
109 return getInstance(view).lookup(src, start, end);
110 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800111
Amith Yamasania565e5e2009-03-26 10:19:09 -0700112 /**
113 * Returns the size of the auto text dictionary. The return value can be zero if there is
114 * no auto correction data available for the current locale.
115 * @param view used to retrieve the current Locale and Resources.
116 * @return the number of entries in the auto text dictionary
117 */
118 public static int getSize(View view) {
119
120 return getInstance(view).getSize();
121 }
122
123 /**
124 * Returns the size of the dictionary.
125 */
126 private int getSize() {
127 return mSize;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800128 }
129
130 private String lookup(CharSequence src, final int start, final int end) {
131 int here = mTrie[TRIE_ROOT];
132
133 for (int i = start; i < end; i++) {
134 char c = src.charAt(i);
135
136 for (; here != TRIE_NULL; here = mTrie[here + TRIE_NEXT]) {
137 if (c == mTrie[here + TRIE_C]) {
138 if ((i == end - 1)
139 && (mTrie[here + TRIE_OFF] != TRIE_NULL)) {
140 int off = mTrie[here + TRIE_OFF];
141 int len = mText.charAt(off);
142
143 return mText.substring(off + 1, off + 1 + len);
144 }
145
146 here = mTrie[here + TRIE_CHILD];
147 break;
148 }
149 }
150
151 if (here == TRIE_NULL) {
152 return null;
153 }
154 }
155
156 return null;
157 }
158
159 private void init(Resources r) {
160 XmlResourceParser parser = r.getXml(com.android.internal.R.xml.autotext);
161
162 StringBuilder right = new StringBuilder(RIGHT);
163 mTrie = new char[DEFAULT];
164 mTrie[TRIE_ROOT] = TRIE_NULL;
165 mTrieUsed = TRIE_ROOT + 1;
166
167 try {
168 XmlUtils.beginDocument(parser, "words");
169 String odest = "";
170 char ooff = 0;
171
172 while (true) {
173 XmlUtils.nextElement(parser);
174
175 String element = parser.getName();
176 if (element == null || !(element.equals("word"))) {
177 break;
178 }
179
180 String src = parser.getAttributeValue(null, "src");
181 if (parser.next() == XmlPullParser.TEXT) {
182 String dest = parser.getText();
183 char off;
184
185 if (dest.equals(odest)) {
186 off = ooff;
187 } else {
188 off = (char) right.length();
189 right.append((char) dest.length());
190 right.append(dest);
191 }
192
193 add(src, off);
194 }
195 }
196
197 // Don't let Resources cache a copy of all these strings.
198 r.flushLayoutCache();
199 } catch (XmlPullParserException e) {
200 throw new RuntimeException(e);
201 } catch (IOException e) {
202 throw new RuntimeException(e);
203 } finally {
204 parser.close();
205 }
206
207 mText = right.toString();
208 }
209
210 private void add(String src, char off) {
211 int slen = src.length();
212 int herep = TRIE_ROOT;
Amith Yamasania565e5e2009-03-26 10:19:09 -0700213 // Keep track of the size of the dictionary
214 mSize++;
215
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800216 for (int i = 0; i < slen; i++) {
217 char c = src.charAt(i);
218 boolean found = false;
219
220 for (; mTrie[herep] != TRIE_NULL;
221 herep = mTrie[herep] + TRIE_NEXT) {
222 if (c == mTrie[mTrie[herep] + TRIE_C]) {
223 // There is a node for this letter, and this is the
224 // end, so fill in the right hand side fields.
225
226 if (i == slen - 1) {
227 mTrie[mTrie[herep] + TRIE_OFF] = off;
228 return;
229 }
230
231 // There is a node for this letter, and we need
232 // to go deeper into it to fill in the rest.
233
234 herep = mTrie[herep] + TRIE_CHILD;
235 found = true;
236 break;
237 }
238 }
239
240 if (!found) {
241 // No node for this letter yet. Make one.
242
243 char node = newTrieNode();
244 mTrie[herep] = node;
245
246 mTrie[mTrie[herep] + TRIE_C] = c;
247 mTrie[mTrie[herep] + TRIE_OFF] = TRIE_NULL;
248 mTrie[mTrie[herep] + TRIE_NEXT] = TRIE_NULL;
249 mTrie[mTrie[herep] + TRIE_CHILD] = TRIE_NULL;
250
251 // If this is the end of the word, fill in the offset.
252
253 if (i == slen - 1) {
254 mTrie[mTrie[herep] + TRIE_OFF] = off;
255 return;
256 }
257
258 // Otherwise, step in deeper and go to the next letter.
259
260 herep = mTrie[herep] + TRIE_CHILD;
261 }
262 }
263 }
264
265 private char newTrieNode() {
266 if (mTrieUsed + TRIE_SIZEOF > mTrie.length) {
267 char[] copy = new char[mTrie.length + INCREMENT];
268 System.arraycopy(mTrie, 0, copy, 0, mTrie.length);
269 mTrie = copy;
270 }
271
272 char ret = mTrieUsed;
273 mTrieUsed += TRIE_SIZEOF;
274
275 return ret;
276 }
277}