summaryrefslogtreecommitdiffstats
path: root/java/com/google/gwtexpui/safehtml/client/HighlightSuggestOracle.java
diff options
context:
space:
mode:
Diffstat (limited to 'java/com/google/gwtexpui/safehtml/client/HighlightSuggestOracle.java')
-rw-r--r--java/com/google/gwtexpui/safehtml/client/HighlightSuggestOracle.java151
1 files changed, 151 insertions, 0 deletions
diff --git a/java/com/google/gwtexpui/safehtml/client/HighlightSuggestOracle.java b/java/com/google/gwtexpui/safehtml/client/HighlightSuggestOracle.java
new file mode 100644
index 0000000000..758521fc0b
--- /dev/null
+++ b/java/com/google/gwtexpui/safehtml/client/HighlightSuggestOracle.java
@@ -0,0 +1,151 @@
+// Copyright (C) 2009 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package com.google.gwtexpui.safehtml.client;
+
+import static java.util.Comparator.comparing;
+import static java.util.stream.Collectors.toList;
+
+import com.google.gwt.user.client.ui.SuggestOracle;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+/**
+ * A suggestion oracle that tries to highlight the matched text.
+ *
+ * <p>Suggestions supplied by the implementation of {@link #onRequestSuggestions(Request, Callback)}
+ * are modified to wrap all occurrences of the {@link
+ * com.google.gwt.user.client.ui.SuggestOracle.Request#getQuery()} substring in HTML {@code
+ * &lt;strong&gt;} tags, so they can be emphasized to the user.
+ */
+public abstract class HighlightSuggestOracle extends SuggestOracle {
+ private static String escape(String ds) {
+ return new SafeHtmlBuilder().append(ds).asString();
+ }
+
+ @Override
+ public final boolean isDisplayStringHTML() {
+ return true;
+ }
+
+ @Override
+ public final void requestSuggestions(Request request, Callback cb) {
+ onRequestSuggestions(
+ request,
+ new Callback() {
+ @Override
+ public void onSuggestionsReady(Request request, Response response) {
+ final String qpat = getQueryPattern(request.getQuery());
+ final boolean html = isHTML();
+ final ArrayList<Suggestion> r = new ArrayList<>();
+ for (Suggestion s : response.getSuggestions()) {
+ r.add(new BoldSuggestion(qpat, s, html));
+ }
+ cb.onSuggestionsReady(request, new Response(r));
+ }
+ });
+ }
+
+ protected String getQueryPattern(String query) {
+ return query;
+ }
+
+ /**
+ * @return true if {@link
+ * com.google.gwt.user.client.ui.SuggestOracle.Suggestion#getDisplayString()} returns HTML;
+ * false if the text must be escaped before evaluating in an HTML like context.
+ */
+ protected boolean isHTML() {
+ return false;
+ }
+
+ /** Compute the suggestions and return them for display. */
+ protected abstract void onRequestSuggestions(Request request, Callback done);
+
+ private static class BoldSuggestion implements Suggestion {
+ private final Suggestion suggestion;
+ private final String displayString;
+
+ BoldSuggestion(String qstr, Suggestion s, boolean html) {
+ suggestion = s;
+
+ String ds = s.getDisplayString();
+ if (!html) {
+ ds = escape(ds);
+ }
+
+ if (qstr != null && !qstr.isEmpty()) {
+ StringBuilder pattern = new StringBuilder();
+ for (String qterm : splitQuery(qstr)) {
+ qterm = escape(qterm);
+ // We now surround qstr by <strong>. But the chosen approach is not too
+ // smooth, if qstr is small (e.g.: "t") and this small qstr may occur in
+ // escapes (e.g.: "Tim &lt;email@example.org&gt;"). Those escapes will
+ // get <strong>-ed as well (e.g.: "&lt;" -> "&<strong>l</strong>t;"). But
+ // as repairing those mangled escapes is easier than not mangling them in
+ // the first place, we repair them afterwards.
+ if (pattern.length() > 0) {
+ pattern.append("|");
+ }
+ pattern.append(qterm);
+ }
+
+ ds = sgi(ds, "(" + pattern.toString() + ")", "<strong>$1</strong>");
+
+ // Repairing <strong>-ed escapes.
+ ds = sgi(ds, "(&[a-z]*)<strong>([a-z]*)</strong>([a-z]*;)", "$1$2$3");
+ }
+
+ displayString = ds;
+ }
+
+ /**
+ * Split the query by whitespace and filter out query terms which are substrings of other query
+ * terms.
+ */
+ private static List<String> splitQuery(String query) {
+ List<String> queryTerms =
+ Arrays.stream(query.split("\\s+")).sorted(comparing(String::length)).collect(toList());
+
+ List<String> result = new ArrayList<>();
+ for (String s : queryTerms) {
+ boolean add = true;
+ for (String queryTerm : result) {
+ if (queryTerm.toLowerCase().contains(s.toLowerCase())) {
+ add = false;
+ break;
+ }
+ }
+ if (add) {
+ result.add(s);
+ }
+ }
+ return result;
+ }
+
+ private static native String sgi(String inString, String pat, String newHtml)
+ /*-{ return inString.replace(RegExp(pat, 'gi'), newHtml); }-*/ ;
+
+ @Override
+ public String getDisplayString() {
+ return displayString;
+ }
+
+ @Override
+ public String getReplacementString() {
+ return suggestion.getReplacementString();
+ }
+ }
+}