MessageFormatter
这是一个字符串格式化工具,主要完成两个功能
1.对于字符串“ hello {0}, {1}“在转换过程中会将{0}替换为所传参数数组下标为0的参数值,{1}则替换为下标为1的参数值。好处是支持任意的下标值,而jdk自带的MessageFormatter下标值只能是0-9
调用方式MessageFormatter.formatter(str, params);
2.将字符串中${xx},替换为一个外部的值,比如默认的MessageFormatter遇到${xx}后会试图通过 System.getProperty(xx)获取值,然后将${xx}替换为获取之后的值,外部值获取主要通过PropertySource来取,构造 器可以传入PropertySource的实现类。
看代码:
/**
*
* @author zhaoming
*
*/
public class MessageFormatter {
private PropertySource source;
private static final char DELIM_START = '{';
private static final char DELIM_START_LEN = 1;
private static final char DELIM_END = '}';
private static final char DELIM_END_LEN = 1;
private static final char ESCAPE_CHAR = '\\';
private static final char REF_CHAR = '$';
public MessageFormatter(PropertySource source) {
if (source == null) {
source = new SystemPropertySource();
} else {
this.source = source;
}
}
public MessageFormatter() {
source = new SystemPropertySource();
}
public String format(String pattern) {
return this.format(pattern, null);
}
public String format(String pattern, Object[] args) {
return this.format(pattern, args, 0);
}
private String format(String pattern, Object[] args, int argIdx) {
try {
if (pattern == null) {
return pattern;
}
if (args == null) {
args = new Object[0];
}
StringBuilder sb = new StringBuilder(pattern.length() + 20);
int start = 0;
int sidx = -1;
int eidx = -1;
String argStr = "";
while (start < pattern.length() && (sidx = pattern.indexOf(DELIM_START, start)) >= 0) {
if (hasEscapeChar(pattern, sidx)) {
if (hasDoubleEscapeChar(pattern, sidx)) {
sb.append(pattern, start, sidx - 1);
} else {
sb.append(pattern, start, sidx - 1).append(DELIM_START);
start = sidx + DELIM_START_LEN;
continue;
}
} else {
sb.append(pattern, start, sidx);
}
eidx = pattern.indexOf(DELIM_END, sidx);
if (eidx == -1) {
sb.append(pattern, sidx, pattern.length());
start = pattern.length();
} else {
argStr = pattern.substring(sidx + DELIM_START_LEN, eidx).trim();
if (argStr.isEmpty() || isNumeric(argStr)) {
appendIndexParam(sb, getArgInt(argStr, argIdx++), args, argStr);
} else {
appendRefParam(sb, argIdx, args, argStr);
}
start = eidx + DELIM_END_LEN;
}
}
if (start < pattern.length()) {
sb.append(pattern, start, pattern.length());
}
return sb.toString();
} catch (Exception e) {
e.printStackTrace();
}
return pattern;
}
private boolean isNumeric(String s) {
int len = s.length();
for (int i = 0; i < len; i++) {
if (!Character.isDigit(s.charAt(i))) {
return false;
}
}
return true;
}
private int getArgInt(String param, int v) {
try {
if ("".equals(param)) {
return v;
}
return Integer.parseInt(param);
} catch (NumberFormatException e) {
}
return -1;
}
private void appendRefParam(StringBuilder sb, int argIdx, Object[] args, String orgParam) {
int len = sb.length();
boolean isRef = true;
if (len == 0 || sb.charAt(len - 1) != REF_CHAR) {
isRef = false;
}
if (hasEscapeChar(sb, len - 1) && !hasDoubleEscapeChar(sb, len - 1)) {
isRef = false;
}
if (isRef) {
String v = source.getProperty(orgParam);
if (v != null) {
sb.setLength(len - 1);
sb.append(this.format(v, args, argIdx));
return;
}
}
sb.append(DELIM_START).append(orgParam).append(DELIM_END);
}
private void appendIndexParam(StringBuilder sb, int argIdx, Object[] args, String orgParam) {
if (argIdx < 0 || argIdx >= args.length) {
sb.append(DELIM_START).append(orgParam).append(DELIM_END);
} else {
deeplyAppendParam(sb, args[argIdx], new HashMap<Object[], Object>());
}
}
private void deeplyAppendParam (StringBuilder sb, Object o, Map<Object[], Object> seenMap) {
if (o == null) {
sb.append("null");
return;
}
if (!o.getClass().isArray()) {
appendToString(sb, o);
} else {
if (o.getClass() == Object[].class) {
objectArrayAppend(sb, (Object[]) o, seenMap);
} else {
int len = Array.getLength(o);
sb.append("[");
for (int i = 0; i < len; i++) {
appendToString(sb, Array.get(o, i));
if (i != len - 1) {
sb.append(", ");
}
}
sb.append(']');
}
}
}
private void objectArrayAppend(StringBuilder sb, Object[] a, Map<Object[], Object> seenMap) {
sb.append('[');
if (!seenMap.containsKey(a)) {
seenMap.put(a, null);
final int len = a.length;
for (int i = 0; i < len; i++) {
deeplyAppendParam(sb, a[i], seenMap);
if (i != len - 1)
sb.append(", ");
}
seenMap.remove(a);
} else {
sb.append("...");
}
sb.append(']');
}
private void appendToString(StringBuilder sb, Object o) {
try {
if(o == null) {
sb.append("null");
} else {
sb.append(o.toString());
}
} catch (Exception e) {
sb.append("[failed toString]");
}
}
private boolean hasEscapeChar(StringBuilder pattern, int idx) {
if (idx > 0 && pattern.charAt(idx - 1) == ESCAPE_CHAR) {
return true;
}
return false;
}
private boolean hasEscapeChar(String pattern, int idx) {
if (idx > 0 && pattern.charAt(idx - 1) == ESCAPE_CHAR) {
return true;
}
return false;
}
private boolean hasDoubleEscapeChar(StringBuilder pattern, int idx) {
if (idx >= 2 && pattern.charAt(idx - 2) == ESCAPE_CHAR) {
return true;
}
return false;
}
private boolean hasDoubleEscapeChar(String pattern, int idx) {
if (idx >= 2 && pattern.charAt(idx - 2) == ESCAPE_CHAR) {
return true;
}
return false;
}
class SystemPropertySource implements PropertySource {
@Override
public String getProperty(String key) {
try {
return System.getProperty(key);
} catch (Exception e) {
}
return null;
}
}
}