字符串插值是非常有用的功能,可以提高编码效率。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;
			}
		}

	}
}

 目前只实现了基本的功能,但是足以应付我现在的需要。如以后有更多的功能需求,再行扩展。