+
+ 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;
+ for (int i = 0; i < searchTokens.size(); ++i) {
+ if (interrupted.get()) { return null; }
+ final String normalized = normalizeToken(searchTokens.get(i));
+ // Normalize them all.
+ searchTokens.set(i, normalized);
+ if (searchToken == null || normalized.length() > searchToken.length()) {
+ searchToken = normalized;
+ }
+ }
+
+ 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);
+ for (final RowMatchType rowMatchType : RowMatchType.values()) {
+ matches.put(rowMatchType, new LinkedHashSet<RowBase>());
+ }
+
+ 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;
+ }
+
+ 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, normalizer, swapPairEntries);
+ if (matchType != RowMatchType.NO_MATCH) {
+ matches.get(matchType).add(row);
+ }
+ }
+ }
+
+ for (final Set<RowBase> rows : matches.values()) {
+ result.addAll(rows);
+ }
+
+ return result;
+ }
+
+ private String normalizeToken(final String searchToken) {
+ if (TransliteratorManager.init(null)) {
+ final Transliterator normalizer = normalizer();
+ return normalizer.transliterate(searchToken);
+ } else {
+ // Do our best since the Transliterators aren't up yet.
+ return searchToken.toLowerCase();
+ }
+ }