import java.io.IOException;\r
import java.io.RandomAccessFile;\r
import java.util.ArrayList;\r
+import java.util.Comparator;\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.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>(Entry.RAF_FACTORY);\r
- static final RAFSerializer<Row> ROW_SERIALIZER = new RAFSerializableSerializer<Row>(Row.RAF_FACTORY);\r
- static final RAFSerializer<IndexEntry> INDEX_ENTRY_SERIALIZER = new RAFSerializableSerializer<IndexEntry>(IndexEntry.RAF_FACTORY);\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 List<Entry> entries;\r
final Language[] languages = new Language[2];\r
- \r
- Language activeLanguage = null;\r
\r
public Dictionary(final String lang0, final String lang1) {\r
- languages[0] = new Language(lang0);\r
- languages[1] = new Language(lang1);\r
+ languages[0] = new Language(lang0, Entry.LANG1);\r
+ languages[1] = new Language(lang1, Entry.LANG2);\r
entries = new ArrayList<Entry>();\r
}\r
- \r
+\r
public Dictionary(final RandomAccessFile raf) throws IOException {\r
- entries = CachingList.create(FileList.create(raf, ENTRY_SERIALIZER, raf.getFilePointer()), 10000);\r
- languages[0] = new Language(raf);\r
- languages[1] = new Language(raf);\r
+ entries = CachingList.create(FileList.create(raf, ENTRY_SERIALIZER, raf\r
+ .getFilePointer()), 10000);\r
+ languages[0] = new Language(raf, Entry.LANG1);\r
+ languages[1] = new Language(raf, Entry.LANG2);\r
}\r
+\r
public void write(RandomAccessFile raf) throws IOException {\r
FileList.write(raf, entries, ENTRY_SERIALIZER);\r
languages[0].write(raf);\r
languages[1].write(raf);\r
}\r
- \r
- static final class Language implements RAFSerializable<Language> {\r
+\r
+ final class Language implements RAFSerializable<Language> {\r
+ final byte lang;\r
final String symbol;\r
final List<Row> rows;\r
final List<IndexEntry> sortedIndex;\r
- \r
- public Language(final String symbol) {\r
+ final Comparator<String> comparator = EntryFactory.entryFactory\r
+ .getEntryComparator();\r
+\r
+ Language(final String symbol, final byte lang) {\r
+ this.lang = lang;\r
this.symbol = symbol;\r
rows = new ArrayList<Row>();\r
sortedIndex = new ArrayList<IndexEntry>();\r
}\r
\r
- public Language(final RandomAccessFile raf) throws IOException {\r
+ Language(final RandomAccessFile raf, final byte lang) throws IOException {\r
+ this.lang = lang;\r
symbol = raf.readUTF();\r
- rows = CachingList.create(FileList.create(raf, ROW_SERIALIZER, raf.getFilePointer()), 10000);\r
- sortedIndex = CachingList.create(FileList.create(raf, INDEX_ENTRY_SERIALIZER, raf.getFilePointer()), 10000);\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(symbol);\r
- FileList.write(raf, rows, ROW_SERIALIZER);\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
+\r
+ final int comp = comparator.compare(word, midEntry.word.toLowerCase());\r
+ if (comp == 0) {\r
+ int result = mid;\r
+ while (result > 0 && comparator.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
+ end = mid;\r
+ } else {\r
+ start = mid + 1;\r
+ }\r
+ }\r
+ return start;\r
+ }\r
}\r
- \r
+\r
public static final class Row implements RAFSerializable<Row> {\r
final int index;\r
\r
- public Row(int index) {\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
+\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
+\r
public IndexEntry(final String word, final int startRow) {\r
this.word = word;\r
this.startRow = startRow;\r
final String word = raf.readUTF();\r
final int startRow = raf.readInt();\r
return new IndexEntry(word, startRow);\r
- }};\r
+ }\r
+ };\r
+\r
public void write(final RandomAccessFile raf) throws IOException {\r
raf.writeUTF(word);\r
raf.writeInt(startRow);\r
public String toString() {\r
return word + "@" + startRow;\r
}\r
- \r
- \r
+\r
}\r
\r
}\r
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
-import java.util.ArrayList;
-import java.util.List;
+import java.io.RandomAccessFile;
import java.util.concurrent.Executor;
import java.util.concurrent.Executors;
import java.util.concurrent.atomic.AtomicBoolean;
-import com.hughes.android.dictionary.Dictionary.Row;
-
import android.app.ListActivity;
import android.os.Bundle;
import android.os.Handler;
import android.text.TextWatcher;
import android.util.Log;
import android.view.ContextMenu;
+import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
import android.view.ContextMenu.ContextMenuInfo;
import android.view.MenuItem.OnMenuItemClickListener;
+import android.view.View.OnClickListener;
import android.widget.AdapterView;
import android.widget.BaseAdapter;
+import android.widget.Button;
import android.widget.EditText;
import android.widget.ListView;
+import android.widget.TableLayout;
+import android.widget.TableRow;
import android.widget.TextView;
import android.widget.AdapterView.OnItemLongClickListener;
+import com.hughes.android.dictionary.Dictionary.IndexEntry;
+import com.hughes.android.dictionary.Dictionary.Language;
+import com.hughes.android.dictionary.Dictionary.Row;
+
public class DictionaryActivity extends ListActivity {
+ private RandomAccessFile dictRaf = null;
private Dictionary dictionary = null;
-
+ private Language activeLanguge = null;
+
private File wordList = new File("/sdcard/wordList.txt");
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 List<Entry> entries = Collections.emptyList();
private DictionaryListAdapter dictionaryListAdapter = new DictionaryListAdapter();
- private int selectedItem = -1;
+ private int selectedRow = -1;
/** Called when the activity is first created. */
@Override
public void onCreate(Bundle savedInstanceState) {
Log.d("THAD", "onCreate");
super.onCreate(savedInstanceState);
-
+
try {
- dictionary = new Dictionary("/sdcard/dict-de-en.txt", Entry.LANG1);
+ dictRaf = new RandomAccessFile("/sdcard/de-en.dict", "r");
+ dictionary = new Dictionary(dictRaf);
+ activeLanguge = dictionary.languages[Entry.LANG1];
} catch (Exception e) {
throw new RuntimeException(e);
}
-
+
setContentView(R.layout.main);
- EditText searchText = (EditText) findViewById(R.id.SearchText);
+ final EditText searchText = (EditText) findViewById(R.id.SearchText);
searchText.addTextChangedListener(new DictionaryTextWatcher());
setListAdapter(dictionaryListAdapter);
onSearchTextChange("");
+ final Button langButton = (Button) findViewById(R.id.LangButton);
+ langButton.setOnClickListener(new OnClickListener() {
+ public void onClick(View v) {
+ switchLanguage();
+ }});
+ updateLangButton();
registerForContextMenu(getListView());
getListView().setOnItemLongClickListener((new OnItemLongClickListener() {
public boolean onItemLongClick(AdapterView<?> arg0, View arg1, int arg2,
long arg3) {
- selectedItem = arg2;
+ selectedRow = arg2;
return false;
}
}));
}
+
+ public String getSelectedRowText() {
+ return activeLanguge.rowToString(activeLanguge.rows.get(selectedRow));
+ }
+
+ private MenuItem switchLanguageMenuItem = null;
+
+ @Override
+ public boolean onCreateOptionsMenu(final Menu menu) {
+ switchLanguageMenuItem = menu.add("Switch to language.");
+ return true;
+ }
+
+ @Override
+ public boolean onPrepareOptionsMenu(final Menu menu) {
+ switchLanguageMenuItem.setTitle(String.format("Switch to %s", dictionary.languages[Entry.otherLang(activeLanguge.lang)].symbol));
+ return super.onPrepareOptionsMenu(menu);
+ }
+
+ @Override
+ public boolean onOptionsItemSelected(final MenuItem item) {
+ if (item == switchLanguageMenuItem) {
+ switchLanguage();
+ }
+ return super.onOptionsItemSelected(item);
+ }
@Override
public void onCreateContextMenu(ContextMenu menu, View v,
ContextMenuInfo menuInfo) {
- if (selectedItem == -1) {
+ if (selectedRow == -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);
+ final String rawText = getSelectedRowText();
+ Log.d("THAD", "Writing : " + rawText);
try {
final OutputStream out = new FileOutputStream(wordList, true);
out.write((rawText + "\n").getBytes());
}
});
}
+
+ void switchLanguage() {
+ activeLanguge = dictionary.languages[(activeLanguge == dictionary.languages[0]) ? 1 : 0];
+ updateLangButton();
+ dictionaryListAdapter.notifyDataSetChanged();
+ final EditText searchText = (EditText) findViewById(R.id.SearchText);
+ onSearchTextChange(searchText.getText().toString());
+ }
+
+ void updateLangButton() {
+ final Button langButton = (Button) findViewById(R.id.LangButton);
+ langButton.setText(dictionary.languages[activeLanguge.lang].symbol.toUpperCase());
+ }
@Override
- protected void onListItemClick(ListView l, View v, int position, long id) {
- try {
- Log.d("THAD", "Clicked: " + dictionary.getRow(position));
- } catch (IOException e) {
- throw new RuntimeException(e);
- }
- selectedItem = position;
+ protected void onListItemClick(ListView l, View v, int row, long id) {
+ selectedRow = row;
+ Log.d("THAD", "Clicked: " + getSelectedRowText());
openContextMenu(getListView());
}
void onSearchTextChange(final String searchText) {
Log.d("THAD", "onSearchTextChange: " + searchText);
- if (1==1) return;
- synchronized (mutex) {
- if (searchOperation != null) {
- searchOperation.interrupted.set(true);
- }
- searchOperation = new SearchOperation(searchText);
+ if (searchOperation != null) {
+ searchOperation.interrupted.set(true);
}
+ searchOperation = new SearchOperation(searchText);
searchExecutor.execute(searchOperation);
}
+
+ private void jumpToRow(final int row) {
+ selectedRow = row;
+ getListView().setSelection(row);
+ }
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
+ this.searchText = searchText;
}
public void run() {
Log.d("THAD", "SearchOperation: " + searchText);
-
+ final int indexLocation = activeLanguge.lookup(searchText, interrupted);
+ if (interrupted.get()) {
+ return;
+ }
+ final IndexEntry indexEntry = activeLanguge.sortedIndex
+ .get(indexLocation);
uiHandler.post(new Runnable() {
public void run() {
- synchronized (mutex) {
- dictionaryListAdapter.notifyDataSetChanged();
- }
+ jumpToRow(indexEntry.startRow);
}
});
}
private class DictionaryListAdapter extends BaseAdapter {
public int getCount() {
- return dictionary.numRows();
+ return activeLanguge.rows.size();
}
public Dictionary.Row getItem(int position) {
- assert position < dictionary.numRows();
- try {
- return dictionary.getRow(position);
- } catch (IOException e) {
- throw new RuntimeException(e);
- }
+ assert position < activeLanguge.rows.size();
+ return activeLanguge.rows.get(position);
}
public long getItemId(int position) {
public View getView(final int position, final View convertView,
final ViewGroup parent) {
- TextView result = null;
- if (convertView instanceof TextView) {
- result = (TextView) convertView;
+ final Row row = getItem(position);
+ if (row.isToken()) {
+ TextView result = null;
+ if (convertView instanceof TextView) {
+ result = (TextView) convertView;
+ } else {
+ result = new TextView(parent.getContext());
+ }
+ result.setText(activeLanguge.rowToString(row));
+ result.setTextAppearance(parent.getContext(),
+ android.R.style.TextAppearance_Large);
+ return result;
+ }
+
+ TableLayout result = null;
+ if (convertView instanceof TableLayout) {
+ result = (TableLayout) convertView;
} else {
- result = new TextView(parent.getContext());
+ result = new TableLayout(parent.getContext());
}
- final Row row = getItem(position);
- result.setText(row.text);
- result.setBackgroundColor(row.isWord ? );
+
+ TableRow tableRow = null;
+ if (result.getChildCount() != 1) {
+ result.removeAllViews();
+ tableRow = new TableRow(result.getContext());
+ result.addView(tableRow);
+ } else {
+ tableRow = (TableRow) result.getChildAt(0);
+ }
+ TextView column1 = null;
+ TextView column2 = null;
+ if (tableRow.getChildCount() != 2
+ || !(tableRow.getChildAt(0) instanceof TextView)
+ || !(tableRow.getChildAt(1) instanceof TextView)) {
+ tableRow.removeAllViews();
+ column1 = new TextView(tableRow.getContext());
+ column2 = new TextView(tableRow.getContext());
+ tableRow.addView(column1);
+ tableRow.addView(column2);
+ } else {
+ column1 = (TextView) tableRow.getChildAt(0);
+ column2 = (TextView) tableRow.getChildAt(1);
+ }
+ column1.setWidth(100);
+ column2.setWidth(100);
+ // column1.setTextAppearance(parent.getContext(), android.R.style.Text);
+ final Entry entry = dictionary.entries.get(row.getIndex());
+ column1.setText(entry.getAllText(activeLanguge.lang));
+ column2.setText(entry.getAllText(Entry.otherLang(activeLanguge.lang)));
+ // result.setTextAppearance(parent.getContext(),
+ // android.R.style.TextAppearance_Small);
return result;
+
}
}