--- /dev/null
+<ScrollView android:id="@+id/ScrollView01"
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="fill_parent"
+ android:layout_height="fill_parent">
+
+ <TextView
+ android:id="@+id/text"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center_horizontal"/>
+
+</ScrollView>
<Button
android:id="@+id/ClearSearchTextButton"
- android:text="<x"
- android:minWidth="50dip"
+ android:drawableLeft="@+drawable/ic_input_delete"
+ android:background="#00000000"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
- />
+ /> <!-- transparent -->
<Button
android:id="@+id/LangButton"
- android:text="LANG"
+ android:text=""
android:minWidth="50dip"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
/>
<Button
- android:id="@+id/DownButton"
- android:text="v"
- android:minWidth="50dip"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- />
+ android:id="@+id/DownButton"
+ android:drawableLeft="@+drawable/arrow_down_float"
+ android:background="#00000000"
+ android:minWidth="30dip"
+ android:minHeight="30dip"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ />
<Button
android:id="@+id/UpButton"
- android:text="^"
- android:minWidth="50dip"
+ android:drawableLeft="@+drawable/arrow_up_float"
+ android:background="#00000000"
+ android:minWidth="30dip"
+ android:minHeight="30dip"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
/>
android:clickable="true"
android:focusableInTouchMode="true"
android:focusable="true"/>
+
+ <TextView android:id="@android:id/empty"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:text="@+string/noSearchResults"/>
</LinearLayout>
<string name="HU">Hungarian</string>
<string name="IS">Icelandic</string>
<string name="ID">Indonesian</string>
- <string name="GA">Irish</string>
+ <string name="GA">Gaelic (Irish, Scottish)</string>
<string name="IT">Italian</string>
<string name="LA">Latin</string>
<string name="LV">Latvian</string>
<string name="YI">Yiddish</string>
<string name="ZU">Zulu</string>
+
+ <string name="AZ">Azeri</string>
+ <string name="EU">Basque</string>
+ <string name="BR">Breton</string>
+ <string name="MR">Burmese</string>
+ <string name="FO">Faroese</string>
+ <string name="GL">Galician</string>
+ <string name="KA">Georgian</string>
+ <string name="HT">Haitian Creole</string>
+ <string name="LB">Luxembourgish</string>
+ <string name="MK">Macedonian</string>
+
</resources>
\ No newline at end of file
<string name="selectDictionary">Select dictionary…</string>
<string name="switchToLanguage">Switch to %s</string>
<string name="addToWordList">Add to word list: %s</string>
- <string name="searchForSelection">Find: %s</string>
+ <string name="searchForSelection">Search: "%s"</string>
<string name="failedAddingToWordList">Failure adding to word list: %s</string>
<string name="unzippingDictionary">Unzipping dictionary…</string>
<string name="failedToUnzipDictionary">Failed to unzip dictionary…</string>
<string name="invalidDictionary">Invalid dictionary: file=%1$s, error=%2$s</string>
+ <string name="noSearchResults">No search results.</string>
+ <string name="aboutDictionary">About dictionary…</string>
+
+ <!-- About dictionary. -->
+ <string name="dictionaryPath">File: %s</string>
+ <string name="dictionarySize">Size: %d bytes</string>
+ <string name="dictionaryCreationTime">Created: %tc</string>
+ <string name="indexName">Index: %s</string>
+ <string name="mainTokenCount">Num words: %d</string>
+
<!-- About. -->
<string name="titleWithVersion">QuickDic 3.1</string>
import java.io.PrintWriter;\r
import java.io.RandomAccessFile;\r
import java.text.SimpleDateFormat;\r
+import java.util.Arrays;\r
+import java.util.Collections;\r
import java.util.Date;\r
+import java.util.LinkedHashSet;\r
import java.util.List;\r
+import java.util.Set;\r
import java.util.concurrent.Executor;\r
import java.util.concurrent.Executors;\r
import java.util.concurrent.ThreadFactory;\r
import java.util.regex.Matcher;\r
import java.util.regex.Pattern;\r
\r
-import android.app.Activity;\r
import android.app.Dialog;\r
import android.app.ListActivity;\r
import android.content.Context;\r
import android.view.KeyEvent;\r
import android.view.Menu;\r
import android.view.MenuItem;\r
+import android.view.WindowManager;\r
import android.view.MenuItem.OnMenuItemClickListener;\r
import android.view.MotionEvent;\r
import android.view.View;\r
Dictionary dictionary = null;\r
int indexIndex = 0;\r
Index index = null;\r
+ List<RowBase> rowsToShow = null; // if not null, just show these rows.\r
\r
// package for test.\r
final Handler uiHandler = new Handler();\r
public void onItemSelected(AdapterView<?> adapterView, View arg1, final int position,\r
long id) {\r
if (!searchText.isFocused()) {\r
- // TODO: don't do this if multi words are entered.\r
- final RowBase row = (RowBase) getListAdapter().getItem(position);\r
- Log.d(LOG, "onItemSelected: " + row.index());\r
- final TokenRow tokenRow = row.getTokenRow(true);\r
- searchText.setText(tokenRow.getToken());\r
+ if (!isFiltered()) {\r
+ final RowBase row = (RowBase) getListAdapter().getItem(position);\r
+ Log.d(LOG, "onItemSelected: " + row.index());\r
+ final TokenRow tokenRow = row.getTokenRow(true);\r
+ searchText.setText(tokenRow.getToken());\r
+ }\r
}\r
}\r
\r
searchText.requestFocus();\r
Log.d(LOG, "Trying to show soft keyboard.");\r
final InputMethodManager manager = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE);\r
- manager.showSoftInput(searchText, InputMethodManager.SHOW_FORCED);\r
+ manager.showSoftInput(searchText, InputMethodManager.SHOW_IMPLICIT);\r
}\r
\r
void updateLangButton() {\r
}\r
\r
void onUpDownButton(final boolean up) {\r
+ if (isFiltered()) {\r
+ return;\r
+ }\r
final int firstVisibleRow = getListView().getFirstVisiblePosition();\r
final RowBase row = index.rows.get(firstVisibleRow);\r
final TokenRow tokenRow = row.getTokenRow(true);\r
Log.d(LOG, "onUpDownButton, destIndexEntry=" + dest.token);\r
searchText.removeTextChangedListener(searchTextWatcher);\r
searchText.setText(dest.token);\r
+ Selection.moveToRightEdge(searchText.getText(), searchText.getLayout());\r
jumpToRow(index.sortedIndexEntries.get(destIndexEntry).startRow);\r
searchText.addTextChangedListener(searchTextWatcher);\r
}\r
});\r
}\r
\r
+ {\r
+ final MenuItem aboutDictionary = menu.add(getString(R.string.aboutDictionary));\r
+ aboutDictionary.setOnMenuItemClickListener(new OnMenuItemClickListener() {\r
+ public boolean onMenuItemClick(final MenuItem menuItem) {\r
+ final Context context = getListView().getContext();\r
+ final Dialog dialog = new Dialog(context);\r
+ dialog.setContentView(R.layout.about_dictionary_dialog);\r
+ final TextView textView = (TextView) dialog.findViewById(R.id.text);\r
+\r
+ final String name = application.getDictionaryName(dictFile.getName());\r
+ dialog.setTitle(name);\r
+ \r
+ final StringBuilder builder = new StringBuilder();\r
+ final DictionaryInfo dictionaryInfo = Dictionary.getDictionaryInfo(dictFile);\r
+ if (dictionaryInfo != null) {\r
+ builder.append(dictionaryInfo.dictInfo).append("\n\n");\r
+ builder.append(getString(R.string.dictionaryPath, dictFile.getPath())).append("\n");\r
+ builder.append(getString(R.string.dictionarySize, dictionaryInfo.uncompressedBytes)).append("\n");\r
+ builder.append(getString(R.string.dictionaryCreationTime, dictionaryInfo.creationMillis)).append("\n");\r
+ for (final IndexInfo indexInfo : dictionaryInfo.indexInfos) {\r
+ builder.append("\n");\r
+ builder.append(getString(R.string.indexName, indexInfo.shortName)).append("\n");\r
+ builder.append(getString(R.string.mainTokenCount, indexInfo.mainTokenCount)).append("\n");\r
+ }\r
+ } else {\r
+ builder.append(getString(R.string.invalidDictionary));\r
+ }\r
+ textView.setText(builder.toString());\r
+ \r
+ dialog.show();\r
+ final WindowManager.LayoutParams layoutParams = new WindowManager.LayoutParams();\r
+ layoutParams.width = WindowManager.LayoutParams.FILL_PARENT;\r
+ layoutParams.height = WindowManager.LayoutParams.FILL_PARENT;\r
+ dialog.getWindow().setAttributes(layoutParams);\r
+ return false;\r
+ }\r
+ });\r
+ }\r
+\r
return true;\r
}\r
\r
Log.d(LOG, "searchFinished: " + searchOperation + ", searchResult=" + searchResult);\r
\r
currentSearchOperation = null;\r
-\r
uiHandler.postDelayed(new Runnable() {\r
@Override\r
public void run() {\r
if (currentSearchOperation == null) {\r
- jumpToRow(searchResult.startRow);\r
+ if (searchResult != null) {\r
+ if (isFiltered()) {\r
+ clearFiltered();\r
+ }\r
+ jumpToRow(searchResult.startRow);\r
+ } else if (searchOperation.multiWordSearchResult != null) {\r
+ // Multi-row search....\r
+ setFiltered(searchOperation);\r
+ } else {\r
+ throw new IllegalStateException("This should never happen.");\r
+ }\r
} else {\r
Log.d(LOG, "More coming, waiting for currentSearchOperation.");\r
}\r
}\r
- }, 50);\r
- \r
-// if (!searchResult.success) {\r
-// if (vibrator != null) {\r
-// vibrator.vibrate(VIBRATE_MILLIS);\r
-// }\r
-// searchText.setText(searchResult.longestPrefixString);\r
-// searchText.setSelection(searchResult.longestPrefixString.length());\r
-// return;\r
-// }\r
+ }, 20);\r
\r
}\r
\r
getListView().setSelected(true);\r
}\r
\r
+ static final Pattern WHITESPACE = Pattern.compile("\\s+");\r
final class SearchOperation implements Runnable {\r
\r
final AtomicBoolean interrupted = new AtomicBoolean(false);\r
final String searchText;\r
+ List<String> searchTokens; // filled in for multiWord.\r
final Index index;\r
\r
long searchStartMillis;\r
\r
Index.IndexEntry searchResult;\r
+ List<RowBase> multiWordSearchResult;\r
\r
boolean done = false;\r
\r
public void run() {\r
try {\r
searchStartMillis = System.currentTimeMillis();\r
- searchResult = index.findInsertionPoint(searchText, interrupted);\r
+ final String[] searchTokenArray = WHITESPACE.split(searchText);\r
+ if (searchTokenArray.length == 1) {\r
+ searchResult = index.findInsertionPoint(searchText, interrupted);\r
+ } else {\r
+ searchTokens = Arrays.asList(searchTokenArray);\r
+ multiWordSearchResult = index.multiWordSearch(searchTokens, interrupted);\r
+ }\r
Log.d(LOG, "searchText=" + searchText + ", searchDuration="\r
+ (System.currentTimeMillis() - searchStartMillis) + ", interrupted="\r
+ interrupted.get());\r
final class IndexAdapter extends BaseAdapter {\r
\r
final Index index;\r
+ final List<RowBase> rows;\r
+ final Set<String> toHighlight;\r
\r
IndexAdapter(final Index index) {\r
this.index = index;\r
+ rows = index.rows;\r
+ this.toHighlight = null;\r
+ }\r
+\r
+ IndexAdapter(final Index index, final List<RowBase> rows, final List<String> toHighlight) {\r
+ this.index = index;\r
+ this.rows = rows;\r
+ this.toHighlight = new LinkedHashSet<String>(toHighlight);\r
}\r
\r
@Override\r
public int getCount() {\r
- return index.rows.size();\r
+ return rows.size();\r
}\r
\r
@Override\r
public RowBase getItem(int position) {\r
- return index.rows.get(position);\r
+ return rows.get(position);\r
}\r
\r
@Override\r
\r
@Override\r
public View getView(int position, final View convertView, ViewGroup parent) {\r
- final RowBase row = index.rows.get(position);\r
+ final RowBase row = getItem(position);\r
if (row instanceof PairEntry.Row) {\r
return getView(position, (PairEntry.Row) row, parent, convertView);\r
} else if (row instanceof TokenRow) {\r
col2.setText(col2Text, TextView.BufferType.SPANNABLE);\r
\r
// Bold the token instances in col1.\r
+ final Set<String> toBold = toHighlight != null ? this.toHighlight : Collections.singleton(row.getTokenRow(true).getToken());\r
final Spannable col1Spannable = (Spannable) col1.getText();\r
- int startPos = 0;\r
- final String token = row.getTokenRow(true).getToken();\r
- while ((startPos = col1Text.indexOf(token, startPos)) != -1) {\r
- col1Spannable.setSpan(new StyleSpan(Typeface.BOLD), startPos,\r
- startPos + token.length(), Spannable.SPAN_INCLUSIVE_EXCLUSIVE);\r
- startPos += token.length();\r
+ for (final String token : toBold) {\r
+ int startPos = 0;\r
+ while ((startPos = col1Text.indexOf(token, startPos)) != -1) {\r
+ col1Spannable.setSpan(new StyleSpan(Typeface.BOLD), startPos,\r
+ startPos + token.length(), Spannable.SPAN_INCLUSIVE_EXCLUSIVE);\r
+ startPos += token.length();\r
+ }\r
}\r
\r
createTokenLinkSpans(col1, col1Spannable, col1Text);\r
}\r
}\r
\r
+ // --------------------------------------------------------------------------\r
+ // Filtered results.\r
+ // --------------------------------------------------------------------------\r
+\r
+ boolean isFiltered() {\r
+ return rowsToShow != null;\r
+ }\r
+\r
+ void setFiltered(final SearchOperation searchOperation) {\r
+ ((Button) findViewById(R.id.UpButton)).setEnabled(false);\r
+ ((Button) findViewById(R.id.DownButton)).setEnabled(false);\r
+ rowsToShow = searchOperation.multiWordSearchResult;\r
+ setListAdapter(new IndexAdapter(index, rowsToShow, searchOperation.searchTokens));\r
+ }\r
+\r
+ void clearFiltered() {\r
+ ((Button) findViewById(R.id.UpButton)).setEnabled(true);\r
+ ((Button) findViewById(R.id.DownButton)).setEnabled(true);\r
+ setListAdapter(new IndexAdapter(index));\r
+ rowsToShow = null;\r
+ }\r
+\r
}\r
final List<DictionaryInfo> toAddSorted = new ArrayList<DictionaryInfo>();
final File[] dictDirFiles = DICT_DIR.listFiles();
for (final File file : dictDirFiles) {
+ // TODO: delete zip files here.
if (!file.getName().endsWith(".quickdic")) {
continue;
}
row.setOrientation(LinearLayout.HORIZONTAL);
result.addView(row);
+ {
+ final TextView textView = new TextView(parent.getContext());
+ final String name = application.getDictionaryName(dictionaryInfo.uncompressedFilename);
+ textView.setText(name);
+ textView.setTextSize(TypedValue.COMPLEX_UNIT_SP, 22);
+ row.addView(textView);
+ LinearLayout.LayoutParams layoutParams = new LinearLayout.LayoutParams(WindowManager.LayoutParams.WRAP_CONTENT, WindowManager.LayoutParams.WRAP_CONTENT);
+ layoutParams.weight = 1.0f;
+ textView.setLayoutParams(layoutParams);
+ }
+
final boolean updateAvailable = application.updateAvailable(dictionaryInfo);
final DictionaryInfo downloadable = application.getDownloadable(dictionaryInfo.uncompressedFilename);
if ((!application.isDictionaryOnDevice(dictionaryInfo.uncompressedFilename) || updateAvailable) && downloadable != null) {
row.addView(downloadButton);
} else {
final ImageView checkMark = new ImageView(parent.getContext());
- checkMark.setImageResource(android.R.drawable.checkbox_on_background);
+ checkMark.setImageResource(R.drawable.btn_check_buttonless_on);
row.addView(checkMark);
}
- final TextView textView = new TextView(parent.getContext());
- final String name = application.getDictionaryName(dictionaryInfo.uncompressedFilename);
- textView.setText(name);
- textView.setTextSize(TypedValue.COMPLEX_UNIT_SP, 22);
- row.addView(textView);
-
// Add the information about each index.
final LinearLayout row2 = new LinearLayout(parent.getContext());
row2.setOrientation(LinearLayout.HORIZONTAL);
\r
try {\r
final File destFile = new File(dest);\r
- destFile.getParentFile().mkdirs();\r
+ if (destFile.getAbsoluteFile().getParent() != null) {\r
+ destFile.getAbsoluteFile().getParentFile().mkdirs();\r
+ }\r
\r
final File destTmpFile = File.createTempFile("dictionaryDownload", "tmp", destFile\r
.getParentFile());\r
static final int CACHE_SIZE = 5000;
- static final int CURRENT_DICT_VERSION = 3;
+ static final int CURRENT_DICT_VERSION = 4;
static final String END_OF_DICTIONARY = "END OF DICTIONARY";
// persisted
dictInfo = raf.readUTF();
// Load the sources, then seek past them, because reading them later disrupts the offset.
- final RAFList<EntrySource> rafSources = RAFList.create(raf, new EntrySource.Serializer(this), raf.getFilePointer());
- sources = new ArrayList<EntrySource>(rafSources);
- raf.seek(rafSources.getEndOffset());
-
- pairEntries = CachingList.create(RAFList.create(raf, new PairEntry.Serializer(this), raf.getFilePointer()), CACHE_SIZE);
- textEntries = CachingList.create(RAFList.create(raf, new TextEntry.Serializer(this), raf.getFilePointer()), CACHE_SIZE);
- indices = CachingList.createFullyCached(RAFList.create(raf, indexSerializer, raf.getFilePointer()));
+ try {
+ final RAFList<EntrySource> rafSources = RAFList.create(raf, new EntrySource.Serializer(this), raf.getFilePointer());
+ sources = new ArrayList<EntrySource>(rafSources);
+ raf.seek(rafSources.getEndOffset());
+
+ pairEntries = CachingList.create(RAFList.create(raf, new PairEntry.Serializer(this), raf.getFilePointer()), CACHE_SIZE);
+ textEntries = CachingList.create(RAFList.create(raf, new TextEntry.Serializer(this), raf.getFilePointer()), CACHE_SIZE);
+ indices = CachingList.createFullyCached(RAFList.create(raf, indexSerializer, raf.getFilePointer()));
+ } catch (RuntimeException e) {
+ final IOException ioe = new IOException("RuntimeException loading dictionary");
+ ioe.initCause(e);
+ throw ioe;
+ }
final String end = raf.readUTF();
if (!end.equals(END_OF_DICTIONARY)) {
throw new IOException("Dictionary seems corrupt: " + end);
import java.io.RandomAccessFile;
import java.util.ArrayList;
import java.util.Collection;
+import java.util.Collections;
import java.util.EnumMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.regex.Pattern;
import com.hughes.android.dictionary.DictionaryInfo;
import com.hughes.android.dictionary.DictionaryInfo.IndexInfo;
import com.hughes.util.raf.RAFList;
import com.hughes.util.raf.RAFSerializable;
import com.hughes.util.raf.RAFSerializer;
+import com.hughes.util.raf.SerializableSerializer;
import com.hughes.util.raf.UniformRAFList;
import com.ibm.icu.text.Collator;
import com.ibm.icu.text.Transliterator;
// persisted
public final List<IndexEntry> sortedIndexEntries;
+
+ // persisted.
+ public final Set<String> stoplist;
// One big list!
// Various sub-types.
// --------------------------------------------------------------------------
- public Index(final Dictionary dict, final String shortName, final String longName, final Language sortLanguage, final String normalizerRules, final boolean swapPairEntries) {
+ public Index(final Dictionary dict, final String shortName, final String longName, final Language sortLanguage, final String normalizerRules, final boolean swapPairEntries, final Set<String> stoplist) {
this.dict = dict;
this.shortName = shortName;
this.longName = longName;
this.normalizerRules = normalizerRules;
this.swapPairEntries = swapPairEntries;
sortedIndexEntries = new ArrayList<IndexEntry>();
+ this.stoplist = stoplist;
rows = new ArrayList<RowBase>();
normalizer = null;
mainTokenCount = raf.readInt();
}
sortedIndexEntries = CachingList.create(RAFList.create(raf, IndexEntry.SERIALIZER, raf.getFilePointer()), CACHE_SIZE);
+ if (dict.dictFileVersion >= 4) {
+ stoplist = new SerializableSerializer<Set<String>>().read(raf);
+ } else {
+ stoplist = Collections.emptySet();
+ }
rows = CachingList.create(UniformRAFList.create(raf, new RowBase.Serializer(this), raf.getFilePointer()), CACHE_SIZE);
}
raf.writeInt(mainTokenCount);
}
RAFList.write(raf, sortedIndexEntries, IndexEntry.SERIALIZER);
+ new SerializableSerializer<Set<String>>().write(raf, stoplist);
UniformRAFList.write(raf, (Collection<RowBase>) rows, new RowBase.Serializer(this), 5);
}
return new DictionaryInfo.IndexInfo(shortName, sortedIndexEntries.size(), mainTokenCount);
}
- final List<RowBase> multiWordSearch(final List<String> searchTokens, final AtomicBoolean interrupted) {
+ public final List<RowBase> multiWordSearch(final List<String> searchTokens, final AtomicBoolean interrupted) {
final List<RowBase> result = new ArrayList<RowBase>();
-
- // Heuristic: use the longest searchToken as the base.
- String searchToken = null;
+
+ final Set<String> normalizedNonStoplist = new LinkedHashSet<String>();
+
+ final StringBuilder regex = new StringBuilder();
for (int i = 0; i < searchTokens.size(); ++i) {
- if (interrupted.get()) { return null; }
+ final String searchToken = searchTokens.get(i);
final String normalized = normalizeToken(searchTokens.get(i));
// Normalize them all.
searchTokens.set(i, normalized);
- if (searchToken == null || normalized.length() > searchToken.length()) {
- searchToken = normalized;
+
+ if (!stoplist.contains(searchToken)) {
+ normalizedNonStoplist.add(normalized);
}
+
+ if (regex.length() > 0) {
+ regex.append("[\\s]*");
+ }
+ regex.append(Pattern.quote(normalized));
}
+ final Pattern pattern = Pattern.compile(regex.toString());
- final int insertionPointIndex = findInsertionPointIndex(searchToken, interrupted);
- if (insertionPointIndex == -1 || interrupted.get()) {
- return null;
- }
-
+
// The things that match.
- // TODO: use a key
- final Map<RowMatchType,Set<RowBase>> matches = new EnumMap<RowMatchType, Set<RowBase>>(RowMatchType.class);
+ final Map<RowMatchType,List<RowBase>> matches = new EnumMap<RowMatchType, List<RowBase>>(RowMatchType.class);
for (final RowMatchType rowMatchType : RowMatchType.values()) {
- matches.put(rowMatchType, new LinkedHashSet<RowBase>());
+ if (rowMatchType != RowMatchType.NO_MATCH) {
+ matches.put(rowMatchType, new ArrayList<RowBase>());
+ }
}
- for (int index = insertionPointIndex; index < sortedIndexEntries.size(); ++index) {
+ int bestRowCount = Integer.MAX_VALUE;
+ String bestToken = null;
+ for (final String searchToken : normalizedNonStoplist) {
+ final int insertionPointIndex = findInsertionPointIndex(searchToken, interrupted);
if (interrupted.get()) { return null; }
- final IndexEntry indexEntry = sortedIndexEntries.get(index);
- if (!indexEntry.normalizedToken.equals(searchToken)) {
- break;
+ if (insertionPointIndex == -1) {
+ // If we've typed "train statio", don't fail just because the index
+ // doesn't contain "statio".
+ continue;
}
-
- for (int rowIndex = indexEntry.startRow; rowIndex < indexEntry.startRow + indexEntry.numRows; ++rowIndex) {
+
+ int rowCount = 0;
+ for (int index = insertionPointIndex; index < sortedIndexEntries.size(); ++index) {
if (interrupted.get()) { return null; }
- final RowBase row = rows.get(rowIndex);
- final RowMatchType matchType = row.matches(searchTokens, normalizer, swapPairEntries);
- if (matchType != RowMatchType.NO_MATCH) {
- matches.get(matchType).add(row);
+ final IndexEntry indexEntry = sortedIndexEntries.get(index);
+ if (!indexEntry.normalizedToken.equals(searchToken)) {
+ break;
}
+ rowCount += indexEntry.numRows;
+ }
+
+ //System.out.println(searchToken + ", rowCount=" + rowCount);
+ if (rowCount < bestRowCount) {
+ bestRowCount = rowCount;
+ bestToken = searchToken;
}
}
- for (final Set<RowBase> rows : matches.values()) {
- result.addAll(rows);
+ final String searchToken = bestToken != null ? bestToken : searchTokens.get(0);
+
+// for (final String searchToken : searchTokens) {
+
+ final int insertionPointIndex = findInsertionPointIndex(searchToken, interrupted);
+ if (interrupted.get()) { return null; }
+
+
+// System.out.println("Searching token: " + searchToken);
+
+
+ for (int index = insertionPointIndex; index < sortedIndexEntries.size(); ++index) {
+ if (interrupted.get()) { return null; }
+ final IndexEntry indexEntry = sortedIndexEntries.get(index);
+ if (!indexEntry.normalizedToken.equals(searchToken)) {
+ break;
+ }
+
+// System.out.println("Searching indexEntry: " + indexEntry.token);
+
+ for (int rowIndex = indexEntry.startRow; rowIndex < indexEntry.startRow + indexEntry.numRows; ++rowIndex) {
+ if (interrupted.get()) { return null; }
+ final RowBase row = rows.get(rowIndex);
+ final RowMatchType matchType = row.matches(searchTokens, pattern, normalizer(), swapPairEntries);
+ if (matchType != RowMatchType.NO_MATCH) {
+ matches.get(matchType).add(row);
+ }
+ }
+ }
+// } // searchTokens
+
+ final RowBase.LengthComparator lengthComparator = new RowBase.LengthComparator(swapPairEntries);
+ for (final Collection<RowBase> rows : matches.values()) {
+ final List<RowBase> ordered = new ArrayList<RowBase>(rows);
+ Collections.sort(ordered, lengthComparator);
+ result.addAll(ordered);
}
return result;
}
-
+
private String normalizeToken(final String searchToken) {
if (TransliteratorManager.init(null)) {
final Transliterator normalizer = normalizer();
isoCodeToResourceId.put("CI", R.string.CI);\r
isoCodeToResourceId.put("YI", R.string.YI);\r
isoCodeToResourceId.put("ZU", R.string.ZU);\r
- \r
- \r
+\r
+ isoCodeToResourceId.put("AZ", R.string.AZ);\r
+ isoCodeToResourceId.put("EU", R.string.EU);\r
+ isoCodeToResourceId.put("BR", R.string.BR);\r
+ isoCodeToResourceId.put("MR", R.string.MR);\r
+ isoCodeToResourceId.put("FO", R.string.FO);\r
+ isoCodeToResourceId.put("GL", R.string.GL);\r
+ isoCodeToResourceId.put("HT", R.string.HT);\r
+ isoCodeToResourceId.put("LB", R.string.LB);\r
+ isoCodeToResourceId.put("MK", R.string.MK);\r
+\r
// Hack to allow lower-case ISO codes to work:\r
for (final String isoCode : new ArrayList<String>(isoCodeToResourceId.keySet())) {\r
isoCodeToResourceId.put(isoCode.toLowerCase(), isoCodeToResourceId.get(isoCode));\r
*/\r
private static final String rtlChars =\r
"\u0591-\u07FF\uFB1D-\uFDFD\uFE70-\uFEFC";\r
- private static final Pattern RTL_TOKEN = Pattern.compile("[" + rtlChars + "]");\r
+\r
+ private static final String puncChars =\r
+ "\\[\\]\\(\\)\\{\\}\\=";\r
+\r
+ private static final Pattern RTL_LEFT_BOUNDARY = Pattern.compile("(["+ puncChars +"])([" + rtlChars + "])");\r
+ private static final Pattern RTL_RIGHT_BOUNDARY = Pattern.compile("([" + rtlChars + "])(["+ puncChars +"])");\r
\r
- public static String fixBidiText(final String text) {\r
- // TODO: fix me!, use me!\r
+ public static String fixBidiText(String text) {\r
+// text = RTL_LEFT_BOUNDARY.matcher(text).replaceAll("$1\u200e $2");\r
+// text = RTL_RIGHT_BOUNDARY.matcher(text).replaceAll("$1 \u200e$2");\r
return text;\r
}\r
\r
}
@Override
- public RowMatchType matches(final List<String> searchTokens, final Transliterator normalizer, final boolean swapPairEntries) {
+ public RowMatchType matches(final List<String> searchTokens, final Pattern orderedMatchPattern, final Transliterator normalizer, final boolean swapPairEntries) {
final int side = swapPairEntries ? 1 : 0;
final List<Pair> pairs = getEntry().pairs;
final String[] pairSides = new String[pairs.size()];
return RowMatchType.NO_MATCH;
}
}
- final StringBuilder regex = new StringBuilder();
- for (final String searchToken : searchTokens) {
- if (regex.length() > 0) {
- regex.append("[\\s]*");
- }
- regex.append(Pattern.quote(searchToken));
- }
- final Pattern pattern = Pattern.compile(regex.toString());
for (final String pairSide : pairSides) {
- if (pattern.matcher(pairSide).matches()) {
+ if (orderedMatchPattern.matcher(pairSide).find()) {
return RowMatchType.ORDERED_MATCH;
}
}
return RowMatchType.BAG_OF_WORDS_MATCH;
}
+
+ @Override
+ public int getSideLength(boolean swapPairEntries) {
+ int result = 0;
+ final int side = swapPairEntries ? 1 : 0;
+ for (final Pair pair : getEntry().pairs) {
+ result += pair.get(side).length();
+ }
+ return result;
+ }
}
import java.io.IOException;
import java.io.PrintStream;
import java.io.RandomAccessFile;
+import java.util.Comparator;
import java.util.List;
+import java.util.regex.Pattern;
import com.hughes.util.IndexedObject;
import com.hughes.util.raf.RAFListSerializer;
this.index = index;
this.referenceIndex = referenceIndex;
}
+
+ static final class RowKey {
+ final Class<? extends RowBase> rowClass;
+ final int referenceIndex;
+
+ private RowKey(Class<? extends RowBase> rowClass, int referenceIndex) {
+ this.rowClass = rowClass;
+ this.referenceIndex = referenceIndex;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (!(o instanceof RowKey)) {
+ return false;
+ }
+ final RowKey that = (RowKey) o;
+ return this.referenceIndex == that.referenceIndex && this.rowClass.equals(that.rowClass);
+ }
+
+ @Override
+ public int hashCode() {
+ return rowClass.hashCode() + referenceIndex;
+ }
+ }
+
+ public RowKey getRowKey() {
+ return new RowKey(this.getClass(), referenceIndex);
+ }
/**
* @return the TokenRow that this row is "filed under".
public abstract String getRawText(final boolean compact);
- public abstract RowMatchType matches(final List<String> searchTokens, final Transliterator normalizer, boolean swapPairEntries);
+ public abstract RowMatchType matches(final List<String> searchTokens, final Pattern orderedMatch, final Transliterator normalizer, boolean swapPairEntries);
// RowBase must manage "disk-based" polymorphism. All other polymorphism is
// dealt with in the normal manner.
}
}
+ public static final class LengthComparator implements Comparator<RowBase> {
+
+ final boolean swapPairEntries;
+
+ public LengthComparator(boolean swapPairEntries) {
+ this.swapPairEntries = swapPairEntries;
+ }
+
+ @Override
+ public int compare(RowBase row1, RowBase row2) {
+ final int l1 = row1.getSideLength(swapPairEntries);
+ final int l2 = row2.getSideLength(swapPairEntries);
+ return l1 < l2 ? -1 : l1 == l2 ? 0 : 1;
+ }
+ }
+
+ public int getSideLength(boolean swapPairEntries) {
+ return getRawText(false).length();
+ }
+
}
import java.io.PrintStream;
import java.io.RandomAccessFile;
import java.util.List;
+import java.util.regex.Pattern;
import com.hughes.util.raf.RAFSerializable;
import com.hughes.util.raf.RAFSerializer;
}
@Override
- public RowMatchType matches(List<String> searchTokens, Transliterator normalizer, boolean swapPairEntries) {
+ public RowMatchType matches(final List<String> searchTokens, final Pattern orderedMatchPattern, Transliterator normalizer, boolean swapPairEntries) {
return null;
}
}
import java.io.PrintStream;
import java.io.RandomAccessFile;
import java.util.List;
+import java.util.regex.Pattern;
import com.ibm.icu.text.Transliterator;
}
@Override
- public RowMatchType matches(List<String> searchTokens, Transliterator normalizer, boolean swapPairEntries) {
+ public RowMatchType matches(List<String> searchTokens, final Pattern orderedMatchPattern, Transliterator normalizer, boolean swapPairEntries) {
return RowMatchType.NO_MATCH;
}
import java.util.LinkedHashMap;
import java.util.Map;
+import com.hughes.android.dictionary.DictionaryApplication;
+
+import android.app.Application;
import android.content.Context;
+import android.os.Environment;
import android.util.Log;
public class PersistentObjectCache {
}
private PersistentObjectCache(final Context context) {
- dir = context.getFilesDir();
+ final File filesDir = context.getFilesDir();
+ dir = filesDir != null ? filesDir : Environment.getExternalStorageDirectory();
if (dir == null) {
- throw new RuntimeException("context.getFilesDir() == null");
+ throw new RuntimeException("context.getFilesDir() == " + context.getFilesDir() + ", Environment.getExternalStorageDirectory()=" + Environment.getExternalStorageDirectory());
}
}