public String replace(String input, Map<String, String> pairs) {
// Reverse lexic-order of keys is good enough for most cases,
// as it puts longer words before their prefixes ("tool" before "too").
// However, there are corner cases, which this algorithm doesn't handle
// no matter what order of keys you choose, eg. it fails to match "edit"
// before "bed" in "..bedit.." because "bed" appears first in the input,
// but "edit" may be the desired longer match. Depends which you prefer.
final Map<String, String> sorted =
new TreeMap<String, String>(Collections.reverseOrder());
sorted.putAll(pairs);
final String[] keys = sorted.keySet().toArray(new String[sorted.size()]);
final String[] vals = sorted.values().toArray(new String[sorted.size()]);
final int lo = 0, hi = input.length();
final StringBuilder result = new StringBuilder();
int s = lo;
for (int i = s; i < hi; i++) {
for (int p = 0; p < keys.length; p++) {
if (input.regionMatches(i, keys[p], 0, keys[p].length())) {
/* TODO: check for "edit", if this is "bed" in "..bedit.." case,
* i.e. look ahead for all prioritized/longer keys starting within
* the current match region; iff found, then ignore match ("bed")
* and continue search (find "edit" later), else handle match. */
// if (better-match-overlaps-right-ahead)
// continue;
result.append(input, s, i).append(vals[p]);
i += keys[p].length();
s = i--;
}
}
}
if (s == lo) // no matches? no changes!
return input;
return result.append(input, s, hi).toString();
}