UserDictionarypublic class UserDictionary extends Dictionary
Fields Summary |
---|
private static final String[] | PROJECTION | private static final int | INDEX_WORD | private static final int | INDEX_FREQUENCY | private static final char | QUOTE | private android.content.Context | mContext | List | mRoots | private int | mMaxDepth | private int | mInputLength | public static final int | MAX_WORD_LENGTH | private char[] | mWordBuilder | private android.database.ContentObserver | mObserver | private boolean | mRequiresReload | static final char[] | BASE_CHARSTable mapping most combined Latin, Greek, and Cyrillic characters
to their base characters. If c is in range, BASE_CHARS[c] == c
if c is not a combined character, or the base character if it
is combined. |
Constructors Summary |
---|
public UserDictionary(android.content.Context context)
mContext = context;
// Perform a managed query. The Activity will handle closing and requerying the cursor
// when needed.
ContentResolver cres = context.getContentResolver();
cres.registerContentObserver(Words.CONTENT_URI, true, mObserver = new ContentObserver(null) {
@Override
public void onChange(boolean self) {
mRequiresReload = true;
}
});
loadDictionary();
|
Methods Summary |
---|
public synchronized void | addWord(java.lang.String word, int frequency)Adds a word to the dictionary and makes it persistent.
if (mRequiresReload) loadDictionary();
// Safeguard against adding long words. Can cause stack overflow.
if (word.length() >= MAX_WORD_LENGTH) return;
addWordRec(mRoots, word, 0, frequency);
Words.addWord(mContext, word, frequency, Words.LOCALE_TYPE_CURRENT);
// In case the above does a synchronous callback of the change observer
mRequiresReload = false;
| private void | addWordRec(java.util.List children, java.lang.String word, int depth, int frequency)
final int wordLength = word.length();
final char c = word.charAt(depth);
// Does children have the current character?
final int childrenLength = children.size();
Node childNode = null;
boolean found = false;
for (int i = 0; i < childrenLength; i++) {
childNode = children.get(i);
if (childNode.code == c) {
found = true;
break;
}
}
if (!found) {
childNode = new Node();
childNode.code = c;
children.add(childNode);
}
if (wordLength == depth + 1) {
// Terminate this word
childNode.terminal = true;
childNode.frequency += frequency; // If there are multiple similar words
return;
}
if (childNode.children == null) {
childNode.children = new ArrayList<Node>();
}
addWordRec(childNode.children, word, depth + 1, frequency);
| private void | addWords(android.database.Cursor cursor)
mRoots = new ArrayList<Node>();
if (cursor.moveToFirst()) {
while (!cursor.isAfterLast()) {
String word = cursor.getString(INDEX_WORD);
int frequency = cursor.getInt(INDEX_FREQUENCY);
// Safeguard against adding really long words. Stack may overflow due
// to recursion
if (word.length() < MAX_WORD_LENGTH) {
addWordRec(mRoots, word, 0, frequency);
}
cursor.moveToNext();
}
}
cursor.close();
| public synchronized void | close()
if (mObserver != null) {
mContext.getContentResolver().unregisterContentObserver(mObserver);
mObserver = null;
}
| public synchronized void | getWords(WordComposer codes, WordCallback callback)
if (mRequiresReload) loadDictionary();
mInputLength = codes.size();
mMaxDepth = mInputLength * 3;
getWordsRec(mRoots, codes, mWordBuilder, 0, false, 1.0f, 0, callback);
| private void | getWordsRec(java.util.List roots, WordComposer codes, char[] word, int depth, boolean completion, float snr, int inputIndex, WordCallback callback)Recursively traverse the tree for words that match the input. Input consists of
a list of arrays. Each item in the list is one input character position. An input
character is actually an array of multiple possible candidates. This function is not
optimized for speed, assuming that the user dictionary will only be a few hundred words in
size.
final int count = roots.size();
final int codeSize = mInputLength;
// Optimization: Prune out words that are too long compared to how much was typed.
if (depth > mMaxDepth) {
return;
}
int[] currentChars = null;
if (codeSize <= inputIndex) {
completion = true;
} else {
currentChars = codes.getCodesAt(inputIndex);
}
for (int i = 0; i < count; i++) {
final Node node = roots.get(i);
final char c = node.code;
final char lowerC = toLowerCase(c);
boolean terminal = node.terminal;
List<Node> children = node.children;
int freq = node.frequency;
if (completion) {
word[depth] = c;
if (terminal) {
if (!callback.addWord(word, 0, depth + 1, (int) (freq * snr))) {
return;
}
}
if (children != null) {
getWordsRec(children, codes, word, depth + 1, completion, snr, inputIndex,
callback);
}
} else if (c == QUOTE && currentChars[0] != QUOTE) {
// Skip the ' and continue deeper
word[depth] = QUOTE;
if (children != null) {
getWordsRec(children, codes, word, depth + 1, completion, snr, inputIndex,
callback);
}
} else {
for (int j = 0; j < currentChars.length; j++) {
float addedAttenuation = (j > 0 ? 1f : 3f);
if (currentChars[j] == -1) {
break;
}
if (currentChars[j] == lowerC || currentChars[j] == c) {
word[depth] = c;
if (codes.size() == depth + 1) {
if (terminal) {
if (INCLUDE_TYPED_WORD_IF_VALID
|| !same(word, depth + 1, codes.getTypedWord())) {
callback.addWord(word, 0, depth + 1,
(int) (freq * snr * addedAttenuation
* FULL_WORD_FREQ_MULTIPLIER));
}
}
if (children != null) {
getWordsRec(children, codes, word, depth + 1,
true, snr * addedAttenuation, inputIndex + 1, callback);
}
} else if (children != null) {
getWordsRec(children, codes, word, depth + 1,
false, snr * addedAttenuation, inputIndex + 1, callback);
}
}
}
}
}
| public synchronized boolean | isValidWord(java.lang.CharSequence word)
if (mRequiresReload) loadDictionary();
return isValidWordRec(mRoots, word, 0, word.length());
| private boolean | isValidWordRec(java.util.List children, java.lang.CharSequence word, int offset, int length)
final int count = children.size();
char currentChar = word.charAt(offset);
for (int j = 0; j < count; j++) {
final Node node = children.get(j);
if (node.code == currentChar) {
if (offset == length - 1) {
if (node.terminal) {
return true;
}
} else {
if (node.children != null) {
if (isValidWordRec(node.children, word, offset + 1, length)) {
return true;
}
}
}
}
}
return false;
| private synchronized void | loadDictionary()
Cursor cursor = mContext.getContentResolver()
.query(Words.CONTENT_URI, PROJECTION, "(locale IS NULL) or (locale=?)",
new String[] { Locale.getDefault().toString() }, null);
addWords(cursor);
mRequiresReload = false;
| static char | toLowerCase(char c)
if (c < BASE_CHARS.length) {
c = BASE_CHARS[c];
}
c = Character.toLowerCase(c);
return c;
|
|