From ee244dcc9727c0af8377dd4f0b6a6fb7e3b8b921 Mon Sep 17 00:00:00 2001 From: thadh Date: Mon, 9 Feb 2009 23:28:24 -0800 Subject: [PATCH] go --- .../hughes/android/dictionary/Dictionary.java | 117 +++++++++++++----- src/com/hughes/android/dictionary/Entry.java | 16 +++ src/com/hughes/android/dictionary/Index.java | 49 ++++++-- 3 files changed, 140 insertions(+), 42 deletions(-) diff --git a/src/com/hughes/android/dictionary/Dictionary.java b/src/com/hughes/android/dictionary/Dictionary.java index 52b46b4..06779fa 100755 --- a/src/com/hughes/android/dictionary/Dictionary.java +++ b/src/com/hughes/android/dictionary/Dictionary.java @@ -4,10 +4,17 @@ import java.io.File; import java.io.IOException; import java.io.RandomAccessFile; import java.util.ArrayList; +import java.util.Collections; +import java.util.LinkedHashSet; import java.util.List; +import java.util.Set; +import java.util.concurrent.Executor; +import java.util.concurrent.Executors; +import java.util.concurrent.atomic.AtomicBoolean; import android.app.Activity; import android.os.Bundle; +import android.os.Handler; import android.text.Editable; import android.text.TextWatcher; import android.util.Log; @@ -18,19 +25,24 @@ import android.widget.EditText; import android.widget.ListView; import android.widget.TextView; +import com.hughes.util.FileUtil; + public class Dictionary extends Activity { - private String searchText = ""; - - private List entries = new ArrayList(100); + public static final String INDEX_FORMAT = "%s_index_%d"; + private File dictionaryFile = new File("/sdcard/dict-de-en.txt"); - public static final String INDEX_FORMAT = "%s_index_%d"; - private final File dictionaryFile = new File("/sdcard/dict-de-en.txt"); + private RandomAccessFile dictionaryRaf; private final Index[] indexes = new Index[2]; - private final byte lang = Entry.LANG1; + private final byte lang = Entry.LANG1; + + final Handler uiHandler = new Handler(); + private final Object mutex = new Object(); + private Executor searchExecutor = Executors.newSingleThreadExecutor(); + private SearchOperation searchOperation = null; + private List entries = Collections.emptyList(); private DictionaryListAdapter dictionaryListAdapter = new DictionaryListAdapter(); - /** Called when the activity is first created. */ @Override @@ -44,57 +56,91 @@ public class Dictionary extends Activity { ListView searchResults = (ListView) findViewById(R.id.SearchResults); searchResults.setAdapter(dictionaryListAdapter); - try { loadIndex(); } catch (Exception e) { throw new RuntimeException(e); } - + onSearchTextChange(""); } private void loadIndex() throws IOException, ClassNotFoundException { Log.d("THAD", "enter loadIndex"); - indexes[0] = new Index(String.format(INDEX_FORMAT, dictionaryFile.getAbsolutePath(), Entry.LANG1)); + indexes[0] = new Index(String.format(INDEX_FORMAT, dictionaryFile + .getAbsolutePath(), Entry.LANG1)); + dictionaryRaf = new RandomAccessFile(dictionaryFile, "r"); Log.d("THAD", "exit loadIndex"); } void onSearchTextChange(final String searchText) { - this.searchText = searchText; - final Index.Node node = indexes[lang].lookup(searchText); - - try { - final long length = dictionaryFile.length(); - Log.d("THAD", "Dictionary file length=" + length); + Log.d("THAD", "onSearchTextChange: " + searchText); + synchronized (mutex) { + if (searchOperation != null) { + searchOperation.interrupted.set(true); + } + searchOperation = new SearchOperation(searchText); + } + searchExecutor.execute(searchOperation); + } - final RandomAccessFile raf = new RandomAccessFile(dictionaryFile, "r"); - raf.seek(length / 2); -// final InputStreamReader dictionaryReader = new InputStreamReader(new BufferedInputStream(new FileInputStream("/sdcard/dict-de-en.txt"))); -// skip(dictionaryReader, length / 2); + private final class SearchOperation implements Runnable { + final String searchText; + final int count = 100; + final AtomicBoolean interrupted = new AtomicBoolean(false); + + public SearchOperation(final String searchText) { + this.searchText = searchText; + } + + public void run() { + Log.d("THAD", "SearchOperation: " + searchText); + final List newEntries = new ArrayList(count); + try { + final Index.Node node = indexes[lang].lookup(searchText); + final Set entryOffsets = new LinkedHashSet(count); + node.getDescendantEntryOffsets(entryOffsets, count); + for (final int entryOffset : entryOffsets) { + if (interrupted.get()) { + Log.d("THAD", "Interrupted while parsing entries."); + return; + } + final String line = FileUtil.readLine(dictionaryRaf, entryOffset); + final Entry entry = new Entry(line); + newEntries.add(entry); + } + } catch (IOException e) { + Log.e("THAD", "Search failure.", e); + } - for (int i = 0; i < entries.length; ++i) { - entries[i] = raf.readLine(); - raf.skipBytes((int) (length / 100000)); + synchronized (mutex) { + if (interrupted.get()) { + return; + } + entries = newEntries; } - raf.close(); - - } catch (IOException e) { - throw new RuntimeException(e); + uiHandler.post(new Runnable() { + public void run() { + synchronized (mutex) { + dictionaryListAdapter.notifyDataSetChanged(); + } + }}); } - - dictionaryListAdapter.notifyDataSetChanged(); } - + private class DictionaryListAdapter extends BaseAdapter { public int getCount() { - return 1000000; - + synchronized (mutex) { + return entries.size(); + } } public Object getItem(int position) { - return searchText + position + entries[position % entries.length]; + synchronized (mutex) { + assert position < entries.size(); + return entries.get(position).getFormattedEntry(lang); + } } public long getItemId(int position) { @@ -127,4 +173,9 @@ public class Dictionary extends Activity { } } + public void run() { + // TODO Auto-generated method stub + + } + } \ No newline at end of file diff --git a/src/com/hughes/android/dictionary/Entry.java b/src/com/hughes/android/dictionary/Entry.java index 3bb7b67..6187ab8 100755 --- a/src/com/hughes/android/dictionary/Entry.java +++ b/src/com/hughes/android/dictionary/Entry.java @@ -12,6 +12,13 @@ public final class Entry { String lang1 = ""; String lang2 = ""; + Entry() {} + + Entry(final String line) { + final boolean parsed = parseFromLine(line); + assert parsed; + } + boolean parseFromLine(final String line) { final String[] parts = lineSplitPattern.split(line); if (parts.length != 2) { @@ -44,4 +51,13 @@ public final class Entry { .replaceAll("[^A-Za-z]", ""); } + public Object getFormattedEntry(final byte lang) { + return getAllText(lang) + "\n" + getAllText(OtherLang(lang)); + } + + private byte OtherLang(final byte lang) { + assert lang == LANG1 || lang == LANG2; + return lang == LANG1 ? LANG2 : LANG1; + } + } diff --git a/src/com/hughes/android/dictionary/Index.java b/src/com/hughes/android/dictionary/Index.java index 89024ff..5fd2dff 100755 --- a/src/com/hughes/android/dictionary/Index.java +++ b/src/com/hughes/android/dictionary/Index.java @@ -3,6 +3,7 @@ package com.hughes.android.dictionary; import java.io.IOException; import java.io.RandomAccessFile; import java.util.Map; +import java.util.Set; import java.util.TreeMap; import com.hughes.util.LRUCacheMap; @@ -14,7 +15,7 @@ public final class Index { final RandomAccessFile file; final Node root; - final Map locationToNode = new LRUCacheMap(5000); + final Map indexOffsetToNode = new LRUCacheMap(5000); public Index(final String filename) throws IOException { @@ -31,35 +32,46 @@ public final class Index { if (pos == text.length()) { return n; } + + // Check whether any prefix of text is a child. for (int i = pos + 1; i <= text.length(); ++i) { final Integer child = n.children.get(text.substring(pos, i)); if (child != null) { return lookup(text, i, getNode(text.substring(0, i), child)); } } + + // Check whether any child starts with what's left of text. + final String remainder = text.substring(pos); + for (final Map.Entry childEntry : n.children.entrySet()) { + if (childEntry.getKey().startsWith(remainder)) { + return getNode(n.text + childEntry.getKey(), childEntry.getValue()); + } + } + return n; } - private Node getNode(final String text, final int location) throws IOException { - Node node = locationToNode.get(location); + private Node getNode(final String text, final int indexOffset) throws IOException { + Node node = indexOffsetToNode.get(indexOffset); if (node == null) { - node = new Node(text, location); - locationToNode.put(location, node); + node = new Node(text, indexOffset); + indexOffsetToNode.put(indexOffset, node); } return node; } final class Node { final String text; - final int location; + final int indexOffset; final TreeMap children; final int[] offsets; - Node(final String text, final int location) throws IOException { + Node(final String text, final int indexOffset) throws IOException { this.text = text; - this.location = location; + this.indexOffset = indexOffset; - file.seek(location); + file.seek(indexOffset); final int numChildren = file.readInt(); children = new TreeMap(); for (int i = 0; i < numChildren; ++i) { @@ -76,6 +88,25 @@ public final class Index { offsets[i] = file.readInt(); } } + + public void getDescendantEntryOffsets(final Set entryOffsets, int maxSize) throws IOException { + for (int i = 0; i < offsets.length; ++i) { + if (entryOffsets.size() >= maxSize) { + return; + } + entryOffsets.add(offsets[i]); + } + if (entryOffsets.size() >= maxSize) { + return; + } + for (final Map.Entry childEntry : children.entrySet()) { + final Node child = getNode(text + childEntry.getKey(), childEntry.getValue()); + child.getDescendantEntryOffsets(entryOffsets, maxSize); + if (entryOffsets.size() >= maxSize) { + return; + } + } + } } } -- 2.43.0