]> gitweb.fperrin.net Git - Dictionary.git/commitdiff
go
authorthadh <thadh@THADH-MTV.ad.corp.google.com>
Thu, 9 Apr 2009 00:38:41 +0000 (17:38 -0700)
committerthadh <thadh@THADH-MTV.ad.corp.google.com>
Thu, 9 Apr 2009 00:38:41 +0000 (17:38 -0700)
src/com/hughes/android/dictionary/Dictionary.java
src/com/hughes/android/dictionary/DictionaryActivity.java
src/com/hughes/android/dictionary/Entry.java
src/com/hughes/android/dictionary/EntryFactory.java [deleted file]
src/com/hughes/android/dictionary/Index.java [deleted file]
src/com/hughes/android/dictionary/MemoryIndex.java [deleted file]

index 6645c9c5fd4f6ac64590b644aff4c38a2f7c5c7d..7c1565a65b3c9743953eb923769609701b5390bb 100755 (executable)
@@ -3,7 +3,6 @@ package com.hughes.android.dictionary;
 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
@@ -25,45 +24,46 @@ public final class Dictionary implements RAFSerializable<Dictionary> {
       IndexEntry.RAF_FACTORY);\r
 \r
   final List<Entry> entries;\r
-  final Language[] languages = new Language[2];\r
+  final LanguageData[] languageDatas = new LanguageData[2];\r
 \r
