字符串插值是非常有用的功能,可以提高编码效率。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")); } }
输出:
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)); } }
插值引擎接口:
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.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; } } } }
目前只实现了基本的功能,但是足以应付我现在的需要。如以后有更多的功能需求,再行扩展。