Where a comparator is obviously comparing one or more derived keys, either manually or with ComparisonChain, replace with the equivalent comparing() call. Not all comparators are simple to translate in this way, such as ChunkManager#getDiffChunkComparator. Even though these cases could be improved, or, quite likely, they contain bugs, that work is left for a future change. In the common case of creating a comparator to pass to Collections#sort to sort a newly-created ArrayList, replace these with equivalent stream expressions. Again, not all cases of Collections#sort are simple enough to tackle here. In some of these cases, we could probably further alter the types to more aggressively use immutable types. Generally speaking, that would introduce ripple effects which would make this change less focused and difficult to review. Don't do that, in order to limit the cleanup here. Change-Id: I098d3820927367ee98c96698481cb0edcceb3d64
152 lines
4.9 KiB
Java
152 lines
4.9 KiB
Java
// 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
|
|
* <strong>} 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 <email@example.org>"). Those escapes will
|
|
// get <strong>-ed as well (e.g.: "<" -> "&<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();
|
|
}
|
|
}
|
|
}
|