Skip to content

Instantly share code, notes, and snippets.

@ogregoire
Last active July 12, 2016 14:08
Show Gist options
  • Save ogregoire/8c95bd3ab7c7e670a5012caad47125e3 to your computer and use it in GitHub Desktop.
Save ogregoire/8c95bd3ab7c7e670a5012caad47125e3 to your computer and use it in GitHub Desktop.
Replacement class for multiple replacements occurring at once.
package replacer;
import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.base.Preconditions.checkNotNull;
import com.google.common.base.Function;
import com.google.common.base.Functions;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Iterables;
import java.util.Map;
import java.util.Set;
/**
* @author Olivier Grégoire
*/
public abstract class Replacer implements Function<String, String> {
public static MapBasedReplacer usingMapping() {
return new MapBasedReplacer(ImmutableMap.<String, String>builder());
}
public static MapBasedReplacer usingMapping(Map<String, String> replacements) {
checkNotNull(replacements);
for (String search : replacements.keySet()) {
checkArgument(!search.isEmpty());
}
return new MapBasedReplacer(ImmutableMap.<String, String>builder().putAll(replacements));
}
public static FunctionBasedReplacer usingFunction(Function<String, String> replacement) {
checkNotNull(replacement);
return new FunctionBasedReplacer(replacement, ImmutableSet.<String>builder());
}
public static FunctionBasedReplacer usingFunction(Function<String, String> replacement, Set<String> searches) {
checkNotNull(replacement);
checkNotNull(searches);
for (String search : searches) {
checkArgument(!search.isEmpty());
}
return new FunctionBasedReplacer(replacement, ImmutableSet.<String>builder().addAll(searches));
}
Replacer() {
}
@Override
public String apply(String str) {
return replaceIn(str);
}
public abstract String replaceIn(String text);
protected final String doReplaceIn(String text, Set<String> searches, Function<String, String> replacement) {
checkNotNull(text);
if (text.isEmpty()) {
return text;
}
String[] lookups = Iterables.toArray(searches, String.class);
int[] indicesOf = new int[lookups.length];
int textIndex = -1;
int lookupIndex = -1;
for (int i = 0; i < lookups.length; i++) {
int index = text.indexOf(lookups[i]);
indicesOf[i] = index;
if (textIndex == -1 || index < textIndex) {
textIndex = index;
lookupIndex = i;
}
}
if (textIndex == -1) {
return text;
}
int start = 0;
StringBuilder buffer = new StringBuilder(Math.max(text.length() * 2, 32));
while (textIndex != -1) {
buffer
.append(text, start, textIndex)
.append(replacement.apply(lookups[lookupIndex]));
start = textIndex + lookups[lookupIndex].length();
textIndex = -1;
for (int i = 0; i < lookups.length; i++) {
if (indicesOf[i] != -1) {
int index = text.indexOf(lookups[i], start);
indicesOf[i] = index;
if (textIndex == -1 || index < textIndex) {
textIndex = index;
lookupIndex = i;
}
}
}
}
return buffer
.append(text, start, text.length())
.toString();
}
public static class MapBasedReplacer extends Replacer {
private final ImmutableMap.Builder<String, String> builder;
MapBasedReplacer(ImmutableMap.Builder<String, String> builder) {
this.builder = builder;
}
public MapBasedReplacer add(String search, String replacement) {
checkNotNull(search);
checkArgument(!search.isEmpty());
this.builder.put(search, replacement);
return this;
}
@Override
public String replaceIn(String text) {
ImmutableMap<String, String> map = builder.build();
return doReplaceIn(text, map.keySet(), Functions.forMap(map));
}
}
public static class FunctionBasedReplacer extends Replacer {
private final Function<String, String> replacement;
private final ImmutableSet.Builder<String> searches;
FunctionBasedReplacer(Function<String, String> replacement, ImmutableSet.Builder<String> searches) {
this.replacement = replacement;
this.searches = searches;
}
public FunctionBasedReplacer add(String search) {
checkNotNull(search);
checkArgument(!search.isEmpty());
this.searches.add(search);
return this;
}
@Override
public String replaceIn(String text) {
return doReplaceIn(text, searches.build(), replacement);
}
}
}
package replacer;
import static org.hamcrest.CoreMatchers.equalTo;
import static org.junit.Assert.assertThat;
import org.junit.Test;
import com.google.common.base.Function;
/**
* @author Olivier Grégoire
*/
public class ReplacerTest {
@Test
public void testMap() {
String result = Replacer.usingMapping()
.add("ab", "w")
.add("d", "t")
.replaceIn("abcde");
assertThat(result, equalTo("wcte"));
result = Replacer.usingMapping()
.add("ab", "d")
.add("d", "t")
.replaceIn("abcde");
assertThat(result, equalTo("dcte"));
}
@Test
public void testFunction() {
Function<String, String> func = new Function<String, String>() {
@Override
public String apply(String s) {
switch (s) {
case "ab":
return "w";
case "d":
return "t";
default:
throw new IllegalArgumentException();
}
}
};
String result = Replacer.usingFunction(func)
.add("ab")
.add("d")
.replaceIn("abcde");
assertThat(result, equalTo("wcte"));
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment