字符串插值是非常有用的功能,可以提高编码效率。Java语言没有提供原生的字符串插值功能,但在标准库中 MessageFormat 提供了部分功能。现在把积累的相关代码整理重构一下,以便以后使用。
例子:
import static java.lang.System.out;
import static cc.interpolation.Interpolations.indexed;
import static cc.interpolation.Interpolations.named;
import java.util.Collections;
public class Test {
public static void main(String[] args) {
out.println(indexed("{0} and {1}", "Li Lei", "Han Meimei"));
out.println(named("{a} and {b}", "a", "Li Lei", "b", "Han Meimei"));
out.println(named("{a} and {unknown}", "a", "A", "default"));
out.println(named("{a} and {unknown}",
Collections.singletonMap("a", "A"), "default"));
}
}
import static java.lang.System.out;
import static cc.interpolation.Interpolations.indexed;
import static cc.interpolation.Interpolations.named;
import java.util.Collections;
public class Test {
public static void main(String[] args) {
out.println(indexed("{0} and {1}", "Li Lei", "Han Meimei"));
out.println(named("{a} and {b}", "a", "Li Lei", "b", "Han Meimei"));
out.println(named("{a} and {unknown}", "a", "A", "default"));
out.println(named("{a} and {unknown}",
Collections.singletonMap("a", "A"), "default"));
}
}
输出:
Li Lei and Han Meimei
Li Lei and Han Meimei
A and default
A and default
具体实现如下:
import java.util.Map;
public class Interpolations {
private static final InterpolationEngine INDEXED_ENGINE = new IndexedInterpolationEngine();
private static final InterpolationEngine NAMED_ENGINE = new NamedInterpolationEngine();
public static String indexed(String template, Object... bindings) {
return INDEXED_ENGINE.combine(template,
IndexedInterpolationEngine.createBindings(bindings));
}
public static String named(String template, Object... bindings) {
return NAMED_ENGINE.combine(template,
NamedInterpolationEngine.createBindings(bindings));
}
public static String named(String template, Map<String, ?> bindings) {
return named(template, bindings, null);
}
public static String named(String template, Map<String, ?> bindings,
Object defaultValue) {
return NAMED_ENGINE.combine(template, NamedInterpolationEngine
.createBindings(bindings, defaultValue));
}
}
import java.util.Map;
public class Interpolations {
private static final InterpolationEngine INDEXED_ENGINE = new IndexedInterpolationEngine();
private static final InterpolationEngine NAMED_ENGINE = new NamedInterpolationEngine();
public static String indexed(String template, Object... bindings) {
return INDEXED_ENGINE.combine(template,
IndexedInterpolationEngine.createBindings(bindings));
}
public static String named(String template, Object... bindings) {
return NAMED_ENGINE.combine(template,
NamedInterpolationEngine.createBindings(bindings));
}
public static String named(String template, Map<String, ?> bindings) {
return named(template, bindings, null);
}
public static String named(String template, Map<String, ?> bindings,
Object defaultValue) {
return NAMED_ENGINE.combine(template, NamedInterpolationEngine
.createBindings(bindings, defaultValue));
}
}
插值引擎接口:
public interface InterpolationEngine {
public interface Bindings {
Object get(String name);
}
String combine(String template, Bindings bindings);
}
public interface InterpolationEngine {
public interface Bindings {
Object get(String name);
}
String combine(String template, Bindings bindings);
}
根据主要用到了两种插值方式:索引、命名。
这两种方式都可以使用正则表达式实现。基本的正则表达式插值代码为:
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class PatternInterpolationEngine implements InterpolationEngine {
private final Pattern pattern;
public PatternInterpolationEngine(Pattern pattern) {
this.pattern = pattern;
}
@Override
public String combine(String template, Bindings bindings) {
StringBuffer buffer = new StringBuffer(template.length());
Matcher matcher = pattern.matcher(template);
while (matcher.find()) {
String name = matcher.group(1);
Object value = bindings.get(name);
matcher.appendReplacement(buffer, String.valueOf(value));
}
matcher.appendTail(buffer);
return buffer.toString();
}
}
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class PatternInterpolationEngine implements InterpolationEngine {
private final Pattern pattern;
public PatternInterpolationEngine(Pattern pattern) {
this.pattern = pattern;
}
@Override
public String combine(String template, Bindings bindings) {
StringBuffer buffer = new StringBuffer(template.length());
Matcher matcher = pattern.matcher(template);
while (matcher.find()) {
String name = matcher.group(1);
Object value = bindings.get(name);
matcher.appendReplacement(buffer, String.valueOf(value));
}
matcher.appendTail(buffer);
return buffer.toString();
}
}
索引方式:
import java.util.regex.Pattern;
public class IndexedInterpolationEngine extends PatternInterpolationEngine {
private static final Pattern PATTERN = Pattern.compile("\\{([0-9]+)\\}");
public IndexedInterpolationEngine() {
super(PATTERN);
}
public static Bindings createBindings(Object... array) {
return new ArrayBindings(array);
}
private static final class ArrayBindings implements Bindings {
private final Object[] array;
public ArrayBindings(Object[] array) {
this.array = array;
}
@Override
public Object get(String name) {
return array[Integer.parseInt(name)];
}
}
}
import java.util.regex.Pattern;
public class IndexedInterpolationEngine extends PatternInterpolationEngine {
private static final Pattern PATTERN = Pattern.compile("\\{([0-9]+)\\}");
public IndexedInterpolationEngine() {
super(PATTERN);
}
public static Bindings createBindings(Object... array) {
return new ArrayBindings(array);
}
private static final class ArrayBindings implements Bindings {
private final Object[] array;
public ArrayBindings(Object[] array) {
this.array = array;
}
@Override
public Object get(String name) {
return array[Integer.parseInt(name)];
}
}
}
命名方式:
import java.util.Map;
import java.util.regex.Pattern;
public class NamedInterpolationEngine extends PatternInterpolationEngine {
private static final Pattern PATTERN = Pattern
.compile("\\{([_a-zA-Z0-9]+)\\}");
public NamedInterpolationEngine() {
super(PATTERN);
}
public static Bindings createBindings(Object... array) {
return new AssociativeArrayBindings(array);
}
public static Bindings createBindings(Map<String, ?> map,
Object defaultValue) {
return new MapBindings(map, defaultValue);
}
private static final class AssociativeArrayBindings implements Bindings {
private final Object[] associativeArray;
private final int lookupLength;
private final Object defaultValue;
public AssociativeArrayBindings(Object[] associativeArray) {
this.associativeArray = associativeArray;
if (associativeArray.length % 2 == 0) {
lookupLength = associativeArray.length;
defaultValue = null;
} else {
lookupLength = associativeArray.length - 1;
defaultValue = associativeArray[associativeArray.length - 1];
}
}
@Override
public Object get(String name) {
for (int i = 0; i < lookupLength; i += 2) {
if (name.equals(associativeArray[i])) {
return associativeArray[i + 1];
}
}
return defaultValue;
}
}
private static final class MapBindings implements Bindings {
private final Map<String, ?> map;
private final Object defaultValue;
public MapBindings(Map<String, ?> map, Object defaultValue) {
this.map = map;
this.defaultValue = defaultValue;
}
@Override
public Object get(String name) {
if (map.containsKey(name)) {
return map.get(name);
} else {
return defaultValue;
}
}
}
}
import java.util.Map;
import java.util.regex.Pattern;
public class NamedInterpolationEngine extends PatternInterpolationEngine {
private static final Pattern PATTERN = Pattern
.compile("\\{([_a-zA-Z0-9]+)\\}");
public NamedInterpolationEngine() {
super(PATTERN);
}
public static Bindings createBindings(Object... array) {
return new AssociativeArrayBindings(array);
}
public static Bindings createBindings(Map<String, ?> map,
Object defaultValue) {
return new MapBindings(map, defaultValue);
}
private static final class AssociativeArrayBindings implements Bindings {
private final Object[] associativeArray;
private final int lookupLength;
private final Object defaultValue;
public AssociativeArrayBindings(Object[] associativeArray) {
this.associativeArray = associativeArray;
if (associativeArray.length % 2 == 0) {
lookupLength = associativeArray.length;
defaultValue = null;
} else {
lookupLength = associativeArray.length - 1;
defaultValue = associativeArray[associativeArray.length - 1];
}
}
@Override
public Object get(String name) {
for (int i = 0; i < lookupLength; i += 2) {
if (name.equals(associativeArray[i])) {
return associativeArray[i + 1];
}
}
return defaultValue;
}
}
private static final class MapBindings implements Bindings {
private final Map<String, ?> map;
private final Object defaultValue;
public MapBindings(Map<String, ?> map, Object defaultValue) {
this.map = map;
this.defaultValue = defaultValue;
}
@Override
public Object get(String name) {
if (map.containsKey(name)) {
return map.get(name);
} else {
return defaultValue;
}
}
}
}
目前只实现了基本的功能,但是足以应付我现在的需要。如以后有更多的功能需求,再行扩展。