Skip to content

Commit

Permalink
Add setSuggestAllMatchingWords in MultiWordSuggestOracle
Browse files Browse the repository at this point in the history
Multiple formatted suggestions could be mapped to same normalized
candidate internally, by default, oracle will only return the last
formatted suggestion. With this new method, the caller can specify to
return all matching suggestions. For example, with words 'Mobile',
'MOBILE', 'mobile', 'MoBILE', typing 'm' will only suggest 'MoBILE' by
default. However, it will suggest all four words if
setSuggestAllMatchingWords(true) is called.

Change-Id: Ica9f213e256b943453c06eb72c675eda90d63d8d
Review-Link: https://gwt-review.googlesource.com/#/c/12480/
  • Loading branch information
m12345y committed Apr 13, 2015
1 parent 5f693b7 commit 0ff73ef
Show file tree
Hide file tree
Showing 2 changed files with 124 additions and 37 deletions.
118 changes: 81 additions & 37 deletions user/src/com/google/gwt/user/client/ui/MultiWordSuggestOracle.java
Expand Up @@ -25,9 +25,11 @@
import java.util.Comparator; import java.util.Comparator;
import java.util.HashMap; import java.util.HashMap;
import java.util.HashSet; import java.util.HashSet;
import java.util.Iterator;
import java.util.List; import java.util.List;
import java.util.Locale; import java.util.Locale;
import java.util.Set; import java.util.Set;
import java.util.TreeSet;


/** /**
* The default {@link com.google.gwt.user.client.ui.SuggestOracle}. The default * The default {@link com.google.gwt.user.client.ui.SuggestOracle}. The default
Expand Down Expand Up @@ -143,9 +145,19 @@ public int compareTo(WordBounds that) {
private HashMap<String, Set<String>> toCandidates = new HashMap<String, Set<String>>(); private HashMap<String, Set<String>> toCandidates = new HashMap<String, Set<String>>();


/** /**
* Associates candidates with their formatted suggestions. * Associates candidates with their formatted suggestions. Multiple formatted
* suggestions could be normalized to the same candidate, e.g. both 'Mobile'
* and 'MOBILE' are normalized to 'mobile'.
*/ */
private HashMap<String, String> toRealSuggestions = new HashMap<String, String>(); private HashMap<String, List<String>> toRealSuggestions =
new HashMap<>();

/**
* Specifies whether all formatted suggestions should be returned per
* normalized candidate. Refer ro {@link #setSuggestAllMatchingWords}
* for more details.
*/
private boolean suggestAllMatchingWords = false;


/** /**
* The whitespace masks used to prevent matching and replacing of the given * The whitespace masks used to prevent matching and replacing of the given
Expand All @@ -159,7 +171,7 @@ public int compareTo(WordBounds that) {
* Comparator used for sorting candidates from search. * Comparator used for sorting candidates from search.
*/ */
private Comparator<String> comparator = null; private Comparator<String> comparator = null;

/** /**
* Constructor for <code>MultiWordSuggestOracle</code>. This uses a space as * Constructor for <code>MultiWordSuggestOracle</code>. This uses a space as
* the whitespace character. * the whitespace character.
Expand Down Expand Up @@ -198,7 +210,12 @@ public MultiWordSuggestOracle(String whitespaceChars) {
public void add(String suggestion) { public void add(String suggestion) {
String candidate = normalizeSuggestion(suggestion); String candidate = normalizeSuggestion(suggestion);
// candidates --> real suggestions. // candidates --> real suggestions.
toRealSuggestions.put(candidate, suggestion); List<String> realSuggestions = toRealSuggestions.get(candidate);
if (realSuggestions == null) {
realSuggestions = new ArrayList<String>();
toRealSuggestions.put(candidate, realSuggestions);
}
realSuggestions.add(0, suggestion);


// word fragments --> candidates. // word fragments --> candidates.
String[] words = candidate.split(WHITESPACE_STRING); String[] words = candidate.split(WHITESPACE_STRING);
Expand Down Expand Up @@ -305,7 +322,22 @@ public final void setDefaultSuggestionsFromText(
} }
setDefaultSuggestions(accum); setDefaultSuggestions(accum);
} }


/**
* Sets the flag on whether to suggest all matching words. With words
* 'Mobile', 'MOBILE', 'mobile', 'MoBILE', typing 'm' will only build one
* suggestion for 'MoBILE' if {@code suggestAllMatchingWords} is
* {@code false}. However, it will build suggestions for all four words if
* {@code suggestAllMatchingWords} is {@code true}.
*
* @param suggestAllMatchingWords true to return all formatted suggestions
* per normalized candidate, false to return the last formatted
* suggestions per normalized candidate.
*/
public final void setSuggestAllMatchingWords(boolean suggestAllMatchingWords) {
this.suggestAllMatchingWords = suggestAllMatchingWords;
}

