-package com.hughes.android.dictionary;
-
-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.ListActivity;
-import android.os.Bundle;
-import android.os.Handler;
-import android.text.Editable;
-import android.text.TextWatcher;
-import android.util.Log;
-import android.view.ContextMenu;
-import android.view.KeyEvent;
-import android.view.MenuItem;
-import android.view.View;
-import android.view.ViewGroup;
-import android.view.Window;
-import android.view.ContextMenu.ContextMenuInfo;
-import android.view.MenuItem.OnMenuItemClickListener;
-import android.view.View.OnLongClickListener;
-import android.widget.AdapterView;
-import android.widget.BaseAdapter;
-import android.widget.EditText;
-import android.widget.ListView;
-import android.widget.TextView;
-import android.widget.AdapterView.OnItemClickListener;
-import android.widget.AdapterView.OnItemLongClickListener;
-
-import com.hughes.util.FileUtil;
-
-public class Dictionary extends ListActivity {
-
- public static final String INDEX_FORMAT = "%s_index_%d";
- private 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;
-
- final Handler uiHandler = new Handler();
-
- private final Object mutex = new Object();
- private Executor searchExecutor = Executors.newSingleThreadExecutor();
- private SearchOperation searchOperation = null;
- private List<Entry> entries = Collections.emptyList();
- private DictionaryListAdapter dictionaryListAdapter = new DictionaryListAdapter();
- private int selectedItem = -1;
-
- /** Called when the activity is first created. */
- @Override
- public void onCreate(Bundle savedInstanceState) {
- Log.d("THAD", "onCreate");
- super.onCreate(savedInstanceState);
- setContentView(R.layout.main);
-
- EditText searchText = (EditText) findViewById(R.id.SearchText);
- searchText.addTextChangedListener(new DictionaryTextWatcher());
-
- setListAdapter(dictionaryListAdapter);
-
- try {
- loadIndex();
- } catch (Exception e) {
- throw new RuntimeException(e);
- }
- onSearchTextChange("");
-
- registerForContextMenu(getListView());
- getListView().setOnItemLongClickListener((new OnItemLongClickListener() {
- public boolean onItemLongClick(AdapterView<?> arg0, View arg1, int arg2,
- long arg3) {
- selectedItem = arg2;
- return false;
- }
- }));
- }
-
- @Override
- public void onCreateContextMenu(ContextMenu menu, View v,
- ContextMenuInfo menuInfo) {
- if (selectedItem == -1) {
- return;
- }
- final MenuItem addToWordlist = menu.add("Add to wordlist.");
- addToWordlist.setOnMenuItemClickListener(new OnMenuItemClickListener() {
- public boolean onMenuItemClick(MenuItem item) {
- Log
- .d("THAD", "context menu: "
- + entries.get(selectedItem).getRawText());
- return false;
- }
- });
- }
-
- @Override
- protected void onListItemClick(ListView l, View v, int position, long id) {
- Log.d("THAD", "Clicked: " + entries.get(position).getRawText());
- selectedItem = position;
- openContextMenu(getListView());
- }
-
- private void loadIndex() throws IOException, ClassNotFoundException {
- Log.d("THAD", "enter loadIndex");
- 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) {
- Log.d("THAD", "onSearchTextChange: " + searchText);
- synchronized (mutex) {
- if (searchOperation != null) {
- searchOperation.interrupted.set(true);
- }
- searchOperation = new SearchOperation(searchText);
- }
- searchExecutor.execute(searchOperation);
- }
-
- 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.toLowerCase(); // TODO: better
- }
-
- public void run() {
- Log.d("THAD", "SearchOperation: " + searchText);
- final List<Entry> newEntries = new ArrayList<Entry>(count);
- try {
- final Index.Node node = indexes[lang].lookup(searchText);
- final Set<Integer> entryOffsets = new LinkedHashSet<Integer>(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);
- }
-
- synchronized (mutex) {
- if (interrupted.get()) {
- return;
- }
- entries = newEntries;
- }
-
- uiHandler.post(new Runnable() {
- public void run() {
- synchronized (mutex) {
- dictionaryListAdapter.notifyDataSetChanged();
- if (entries.size() > 0) {
- setSelection(0);
- }
- }
- }
- });
- }
- }
-
- private class DictionaryListAdapter extends BaseAdapter {
-
- public int getCount() {
- synchronized (mutex) {
- return entries.size();
- }
- }
-
- public Object getItem(int position) {
- synchronized (mutex) {
- assert position < entries.size();
- return entries.get(position).getFormattedEntry(lang);
- }
- }
-
- public long getItemId(int position) {
- return position;
- }
-
- public View getView(final int position, final View convertView,
- final ViewGroup parent) {
- TextView result = null;
- if (convertView instanceof TextView) {
- result = (TextView) convertView;
- } else {
- result = new TextView(parent.getContext());
- }
- result.setText((String) getItem(position));
- return result;
- }
-
- }
-
- private class DictionaryTextWatcher implements TextWatcher {
- public void afterTextChanged(Editable searchText) {
- onSearchTextChange(searchText.toString());
- }
-
- public void beforeTextChanged(CharSequence arg0, int arg1, int arg2,
- int arg3) {
- }
-
- public void onTextChanged(CharSequence arg0, int arg1, int arg2, int arg3) {
- }
- }
-
- public void run() {
- // TODO Auto-generated method stub
-
- }
-
-}
\ No newline at end of file
+package com.hughes.android.dictionary;\r
+\r
+import java.io.IOException;\r
+import java.io.RandomAccessFile;\r
+import java.util.ArrayList;\r
+import java.util.List;\r
+import java.util.concurrent.atomic.AtomicBoolean;\r
+\r
+import com.hughes.util.CachingList;\r
+import com.hughes.util.raf.FileList;\r
+import com.hughes.util.raf.RAFFactory;\r
+import com.hughes.util.raf.RAFSerializable;\r
+import com.hughes.util.raf.RAFSerializableSerializer;\r
+import com.hughes.util.raf.RAFSerializer;\r
+import com.hughes.util.raf.UniformFileList;\r
+\r
+public final class Dictionary implements RAFSerializable<Dictionary> {\r
+\r
+ static final RAFSerializer<Entry> ENTRY_SERIALIZER = new RAFSerializableSerializer<Entry>(\r
+ Entry.RAF_FACTORY);\r
+ static final RAFSerializer<Row> ROW_SERIALIZER = new RAFSerializableSerializer<Row>(\r
+ Row.RAF_FACTORY);\r
+ static final RAFSerializer<IndexEntry> INDEX_ENTRY_SERIALIZER = new RAFSerializableSerializer<IndexEntry>(\r
+ IndexEntry.RAF_FACTORY);\r
+\r
+ final String dictionaryInfo;\r
+ final List<Entry> entries;\r
+ final LanguageData[] languageDatas = new LanguageData[2];\r
+\r
+ public Dictionary(final String dictionaryInfo, final Language language0, final Language language1) {\r
+ this.dictionaryInfo = dictionaryInfo;\r
+ languageDatas[0] = new LanguageData(this, language0, Entry.LANG1);\r
+ languageDatas[1] = new LanguageData(this, language1, Entry.LANG2);\r
+ entries = new ArrayList<Entry>();\r
+ }\r
+\r
+ public Dictionary(final RandomAccessFile raf) throws IOException {\r
+ dictionaryInfo = raf.readUTF();\r
+ entries = CachingList.create(FileList.create(raf, ENTRY_SERIALIZER, raf\r
+ .getFilePointer()), 10000);\r
+ languageDatas[0] = new LanguageData(this, raf, Entry.LANG1);\r
+ languageDatas[1] = new LanguageData(this, raf, Entry.LANG2);\r
+ }\r
+\r
+ public void write(RandomAccessFile raf) throws IOException {\r
+ raf.writeUTF(dictionaryInfo);\r
+ FileList.write(raf, entries, ENTRY_SERIALIZER);\r
+ languageDatas[0].write(raf);\r
+ languageDatas[1].write(raf);\r
+ }\r
+\r
+ final class LanguageData implements RAFSerializable<LanguageData> {\r
+ final Dictionary dictionary;\r
+ final Language language;\r
+ final byte lang;\r
+ final List<Row> rows;\r
+ final List<IndexEntry> sortedIndex;\r
+\r
+ LanguageData(final Dictionary dictionary, final Language language, final byte lang) {\r
+ this.dictionary = dictionary;\r
+ this.language = language;\r
+ this.lang = lang;\r
+ rows = new ArrayList<Row>();\r
+ sortedIndex = new ArrayList<IndexEntry>();\r
+ }\r
+\r
+ LanguageData(final Dictionary dictionary, final RandomAccessFile raf, final byte lang) throws IOException {\r
+ this.dictionary = dictionary;\r
+ language = Language.lookup(raf.readUTF());\r
+ if (language == null) {\r
+ throw new RuntimeException("Unknown language.");\r
+ }\r
+ this.lang = lang;\r
+ rows = CachingList.create(UniformFileList.create(raf, ROW_SERIALIZER, raf\r
+ .getFilePointer()), 10000);\r
+ sortedIndex = CachingList.create(FileList.create(raf,\r
+ INDEX_ENTRY_SERIALIZER, raf.getFilePointer()), 10000);\r
+ }\r
+\r
+ public void write(final RandomAccessFile raf) throws IOException {\r
+ raf.writeUTF(language.symbol);\r
+ UniformFileList.write(raf, rows, ROW_SERIALIZER, 4);\r
+ FileList.write(raf, sortedIndex, INDEX_ENTRY_SERIALIZER);\r
+ }\r
+\r
+ String rowToString(final Row row) {\r
+ return row.isToken() ? sortedIndex.get(row.getIndex()).word : entries\r
+ .get(row.getIndex()).toString();\r
+ }\r
+\r
+ int lookup(String word, final AtomicBoolean interrupted) {\r
+ word = word.toLowerCase();\r
+\r
+ int start = 0;\r
+ int end = sortedIndex.size();\r
+ while (start < end) {\r
+ final int mid = (start + end) / 2;\r
+ if (interrupted.get()) {\r
+ return mid;\r
+ }\r
+ final IndexEntry midEntry = sortedIndex.get(mid);\r
+ if (midEntry.word.equals("pre-print")) {\r
+ System.out.println();\r
+ }\r
+\r
+ final int comp = language.sortComparator.compare(word, midEntry.word.toLowerCase());\r
+ if (comp == 0) {\r
+ int result = mid;\r
+ while (result > 0 && language.findComparator.compare(word, sortedIndex.get(result - 1).word.toLowerCase()) == 0) {\r
+ --result;\r
+ if (interrupted.get()) {\r
+ return result;\r
+ }\r
+ }\r
+ return result;\r
+ } else if (comp < 0) {\r
+// Log.d("THAD", "Upper bound: " + midEntry);\r
+ end = mid;\r
+ } else {\r
+// Log.d("THAD", "Lower bound: " + midEntry);\r
+ start = mid + 1;\r
+ }\r
+ }\r
+ return Math.min(sortedIndex.size() - 1, start);\r
+ }\r
+\r
+ public IndexEntry getIndexEntryForRow(final int rowIndex) {\r
+ // TODO: this kinda blows.\r
+ int r = rowIndex;\r
+ Row row;\r
+ while (true) {\r
+ row = rows.get(r); \r
+ if (row.isToken() || row.indexEntry != null) {\r
+ break;\r
+ }\r
+ --r;\r
+ }\r
+ final IndexEntry indexEntry = row.isToken() ? sortedIndex.get(row.getIndex()) : row.indexEntry;\r
+ for (; r <= rowIndex; ++r) {\r
+ rows.get(r).indexEntry = indexEntry;\r
+ }\r
+ assert rows.get(indexEntry.startRow).isToken();\r
+ return indexEntry;\r
+ }\r
+ }\r
+\r
+ public static final class Row implements RAFSerializable<Row> {\r
+ final int index;\r
+\r
+ IndexEntry indexEntry = null;\r
+\r
+ public Row(final int index) {\r
+ this.index = index;\r
+ }\r
+\r
+ static final RAFFactory<Row> RAF_FACTORY = new RAFFactory<Row>() {\r
+ public Row create(RandomAccessFile raf) throws IOException {\r
+ return new Row(raf.readInt());\r
+ }\r
+ };\r
+\r
+ public void write(RandomAccessFile raf) throws IOException {\r
+ raf.writeInt(index);\r
+ }\r
+\r
+ boolean isToken() {\r
+ return index < 0;\r
+ }\r
+\r
+ public int getIndex() {\r
+ if (index >= 0) {\r
+ return index;\r
+ }\r
+ return -index - 1;\r
+ }\r
+ }\r
+\r
+ public static final class IndexEntry implements RAFSerializable<IndexEntry> {\r
+ final String word;\r
+ final int startRow;\r
+\r
+ public IndexEntry(final String word, final int startRow) {\r
+ this.word = word;\r
+ this.startRow = startRow;\r
+ }\r
+\r
+ static final RAFFactory<IndexEntry> RAF_FACTORY = new RAFFactory<IndexEntry>() {\r
+ public IndexEntry create(RandomAccessFile raf) throws IOException {\r
+ final String word = raf.readUTF();\r
+ final int startRow = raf.readInt();\r
+ return new IndexEntry(word, startRow);\r
+ }\r
+ };\r
+\r
+ public void write(final RandomAccessFile raf) throws IOException {\r
+ raf.writeUTF(word);\r
+ raf.writeInt(startRow);\r
+ }\r
+\r
+ @Override\r
+ public String toString() {\r
+ return word + "@" + startRow;\r
+ }\r
+\r
+ }\r
+\r
+}\r