-  public Dictionary(final String lang0, final String lang1) {\r
-    languages[0] = new Language(lang0, Entry.LANG1);\r
-    languages[1] = new Language(lang1, Entry.LANG2);\r
+  public Dictionary(final Language language0, final Language language1) {\r
+    languageDatas[0] = new LanguageData(language0, Entry.LANG1);\r
+    languageDatas[1] = new LanguageData(language1, Entry.LANG2);\r
     entries = new ArrayList<Entry>();\r
   }\r
 \r
   public Dictionary(final RandomAccessFile raf) throws IOException {\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
+    languageDatas[0] = new LanguageData(raf, Entry.LANG1);\r
+    languageDatas[1] = new LanguageData(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
+    languageDatas[0].write(raf);\r
+    languageDatas[1].write(raf);\r
   }\r
 \r
-  final class Language implements RAFSerializable<Language> {\r
+  final class LanguageData implements RAFSerializable<LanguageData> {\r
+    final Language language;\r
     final byte lang;\r
-    final String symbol;\r
     final List<Row> rows;\r
     final List<IndexEntry> sortedIndex;\r
-    final Comparator<String> comparator = EntryFactory.entryFactory\r
-        .getEntryComparator();\r
 \r
-    Language(final String symbol, final byte lang) {\r
+    LanguageData(final Language language, final byte lang) {\r
+      this.language = language;\r
       this.lang = lang;\r
-      this.symbol = symbol;\r
       rows = new ArrayList<Row>();\r
       sortedIndex = new ArrayList<IndexEntry>();\r
     }\r
 \r
-    Language(final RandomAccessFile raf, final byte lang) throws IOException {\r
+    LanguageData(final RandomAccessFile raf, final byte lang) throws IOException {\r
+      language = Language.lookup(raf.readUTF());\r
+      if (language == null) {\r
+        throw new RuntimeException("Unknown language.");\r
+      }\r
       this.lang = lang;\r
-      symbol = raf.readUTF();\r
       rows = CachingList.create(UniformFileList.create(raf, ROW_SERIALIZER, raf\r
           .getFilePointer()), 10000);\r
       sortedIndex = CachingList.create(FileList.create(raf,\r
@@ -71,7 +71,7 @@ public final class Dictionary implements RAFSerializable<Dictionary> {
     }\r
 \r
     public void write(final RandomAccessFile raf) throws IOException {\r
-      raf.writeUTF(symbol);\r
+      raf.writeUTF(language.symbol);\r
       UniformFileList.write(raf, rows, ROW_SERIALIZER, 4);\r
       FileList.write(raf, sortedIndex, INDEX_ENTRY_SERIALIZER);\r
     }\r
@@ -93,10 +93,10 @@ public final class Dictionary implements RAFSerializable<Dictionary> {
         }\r
         final IndexEntry midEntry = sortedIndex.get(mid);\r
 \r
-        final int comp = comparator.compare(word, midEntry.word.toLowerCase());\r
+        final int comp = language.tokenComparator.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
+          while (result > 0 && language.tokenComparator.compare(word, sortedIndex.get(result - 1).word.toLowerCase()) == 0) {\r
             --result;\r
             if (interrupted.get()) {\r
               return result;\r
@@ -109,13 +109,33 @@ public final class Dictionary implements RAFSerializable<Dictionary> {
           start = mid + 1;\r
         }\r
       }\r
-      return start;\r
+      return Math.min(sortedIndex.size() - 1, start);\r
+    }\r
+\r
+    public int getTokenRow(final int rowIndex) {\r
+      int r = rowIndex;\r
+      Row row;\r
+      while (true) {\r
+        row = rows.get(r); \r
+        if (row.isToken() || row.tokenRow != -1) {\r
+          break;\r
+        }\r
+        --r;\r
+      }\r
+      final int result = row.isToken() ? r : row.tokenRow;\r
+      for (; r <= rowIndex; ++r) {\r
+        rows.get(r).tokenRow = result;\r
+      }\r
+      assert rows.get(result).isToken();\r
+      return result;\r
     }\r
   }\r
 \r
   public static final class Row implements RAFSerializable<Row> {\r
     final int index;\r
 \r
+    int tokenRow = -1;\r
+\r
     public Row(final int index) {\r
       this.index = index;\r
     }\r
index 0e300865af98d474c3a28b9cdf2c8623bf239ac8..382e81ce6155bfe4bbef9fb4d00d891fd7ea1b08 100755 (executable)
@@ -10,12 +10,14 @@ import java.util.concurrent.Executors;
 import java.util.concurrent.atomic.AtomicBoolean;
 
 import android.app.ListActivity;
+import android.graphics.Color;
 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.Menu;
 import android.view.MenuItem;
 import android.view.View;
@@ -32,16 +34,17 @@ import android.widget.TableLayout;
 import android.widget.TableRow;
 import android.widget.TextView;
 import android.widget.AdapterView.OnItemLongClickListener;
+import android.widget.AdapterView.OnItemSelectedListener;
 
 import com.hughes.android.dictionary.Dictionary.IndexEntry;
-import com.hughes.android.dictionary.Dictionary.Language;
+import com.hughes.android.dictionary.Dictionary.LanguageData;
 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 LanguageData activeLangaugeData = null;
 
   private File wordList = new File("/sdcard/wordList.txt");
 
@@ -51,7 +54,8 @@ public class DictionaryActivity extends ListActivity {
   private SearchOperation searchOperation = null;
   // private List<Entry> entries = Collections.emptyList();
   private DictionaryListAdapter dictionaryListAdapter = new DictionaryListAdapter();
-  private int selectedRow = -1;
+  private int selectedRowIndex = -1;
+  private int selectedTokenRowIndex = -1;
 
   /** Called when the activity is first created. */
   @Override
@@ -62,7 +66,7 @@ public class DictionaryActivity extends ListActivity {
     try {
       dictRaf = new RandomAccessFile("/sdcard/de-en.dict", "r");
       dictionary = new Dictionary(dictRaf);
-      activeLanguge = dictionary.languages[Entry.LANG1];
+      activeLangaugeData = dictionary.languageDatas[Entry.LANG1];
     } catch (Exception e) {
       throw new RuntimeException(e);
     }
@@ -75,6 +79,8 @@ public class DictionaryActivity extends ListActivity {
     setListAdapter(dictionaryListAdapter);
 
     onSearchTextChange("");
+    
+    // Language button.
     final Button langButton = (Button) findViewById(R.id.LangButton);
     langButton.setOnClickListener(new OnClickListener() {
       public void onClick(View v) {
@@ -82,18 +88,62 @@ public class DictionaryActivity extends ListActivity {
       }});
     updateLangButton();
 
+    final Button upButton = (Button) findViewById(R.id.UpButton);
+    upButton.setOnClickListener(new OnClickListener() {
+      public void onClick(View v) {
+        final int destRowIndex;
+        final Row tokenRow = activeLangaugeData.rows.get(selectedTokenRowIndex);
+        assert tokenRow.isToken();
+        final int prevTokenIndex = tokenRow.getIndex() - 1;
+        if (selectedRowIndex == selectedTokenRowIndex && selectedRowIndex > 0) {
+          destRowIndex = activeLangaugeData.sortedIndex.get(prevTokenIndex).startRow;
+        } else {
+          destRowIndex = selectedTokenRowIndex;
+        }
+        jumpToRow(destRowIndex);
+      }});
+    final Button downButton = (Button) findViewById(R.id.DownButton);
+    downButton.setOnClickListener(new OnClickListener() {
+      public void onClick(View v) {
+        final Row tokenRow = activeLangaugeData.rows.get(selectedTokenRowIndex);
+        assert tokenRow.isToken();
+        final int nextTokenIndex = tokenRow.getIndex() + 1;
+        final int destRowIndex;
+        if (nextTokenIndex < activeLangaugeData.sortedIndex.size()) {
+          destRowIndex = activeLangaugeData.sortedIndex.get(nextTokenIndex).startRow;
+        } else {
+          destRowIndex = activeLangaugeData.rows.size() - 1;
+        }
+        jumpToRow(destRowIndex);
+      }});
+
+    // Context menu.
     registerForContextMenu(getListView());
+    
+    getListView().setOnItemSelectedListener(new OnItemSelectedListener() {
+      public void onItemSelected(AdapterView<?> arg0, View arg1, int rowIndex,
+          long arg3) {
+        Log.d("THAD", "onItemSelected: " + rowIndex);
+        selectedRowIndex = rowIndex;
+        selectedTokenRowIndex = activeLangaugeData.getTokenRow(rowIndex);
+        updateSearchText();
+      }
+
+      public void onNothingSelected(AdapterView<?> arg0) {
+      }});
+    
+    
     getListView().setOnItemLongClickListener((new OnItemLongClickListener() {
-      public boolean onItemLongClick(AdapterView<?> arg0, View arg1, int arg2,
+      public boolean onItemLongClick(AdapterView<?> arg0, View arg1, int rowIndex,
           long arg3) {
-        selectedRow = arg2;
+        selectedRowIndex = rowIndex;
         return false;
       }
     }));
   }
   
   public String getSelectedRowText() {
-    return activeLanguge.rowToString(activeLanguge.rows.get(selectedRow));
+    return activeLangaugeData.rowToString(activeLangaugeData.rows.get(selectedRowIndex));
   }
 
   private MenuItem switchLanguageMenuItem = null;
@@ -106,7 +156,7 @@ public class DictionaryActivity extends ListActivity {
   
   @Override
   public boolean onPrepareOptionsMenu(final Menu menu) {
-    switchLanguageMenuItem.setTitle(String.format("Switch to %s", dictionary.languages[Entry.otherLang(activeLanguge.lang)].symbol));
+    switchLanguageMenuItem.setTitle(String.format("Switch to %s", dictionary.languageDatas[Entry.otherLang(activeLangaugeData.lang)].language.symbol));
     return super.onPrepareOptionsMenu(menu);
   }
   
@@ -121,7 +171,7 @@ public class DictionaryActivity extends ListActivity {
   @Override
   public void onCreateContextMenu(ContextMenu menu, View v,
       ContextMenuInfo menuInfo) {
-    if (selectedRow == -1) {
+    if (selectedRowIndex == -1) {
       return;
     }
     final MenuItem addToWordlist = menu.add("Add to wordlist.");
@@ -142,7 +192,7 @@ public class DictionaryActivity extends ListActivity {
   }
   
   void switchLanguage() {
-    activeLanguge = dictionary.languages[(activeLanguge == dictionary.languages[0]) ? 1 : 0];
+    activeLangaugeData = dictionary.languageDatas[(activeLangaugeData == dictionary.languageDatas[0]) ? 1 : 0];
     updateLangButton();
     dictionaryListAdapter.notifyDataSetChanged();
     final EditText searchText = (EditText) findViewById(R.id.SearchText);
@@ -151,12 +201,26 @@ public class DictionaryActivity extends ListActivity {
   
   void updateLangButton() {
     final Button langButton = (Button) findViewById(R.id.LangButton);
-    langButton.setText(dictionary.languages[activeLanguge.lang].symbol.toUpperCase());
+    langButton.setText(activeLangaugeData.language.symbol);
+  }
+  
+  @Override
+  public boolean onKeyDown(int keyCode, KeyEvent event) {
+    if (event.getUnicodeChar() != 0) {
+      final EditText searchText = (EditText) findViewById(R.id.SearchText);
+      if (!searchText.hasFocus()) {
+        searchText.setText("" + (char)event.getUnicodeChar());
+        onSearchTextChange(searchText.getText().toString());
+        searchText.requestFocus();
+      }
+      return true;
+    }
+    return super.onKeyDown(keyCode, event);
   }
 
   @Override
   protected void onListItemClick(ListView l, View v, int row, long id) {
-    selectedRow = row;
+    selectedRowIndex = row;
     Log.d("THAD", "Clicked: " + getSelectedRowText());
     openContextMenu(getListView());
   }
@@ -170,9 +234,19 @@ public class DictionaryActivity extends ListActivity {
     searchExecutor.execute(searchOperation);
   }
   
-  private void jumpToRow(final int row) {
-    selectedRow = row;
-    getListView().setSelection(row);
+  private void jumpToRow(final int rowIndex) {
+    selectedRowIndex = rowIndex;
+    selectedTokenRowIndex = activeLangaugeData.getTokenRow(rowIndex);
+    getListView().setSelection(rowIndex);
+    getListView().setSelected(true);
+    updateSearchText();
+  }
+
+  private void updateSearchText() {
+    final EditText searchText = (EditText) findViewById(R.id.SearchText);
+    if (!searchText.hasFocus()) {
+      searchText.setText(activeLangaugeData.rowToString(activeLangaugeData.rows.get(selectedTokenRowIndex)));
+    }
   }
 
   private final class SearchOperation implements Runnable {
@@ -185,11 +259,11 @@ public class DictionaryActivity extends ListActivity {
 
     public void run() {
       Log.d("THAD", "SearchOperation: " + searchText);
-      final int indexLocation = activeLanguge.lookup(searchText, interrupted);
+      final int indexLocation = activeLangaugeData.lookup(searchText, interrupted);
       if (interrupted.get()) {
         return;
       }
-      final IndexEntry indexEntry = activeLanguge.sortedIndex
+      final IndexEntry indexEntry = activeLangaugeData.sortedIndex
           .get(indexLocation);
       uiHandler.post(new Runnable() {
         public void run() {
@@ -202,21 +276,23 @@ public class DictionaryActivity extends ListActivity {
   private class DictionaryListAdapter extends BaseAdapter {
 
     public int getCount() {
-      return activeLanguge.rows.size();
+      return activeLangaugeData.rows.size();
     }
 
-    public Dictionary.Row getItem(int position) {
-      assert position < activeLanguge.rows.size();
-      return activeLanguge.rows.get(position);
+    public Dictionary.Row getItem(int rowIndex) {
+      assert rowIndex < activeLangaugeData.rows.size();
+      return activeLangaugeData.rows.get(rowIndex);
     }
 
-    public long getItemId(int position) {
-      return position;
+    public long getItemId(int rowIndex) {
+      return rowIndex;
     }
 
-    public View getView(final int position, final View convertView,
+    public View getView(final int rowIndex, final View convertView,
         final ViewGroup parent) {
-      final Row row = getItem(position);
+      final Row row = getItem(rowIndex);
+      
+      // Token row.
       if (row.isToken()) {
         TextView result = null;
         if (convertView instanceof TextView) {
@@ -224,53 +300,56 @@ public class DictionaryActivity extends ListActivity {
         } else {
           result = new TextView(parent.getContext());
         }
-        result.setText(activeLanguge.rowToString(row));
+//        result.setBackgroundColor(Color.WHITE);
+        result.setText(activeLangaugeData.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 TableLayout(parent.getContext());
-      }
+      // Entry row(s).
+      final TableLayout result = new TableLayout(parent.getContext());
 
-      TableRow tableRow = null;
-      if (result.getChildCount() != 1) {
-        result.removeAllViews();
-        tableRow = new TableRow(result.getContext());
+      final Entry entry = dictionary.entries.get(row.getIndex());
+      final int rowCount = entry.getRowCount();
+      for (int r = 0; r < rowCount; ++r) {
+        final TableRow tableRow = new TableRow(result.getContext());
+//        if (r > 0) {
+//          tableRow.setBackgroundColor(Color.DKGRAY);  
+//        }
+        
+        TextView column1 = new TextView(tableRow.getContext());
+        TextView column2 = new TextView(tableRow.getContext());
+        final TableRow.LayoutParams layoutParams = new TableRow.LayoutParams();
+        layoutParams.weight = 0.5f;
+        
+        if (r>0){
+          final TextView spacer = new TextView(tableRow.getContext());
+          spacer.setText(r == 0 ? "\95 " : " \95 ");
+          tableRow.addView(spacer);
+        }
+        tableRow.addView(column1, layoutParams);
+        if (r>0){
+          final TextView spacer = new TextView(tableRow.getContext());
+          spacer.setText(r == 0 ? "\95 " : " \95 ");
+          tableRow.addView(spacer);
+        }
+        tableRow.addView(column2, layoutParams);
+        
+        column1.setWidth(1);
+        column2.setWidth(1);
+        // column1.setTextAppearance(parent.getContext(), android.R.style.Text);
+        
+        // TODO: highlight query word in entries.
+        column1.setText(entry.getAllText(activeLangaugeData.lang)[r]);
+        column2.setText(entry.getAllText(Entry.otherLang(activeLangaugeData.lang))[r]);
+        
         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;
-
     }
-  }
+  }  // DictionaryListAdapter
 
   private class DictionaryTextWatcher implements TextWatcher {
     public void afterTextChanged(Editable searchText) {
index 6d8576477029d939a0813e5cfc0d4526efd2242c..d8cd9a67a15debc7672caf723b8000119dffafd7 100755 (executable)
@@ -2,6 +2,13 @@ package com.hughes.android.dictionary;
 \r
 import java.io.IOException;\r
 import java.io.RandomAccessFile;\r
+import java.util.ArrayList;\r
+import java.util.Arrays;\r
+import java.util.LinkedHashMap;\r
+import java.util.LinkedHashSet;\r
+import java.util.List;\r
+import java.util.Map;\r
+import java.util.Set;\r
 import java.util.regex.Pattern;\r
 \r
 import com.hughes.util.raf.RAFFactory;\r
@@ -12,23 +19,54 @@ public final class Entry implements RAFSerializable<Entry> {
   static final byte LANG1 = 0;\r
   static final byte LANG2 = 1;\r
 \r
-  static final Pattern lineSplitPattern = Pattern.compile("\\s+::\\s+");\r
+  static final Pattern lineSplitPattern = Pattern.compile("\\s::\\s");\r
+  static final Pattern sublineSplitPattern = Pattern.compile("\\s\\|\\s");\r
 \r
-  final String lang1;\r
-  final String lang2;\r
+  final String[] lang1;\r
+  final String[] lang2;\r
   \r
+//  public Entry(final String lang1, final String lang2) {\r
+//    this.lang1 = new String[] {lang1};\r
+//    this.lang2 = new String[] {lang2};\r
+//  }\r
+\r
+  Entry(final String[] lang1, final String[] lang2) {\r
+    this.lang1 = lang1;\r
+    this.lang2 = lang2;\r
+  }\r
+\r
+  public static final RAFFactory<Entry> RAF_FACTORY = new RAFFactory<Entry>() {\r
+    public Entry create(RandomAccessFile raf) throws IOException {\r
+      final int rows = raf.readByte();\r
+      final String[] lang1 = new String[rows];\r
+      final String[] lang2 = new String[rows];\r
+      for (int i = 0; i < lang1.length; ++i) {\r
+        lang1[i] = raf.readUTF();\r
+        lang2[i] = raf.readUTF();\r
+      }\r
+      return new Entry(lang1, lang2);\r
+    }};\r
+  public void write(RandomAccessFile raf) throws IOException {\r
+    assert lang1.length == (byte) lang1.length;\r
+    raf.writeByte(lang1.length);\r
+    for (int i = 0; i < lang1.length; ++i) {\r
+      raf.writeUTF(lang1[i]);\r
+      raf.writeUTF(lang2[i]);\r
+    }\r
+  }\r
+\r
   @Override\r
   public boolean equals(Object o) {\r
     if (!(o instanceof Entry)) {\r
       return false;\r
     }\r
     final Entry that = (Entry) o;\r
-    return that.lang1.equals(lang1) && that.lang2.equals(lang2);\r
+    return Arrays.deepEquals(this.lang1, that.lang1) && Arrays.deepEquals(this.lang2, that.lang2); \r
   }\r
 \r
   @Override\r
   public int hashCode() {\r
-    return lang1.hashCode() + lang2.hashCode();\r
+    return Arrays.deepHashCode(lang1) + Arrays.deepHashCode(lang2);\r
   }\r
 \r
   @Override\r
@@ -36,23 +74,12 @@ public final class Entry implements RAFSerializable<Entry> {
     return getRawText();\r
   }\r
 \r
-  public Entry(String lang1, String lang2) {\r
-    this.lang1 = lang1;\r
-    this.lang2 = lang2;\r
-  }\r
-\r
-  public static final RAFFactory<Entry> RAF_FACTORY = new RAFFactory<Entry>() {\r
-    public Entry create(RandomAccessFile raf) throws IOException {\r
-      final String lang1 = raf.readUTF();\r
-      final String lang2 = raf.readUTF();\r
-      return new Entry(lang1, lang2);\r
-    }};\r
-  public void write(RandomAccessFile raf) throws IOException {\r
-    raf.writeUTF(lang1);\r
-    raf.writeUTF(lang2);\r
+  public int getRowCount() {\r
+    assert lang1.length == lang2.length;\r
+    return lang1.length;\r
   }\r
 \r
-  String getAllText(final byte lang) {\r
+  String[] getAllText(final byte lang) {\r
     if (lang == LANG1) {\r
       return lang1;\r
     }\r
@@ -60,35 +87,86 @@ public final class Entry implements RAFSerializable<Entry> {
     return lang2;\r
   }\r
   \r
-  String getIndexableText(final byte lang) {\r
-    String text = getAllText(lang);\r
-    text = text.replaceAll("[\"\\.!?,]", "");\r
-    text = text.replaceAll("\\{[^}]+\\}", "");\r
-    return text;\r
-  }\r
-\r
-  String getFormattedEntry(final byte lang) {\r
-    return getAllText(lang) + "\n" + getAllText(otherLang(lang));\r
-  }\r
-\r
   String getRawText() {\r
-    return getAllText(LANG1) + " :: " + getAllText(LANG2);\r
+    return Arrays.asList(getAllText(LANG1)) + "\t" + Arrays.asList(getAllText(LANG2));\r
   }\r
   \r
   static byte otherLang(final byte lang) {\r
     assert lang == LANG1 || lang == LANG2;\r
     return lang == LANG1 ? LANG2 : LANG1;\r
   }\r
-\r
   \r
 \r
-  static Entry parseFromLine(final String line) {\r
+  static Entry parseFromLine(String line, final boolean hasMultipleSubentries) {\r
+    line = line.replaceAll("&lt;", "<");\r
+    line = line.replaceAll("&gt;", ">");\r
     final String[] parts = lineSplitPattern.split(line);\r
     if (parts.length != 2) {\r
       System.err.println("Entry:" + "Invalid line: " + line);\r
       return null;\r
     }\r
-    return new Entry(parts[0], parts[1]);\r
+    if (!hasMultipleSubentries) {\r
+      return new Entry(new String[] {parts[0].trim()}, new String[] {parts[1].trim()});\r
+    }\r
+    \r
+    final String[] lang1 = sublineSplitPattern.split(" " + parts[0].trim() + " ");\r
+    final String[] lang2 = sublineSplitPattern.split(" " + parts[1].trim() + " ");\r
+    if (lang1.length != lang2.length) {\r
+      System.err.println("Entry:" + "Invalid subline: " + line);\r
+      return null;\r
+    }\r
+    for (int i = 0; i < lang1.length; ++i) {\r
+      lang1[i] = lang1[i].trim();\r
+      lang2[i] = lang2[i].trim();\r
+    }\r
+    return new Entry(lang1, lang2);\r
+  }\r
+  \r
+  static final Map<String, String> bracketToClose = new LinkedHashMap<String, String>();\r
+  static {\r
+    bracketToClose.put("\"", "\"");\r
+    bracketToClose.put(" '", "' ");\r
+  }\r
+  \r
+  static final Pattern WHITESPACE = Pattern.compile("\\s+");\r
+  \r
+  public Set<String> getIndexableTokens(final byte lang) {\r
+    final Set<String> result = new LinkedHashSet<String>();\r
+    String text = Arrays.asList(getAllText(lang)).toString();\r
+\r
+    text = text.replaceAll("fig\\.", " ");\r
+    text = text.replaceAll("\\{[^\\}]+}", " ");\r
+    text = text.replaceAll("\"-", "-");\r
+    text = text.replaceAll("-\"", "-");\r
+    text = text.replaceAll("[\":/\\()<>\\[\\],;?!.]", " ");\r
+    result.addAll(Arrays.asList(WHITESPACE.split(text)));\r
+\r
+    text = text.replaceAll("[-]", " ");\r
+    result.addAll(Arrays.asList(WHITESPACE.split(text)));\r
+    \r
+    final Set<String> result2 = new LinkedHashSet<String>();\r
+    for (final String token : result) {\r
+      if (isIndexable(token)) {\r
+        result2.add(token);\r
+      }\r
+    }\r
+    return result2;\r
+  }\r
+\r
+  static boolean isIndexable(final String text) {\r
+    // Does it have an alpha-numeric anywhere?\r
+    return text.matches(".*\\w.*");\r
+  }\r
+  \r
+  static List<String> getTextInside(final String text, final String start, final String end) {\r
+    final List<String> result = new ArrayList<String>();\r
+    int startPos = 0;\r
+    while ((startPos = text.indexOf(start)) != -1) {\r
+      final int endPos = text.indexOf(end, startPos + 1);\r
+      result.add(text.substring(startPos + 1, endPos));\r
+      startPos = endPos + 1;\r
+    }\r
+    return result;\r
   }\r
 \r
-}\r
+}
\ No newline at end of file
diff --git a/src/com/hughes/android/dictionary/EntryFactory.java b/src/com/hughes/android/dictionary/EntryFactory.java
deleted file mode 100755 (executable)
index 3cae708..0000000
+++ /dev/null
@@ -1,31 +0,0 @@
-package com.hughes.android.dictionary;\r
-\r
-import java.util.Comparator;\r
-\r
-import com.hughes.util.StringUtil;\r
-\r
-public class EntryFactory {\r
-  \r
-  static final EntryFactory entryFactory = new EntryFactory();\r
-\r
-  public String normalizeToken(final String token) {\r
-    return token.toLowerCase().replaceAll("ß", "ss").replaceAll("ä", "a")\r
-        .replaceAll("ae", "a").replaceAll("ö", "o").replaceAll("oe", "o")\r
-        .replaceAll("ü", "u").replaceAll("ue", "u").replaceAll("[^A-Za-z0-9]",\r
-            "");\r
-  }\r
-  \r
-  public Comparator<String> getEntryComparator() {\r
-    return new Comparator<String>() {\r
-      public int compare(final String s1, final String s2) {\r
-        final String norm1 = normalizeToken(s1);\r
-        final String norm2 = normalizeToken(s2);\r
-        final int c = norm1.compareTo(norm2);\r
-        if (c != 0) {\r
-          return c;\r
-        }\r
-        return StringUtil.reverse(s1).compareTo(StringUtil.reverse(s2));\r
-      }};\r
-  }\r
-\r
-}\r
diff --git a/src/com/hughes/android/dictionary/Index.java b/src/com/hughes/android/dictionary/Index.java
deleted file mode 100755 (executable)
index 9981a9f..0000000
+++ /dev/null
@@ -1,188 +0,0 @@
-package com.hughes.android.dictionary;\r
-\r
-import java.io.IOException;\r
-import java.io.RandomAccessFile;\r
-import java.util.Map;\r
-import java.util.Set;\r
-import java.util.TreeMap;\r
-\r
-import com.hughes.util.LRUCacheMap;\r
-\r
-\r
-public final class Index {\r
-  \r
-  final String filename;\r
-  final RandomAccessFile file;\r
-  \r
-  final Node root;\r
-  final Map<Integer,Node> indexOffsetToNode = new LRUCacheMap<Integer,Node>(5000);\r
-  \r
-  \r
-  public Index(final String filename) throws IOException {\r
-    this.filename = filename;\r
-    file = new RandomAccessFile(filename, "r");\r
-    root = getNode(new NodeHandle("", 0));\r
-  }\r
-  \r
-  public Node lookup(final String normalizedToken) throws IOException {\r
-    return lookup(normalizedToken, 0, root);\r
-  }\r
-  \r
-  private Node lookup(final String normalizedToken, final int pos, final Node node) throws IOException {\r
-    if (pos == normalizedToken.length()) {\r
-      return node;\r
-    }\r
-    \r
-    // Check whether any prefix of the token is a child.\r
-    for (int i = pos + 1; i <= normalizedToken.length(); ++i) {\r
-      final NodeHandle childHandle = node.children.get(normalizedToken.substring(pos, i));\r
-      if (childHandle != null) {\r
-        return lookup(normalizedToken, i, childHandle.getNode());\r
-      }\r
-    }\r
-    \r
-    // Check whether any child starts with what's left of text.\r
-    final String remainder = normalizedToken.substring(pos);\r
-    for (final Map.Entry<String, NodeHandle> childHandle : node.children.entrySet()) {\r
-      if (childHandle.getKey().startsWith(remainder)) {\r
-        return getNode(childHandle.getValue());\r
-      }\r
-    }\r
-    \r
-    return node;\r
-  }\r
-  \r
-  private Node getNode(final NodeHandle nodeHandle) throws IOException {\r
-    Node node = indexOffsetToNode.get(nodeHandle.indexOffset);\r
-    if (node == null) {\r
-      node = new Node(nodeHandle);\r
-      indexOffsetToNode.put(nodeHandle.indexOffset, node);\r
-    }\r
-    return node;\r
-  }\r
-  \r
-  final class NodeHandle {\r
-    final String normalizedToken;\r
-    final int indexOffset;\r
-\r
-    NodeHandle(final String normalizedToken, final int indexOffset) throws IOException {\r
-      this.normalizedToken = normalizedToken;\r
-      this.indexOffset = indexOffset;\r
-    }\r
-    \r
-    Node getNode() throws IOException {\r
-      return Index.this.getNode(this);\r
-    }\r
-  }\r
-\r
-  final class Node {\r
-    final NodeHandle nodeHandle;\r
-    final TreeMap<String,NodeHandle> children;\r
-    final Map<String,int[]> tokenToOffsets = new TreeMap<String, int[]>(EntryFactory.entryFactory.getEntryComparator());\r
-    final int descendantTokenCount;\r
-    final int descendantEntryCount;\r
-    \r
-    Node(final NodeHandle nodeHandle) throws IOException {\r
-      this.nodeHandle = nodeHandle;\r
-      \r
-      file.seek(nodeHandle.indexOffset);\r
-      \r
-      // Read children to offset.\r
-      final int numChildren = file.readInt();\r
-      children = new TreeMap<String, NodeHandle>();\r
-      for (int i = 0; i < numChildren; ++i) {\r
-        final String chunk = file.readUTF().intern();\r
-        if (chunk.length() == 0) {\r
-          throw new IOException("Empty string chunk.");\r
-        }\r
-        children.put(chunk, new NodeHandle(nodeHandle.normalizedToken + chunk, file.readInt()));\r
-      }\r
-    \r
-      // Read tokens.\r
-      final int numTokens = file.readInt();\r
-      for (int i = 0; i < numTokens; ++i) {\r
-        final String token = file.readUTF();\r
-        assert EntryFactory.entryFactory.normalizeToken(token).equals(nodeHandle.normalizedToken);\r
-        final int[] offsets = new int[file.readInt()];\r
-        for (int j = 0; j < offsets.length; ++j) {\r
-          offsets[j]= file.readInt();\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(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
-          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
-      for (final int[] offsets : tokenToOffsets.values()) {\r
-        for (final int offset : offsets) {\r
-          if (entryOffsets.size() >= maxSize) {\r
-            return;\r
-          }\r
-          entryOffsets.add(offset);\r
-        }\r
-      }\r
-      if (entryOffsets.size() >= maxSize) {\r
-        return;\r
-      }\r
-      for (final Map.Entry<String, NodeHandle> childEntry : children.entrySet()) {\r
-        final Node child = childEntry.getValue().getNode();\r
-        child.getDescendantEntryOffsets(entryOffsets, maxSize);\r
-        if (entryOffsets.size() >= maxSize) {\r
-          return;\r
-        }\r
-      }\r
-    }\r
-  }\r
-  \r
-}\r
diff --git a/src/com/hughes/android/dictionary/MemoryIndex.java b/src/com/hughes/android/dictionary/MemoryIndex.java
deleted file mode 100755 (executable)
index c269a65..0000000
+++ /dev/null
@@ -1,63 +0,0 @@
-package com.hughes.android.dictionary;\r
-\r
-import java.io.DataInputStream;\r
-import java.io.DataOutputStream;\r
-import java.io.IOException;\r
-import java.io.Serializable;\r
-\r
-\r
-public final class MemoryIndex {\r
-  \r
-  private static final long serialVersionUID = 3375180767865334065L;\r
-\r
-  static final class Node implements Serializable {\r
-\r
-    private static final long serialVersionUID = 8824115665859184225L;\r
-\r
-    final String[] chars;\r
-    final Node[] children;\r
-    final int[] offsets;\r
-    \r
-    Node(final int numChildren, final int numOffsets) {\r
-      chars = new String[numChildren];\r
-      children = new Node[numChildren];\r
-      offsets = new int[numOffsets];\r
-    }\r
-    \r
-    int descendantCount() {\r
-      int total = 1;\r
-      for (final Node child : children) {\r
-        total += child.descendantCount();\r
-      }\r
-      return total;\r
-    }\r
-    \r
-    Node(final DataInputStream is) throws IOException {\r
-      final int numChildren = is.readInt();\r
-      chars = new String[numChildren];\r
-      children = new Node[numChildren];\r
-      for (int i = 0; i < numChildren; ++i) {\r
-        chars[i] = is.readUTF().intern();\r
-        children[i] = new Node(is);\r
-      }\r
-      final int numOffsets = is.readInt();\r
-      offsets = new int[numOffsets];\r
-      for (int i = 0; i < numOffsets; ++i) {\r
-        offsets[i] = is.readInt();\r
-      }\r
-    }\r
-    \r
-    void write(final DataOutputStream os) throws IOException {\r
-      os.writeInt(chars.length);\r
-      for (int i = 0; i < chars.length; i++) {\r
-        os.writeUTF(chars[i]);\r
-        children[i].write(os);\r
-      }\r
-      os.writeInt(offsets.length);\r
-      for (int i = 0; i < offsets.length; i++) {\r
-        os.writeInt(offsets[i]);\r
-      }\r
-    }\r
-  }\r
-  \r
-}\r