-package com.hughes.android.dictionary;
-
-import java.io.File;
-import java.io.FileNotFoundException;
-import java.io.FileOutputStream;
-import java.io.IOException;
-import java.io.OutputStream;
-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.AlertDialog;
-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.MenuItem;
-import android.view.View;
-import android.view.ViewGroup;
-import android.view.ContextMenu.ContextMenuInfo;
-import android.view.MenuItem.OnMenuItemClickListener;
-import android.widget.AdapterView;
-import android.widget.BaseAdapter;
-import android.widget.EditText;
-import android.widget.ListView;
-import android.widget.TextView;
-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 File wordList = new File("/sdcard/wordList.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;
- }
- }));
- }
-
- 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");
- }
-
- @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) {
- final String rawText = entries.get(selectedItem).getRawText();
- Log.d("THAD", "Writing : "
- + rawText);
- try {
- final OutputStream out = new FileOutputStream(wordList, true);
- out.write((rawText + "\n").getBytes());
- out.close();
- } catch (IOException e) {
- throw new RuntimeException(e);
- }
- 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());
- }
-
- 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) {
- }
- }
-
-}
\ No newline at end of file
+package com.hughes.android.dictionary;\r
+\r
+import java.io.File;\r
+import java.io.IOException;\r
+import java.io.RandomAccessFile;\r
+\r
+import com.hughes.util.FileUtil;\r
+import com.hughes.util.LRUCacheMap;\r
+\r
+public final class Dictionary {\r
+\r
+ public static final String INDEX_FORMAT = "%s_index_%d";\r
+\r
+ private final byte lang;\r
+ private final File dictionaryFile;\r
+ private final RandomAccessFile dictionaryRaf;\r
+ private final Index index;\r
+ \r
+ private final LRUCacheMap<Integer,Row> positionToRow = new LRUCacheMap<Integer, Row>(2000);\r
+\r
+ public Dictionary(final String dictionaryFileName, final byte lang)\r
+ throws IOException {\r
+ this.lang = lang;\r
+ this.dictionaryFile = new File(dictionaryFileName);\r
+\r
+ dictionaryRaf = new RandomAccessFile(dictionaryFile, "r");\r
+ index = new Index(String.format(INDEX_FORMAT, dictionaryFile\r
+ .getAbsolutePath(), lang));\r
+ }\r
+ \r
+ public int numRows() {\r
+ return index.root.getDescendantCount();\r
+ }\r
+ \r
+ public Row getRow(final int position) throws IOException {\r
+ Row row = positionToRow.get(position);\r
+ if (row == null) {\r
+ final Object o = index.root.getDescendant(position);\r
+ if (o instanceof Integer) {\r
+ final Entry entry = new Entry(FileUtil.readLine(dictionaryRaf, (Integer) o));\r
+ row = new Row(entry.getFormattedEntry(lang), false);\r
+ } else if (o instanceof String) {\r
+ row = new Row((String) o, true);\r
+ } else {\r
+ throw new RuntimeException(o.toString());\r
+ }\r
+ positionToRow.put(position, row);\r
+ }\r
+ return row;\r
+ }\r
+ \r
+ public static class Row {\r
+ final String text;\r
+ final boolean isWord;\r
+ public Row(final String text, final boolean isWord) {\r
+ this.text = text;\r
+ this.isWord = isWord;\r
+ }\r
+ }\r
+\r
+}\r
tokenToOffsets.put(token, offsets);\r
}\r
\r
+ // TODO: move this up, and defer the loading of the other stuff until it's needed.\r
descendantTokenCount = file.readInt();\r
descendantEntryCount = file.readInt();\r
}\r
\r
+ @Override\r
+ public String toString() {\r
+ return String.format("%s(%d,%d)", nodeHandle.normalizedToken, getThisCount(), getDescendantCount());\r
+ }\r
+ \r
public int getDescendantCount() {\r
return descendantEntryCount + descendantTokenCount;\r
}\r
+ \r
+ public int getThisCount() {\r
+ int count = tokenToOffsets.size();\r
+ for (final int[] offsets : tokenToOffsets.values()) {\r
+ count += offsets.length;\r
+ }\r
+ return count;\r
+ }\r
\r
- public Object getDescendant(final int position) throws IOException {\r
- if (position < getDescendantCount()) {\r
- return null;\r
+ public Object getDescendant(int position) throws IOException {\r
+ assert position < getDescendantCount(); \r
+\r
+// System.out.println("getD: " + this + ", " + position);\r
+ if (position < getThisCount()) {\r
+ for (final Map.Entry<String, int[]> tokenEntry : tokenToOffsets.entrySet()) {\r
+ if (position == 0) {\r
+ return tokenEntry.getKey();\r
+ }\r
+ --position;\r
+ if (position < tokenEntry.getValue().length) {\r
+ return tokenEntry.getValue()[position];\r
+ }\r
+ position -= tokenEntry.getValue().length;\r
+ }\r
+ assert false;\r
}\r
+ position -= getThisCount();\r
+ \r
+ \r
for (final Map.Entry<String,NodeHandle> childEntry : children.entrySet()) {\r
final Node child = childEntry.getValue().getNode();\r
if (position < child.getDescendantCount()) {\r
- \r
- } else {\r
- position -= child.getDescendantCount();\r
+ return child.getDescendant(position);\r
}\r
+ position -= child.getDescendantCount();\r
}\r
+ assert false;\r
+ return null;\r
}\r
\r
public void getDescendantEntryOffsets(final Set<Integer> entryOffsets, int maxSize) throws IOException {\r