/** /**
* Creates the suggestion based on the given replacement and display strings. * Creates the suggestion based on the given replacement and display strings.
* *
Expand Down Expand Up @@ -334,43 +366,55 @@ private List<MultiWordSuggestion> convertToFormattedSuggestions(String query,


for (int i = 0; i < candidates.size(); i++) { for (int i = 0; i < candidates.size(); i++) {
String candidate = candidates.get(i); String candidate = candidates.get(i);
int cursor = 0;
int index = 0;
// Use real suggestion for assembly. // Use real suggestion for assembly.
String formattedSuggestion = toRealSuggestions.get(candidate); List<String> realSuggestions = toRealSuggestions.get(candidate);

TreeSet<String> realSuggestionsSet = new TreeSet<>();
// Create strong search string. if (suggestAllMatchingWords) {
SafeHtmlBuilder accum = new SafeHtmlBuilder(); realSuggestionsSet.addAll(realSuggestions);

} else {
String[] searchWords = query.split(WHITESPACE_STRING); // Build only one suggestion per normalized candidate if
while (true) { // suggestAllMatchingWords is false.
WordBounds wordBounds = findNextWord(candidate, searchWords, index); realSuggestionsSet.add(realSuggestions.get(0));
if (wordBounds == null) { }
break; Iterator<String> realSuggestionsIterator = realSuggestionsSet.iterator();
while (realSuggestionsIterator.hasNext()) {
int cursor = 0;
int index = 0;
String formattedSuggestion = realSuggestionsIterator.next();

// Create strong search string.
SafeHtmlBuilder accum = new SafeHtmlBuilder();

String[] searchWords = query.split(WHITESPACE_STRING);
while (true) {
WordBounds wordBounds = findNextWord(candidate, searchWords, index);
if (wordBounds == null) {
break;
}
if (wordBounds.startIndex == 0 ||
WHITESPACE_CHAR == candidate.charAt(wordBounds.startIndex - 1)) {
String part1 = formattedSuggestion.substring(cursor, wordBounds.startIndex);
String part2 = formattedSuggestion.substring(wordBounds.startIndex,
wordBounds.endIndex);
cursor = wordBounds.endIndex;
accum.appendEscaped(part1);
accum.appendHtmlConstant("<strong>");
accum.appendEscaped(part2);
accum.appendHtmlConstant("</strong>");
}
index = wordBounds.endIndex;
} }
if (wordBounds.startIndex == 0 ||
WHITESPACE_CHAR == candidate.charAt(wordBounds.startIndex - 1)) { // Check to make sure the search was found in the string.
String part1 = formattedSuggestion.substring(cursor, wordBounds.startIndex); if (cursor == 0) {
String part2 = formattedSuggestion.substring(wordBounds.startIndex, continue;
wordBounds.endIndex);
cursor = wordBounds.endIndex;
accum.appendEscaped(part1);
accum.appendHtmlConstant("<strong>");
accum.appendEscaped(part2);
accum.appendHtmlConstant("</strong>");
} }
index = wordBounds.endIndex;
}


// Check to make sure the search was found in the string. accum.appendEscaped(formattedSuggestion.substring(cursor));
if (cursor == 0) { MultiWordSuggestion suggestion = createSuggestion(formattedSuggestion,
continue; accum.toSafeHtml().asString());
suggestions.add(suggestion);
} }

accum.appendEscaped(formattedSuggestion.substring(cursor));
MultiWordSuggestion suggestion = createSuggestion(formattedSuggestion,
accum.toSafeHtml().asString());
suggestions.add(suggestion);
} }
return suggestions; return suggestions;
} }
Expand Down
43 changes: 43 additions & 0 deletions user/test/com/google/gwt/user/client/ui/SuggestBoxTest.java
Expand Up @@ -209,6 +209,49 @@ public void testMatchCustomSort() {
display.getSuggestion(2).getDisplayString()); display.getSuggestion(2).getDisplayString());
} }


public void testMultipleSuggestionsWithSameNormalizedCandidateDefault() {
MultiWordSuggestOracle oracle = new MultiWordSuggestOracle();
oracle.add("Mobile");
oracle.add("MOBILE");
oracle.add("MoBiLe");
oracle.add("mobile");

TestSuggestionDisplay display = new TestSuggestionDisplay();
SuggestBox box = new SuggestBox(oracle, new TextBox(), display);
RootPanel.get().add(box);
box.setText("m");
box.showSuggestionList();
assertTrue(display.isSuggestionListShowing());
assertEquals(1, display.getSuggestionCount());
assertEquals("<strong>m</strong>obile",
display.getSuggestion(0).getDisplayString());
}

public void testMultipleSuggestionsWithSameNormalizedCandidateSuggestAllMatchingWords() {
MultiWordSuggestOracle oracle = new MultiWordSuggestOracle();
oracle.add("Mobile");
oracle.add("MOBILE");
oracle.add("MoBiLe");
oracle.add("mobile");
oracle.setSuggestAllMatchingWords(true);

TestSuggestionDisplay display = new TestSuggestionDisplay();
SuggestBox box = new SuggestBox(oracle, new TextBox(), display);
RootPanel.get().add(box);
box.setText("m");
box.showSuggestionList();
assertTrue(display.isSuggestionListShowing());
assertEquals(4, display.getSuggestionCount());
assertEquals("<strong>M</strong>OBILE",
display.getSuggestion(0).getDisplayString());
assertEquals("<strong>M</strong>oBiLe",
display.getSuggestion(1).getDisplayString());
assertEquals("<strong>M</strong>obile",
display.getSuggestion(2).getDisplayString());
assertEquals("<strong>m</strong>obile",
display.getSuggestion(3).getDisplayString());
}

@SuppressWarnings("deprecation") @SuppressWarnings("deprecation")
public void testShowAndHide() { public void testShowAndHide() {
SuggestBox box = createSuggestBox(); SuggestBox box = createSuggestBox();
Expand Down

0 comments on commit 0ff73ef

Please sign in to comment.