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