Last active
July 12, 2016 14:08
-
-
Save ogregoire/8c95bd3ab7c7e670a5012caad47125e3 to your computer and use it in GitHub Desktop.
Replacement class for multiple replacements occurring at once.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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); | |
} | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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