在软件项目开发过程中,经常会碰到这样的一种情况:假定有一个公式A=B+C+1,在保存公式信息时,我们常常会用占位符(如:${})来配合公式项,即把该公式转换成A=${B}+${C}+1再保存起来。在进行公式计算时,通过占位符解析程序解析出B和C,再通过其它途径得到B和C的值,最后进行简单的数值计算得到结果。
在上面的情况里,占位符解析程序是进行公式计算的关键,接下来就来看一个类似的例子。
为了简单起见,例子只模拟如何用系统属性值去替换字符串里对应的系统属性键的占位符信息。如:系统属性:app.root,属性值:D:\\project,输入字符串为${app.root}/logs/app.log,则输出结果为:D:\project/logs/app.log。
下面显示该程序的几个场景。
场景一: |
|
程序输入 |
系统属性:app.root,属性值:D:\\project ${app.root}/logs/app.log |
程序输出 |
D:\project/logs/app.log |
场景二: |
|
程序输入 |
${app.root:D:\project}/logs/app.log |
程序输出 |
D:\project/logs/app.log |
场景三: |
|
程序输入 |
系统属性:app.name,属性值:muse 系统属性:muse.root,属性值:D:\\project ${${app.name}.root}/logs/app.log |
程序输出 |
D:\project/logs/app.log |
场景四: |
|
程序输入 |
系统属性:root.key,属性值:${muse.root} 系统属性:muse.root,属性值:D:\\project ${root.key}/logs/app.log |
程序输出 |
D:\project/logs/app.log |
该程序的接口为SystemPropertyUtils,该类负责接受用户请求。具体负责占位符处理的类由PropertyPlaceholderHelper类实现。StringUtils类为辅助的字符串处理类。
1 SystemPropertyUtils类
这是该程序的用户接口类,用于接受用户请求。该类有占位符的前缀,后缀及占位符的值分隔符三个属性,用于构造PropertyPlaceholderHelper类时使用。
public abstract class SystemPropertyUtils {
/** 系统属性占位符的前缀: "${" */
public static final String PLACEHOLDER_PREFIX = "${";
/** 系统属性占位符的后缀: "}" */
public static final String PLACEHOLDER_SUFFIX = "}";
/** 系统属性占位符的值分隔符: ":" */
public static final String VALUE_SEPARATOR = ":";
private static final PropertyPlaceholderHelper strictHelper =
new PropertyPlaceholderHelper(PLACEHOLDER_PREFIX, PLACEHOLDER_SUFFIX, VALUE_SEPARATOR, false);
private static final PropertyPlaceholderHelper nonStrictHelper =
new PropertyPlaceholderHelper(PLACEHOLDER_PREFIX, PLACEHOLDER_SUFFIX, VALUE_SEPARATOR, true);
/**
* 解析text中的${...}占位符,用相应的系统属性系统属性值。
*/
public static String resolvePlaceholders(String text) {
return resolvePlaceholders(text, false);
}
/**
* 解析text中的${...}占位符,用相应的系统属性系统属性值。如果标志位为true,则没有默认值的无法解析的占位符将保留原样不被解析。
* @param ignoreUnresolvablePlaceholders flag to determine is unresolved placeholders are ignored
*/
public static String resolvePlaceholders(String text, boolean ignoreUnresolvablePlaceholders) {
PropertyPlaceholderHelper helper = (ignoreUnresolvablePlaceholders ? nonStrictHelper : strictHelper);
return helper.replacePlaceholders(text, new SystemPropertyPlaceholderResolver(text));
}
// 这是一个私有内置类,用于从系统属性里获取占位符所对应的值。
private static class SystemPropertyPlaceholderResolver implements PropertyPlaceholderHelper.PlaceholderResolver {
private final String text;
public SystemPropertyPlaceholderResolver(String text) {
this.text = text;
}
public String resolvePlaceholder(String placeholderName) {
try {
String propVal = System.getProperty(placeholderName);
if (propVal == null) {
// 没找到系统属性时,就去查找系统环境变量。
propVal = System.getenv(placeholderName);
}
return propVal;
}
catch (Throwable ex) {
System.err.println("Could not resolve placeholder '" + placeholderName + "' in [" +
this.text + "] as system property: " + ex);
return null;
}
}
}
}
2 PropertyPlaceholderHelper类
这个类负责进行占位符处理,所有的关键处理逻辑都在这个类里。该类有占位符的前缀,后缀及占位符的值分隔符等属性,用于指明占位符的信息。
public class PropertyPlaceholderHelper {
private static final Map<String, String> wellKnownSimplePrefixes = new HashMap<String, String>(4);
static {
wellKnownSimplePrefixes.put("}", "{");
wellKnownSimplePrefixes.put("]", "[");
wellKnownSimplePrefixes.put(")", "(");
}
private final String placeholderPrefix;
private final String placeholderSuffix;
private final String simplePrefix;
private final String valueSeparator;
private final boolean ignoreUnresolvablePlaceholders;
/**
* 构造一个使用指定的前缀和后缀PropertyPlaceholderHelper类,对于无法解析的占位符,该类不给予处理。
*/
public PropertyPlaceholderHelper(String placeholderPrefix, String placeholderSuffix) {
this(placeholderPrefix, placeholderSuffix, null, true);
}
/**
* 构造一个使用指定的前缀和后缀PropertyPlaceholderHelper类。
*/
public PropertyPlaceholderHelper(String placeholderPrefix, String placeholderSuffix,
String valueSeparator, boolean ignoreUnresolvablePlaceholders) {
this.placeholderPrefix = placeholderPrefix;
this.placeholderSuffix = placeholderSuffix;
String simplePrefixForSuffix = wellKnownSimplePrefixes.get(this.placeholderSuffix);
if (simplePrefixForSuffix != null && this.placeholderPrefix.endsWith(simplePrefixForSuffix)) {
this.simplePrefix = simplePrefixForSuffix;
}
else {
this.simplePrefix = this.placeholderPrefix;
}
this.valueSeparator = valueSeparator;
this.ignoreUnresolvablePlaceholders = ignoreUnresolvablePlaceholders;
}
/**
* 用placeholderResolver返回的值来代替所有的类似${name}的点位符。
*/
public String replacePlaceholders(String value, PlaceholderResolver placeholderResolver) {
return parseStringValue(value, placeholderResolver, new HashSet<String>());
}
// 这个类是该程序的核心类。
protected String parseStringValue(
String strVal, PlaceholderResolver placeholderResolver, Set<String> visitedPlaceholders) {
StringBuilder buf = new StringBuilder(strVal);
int startIndex = strVal.indexOf(this.placeholderPrefix);
while (startIndex != -1) {
int endIndex = findPlaceholderEndIndex(buf, startIndex);
if (endIndex != -1) {
String placeholder = buf.substring(startIndex + this.placeholderPrefix.length(), endIndex);
if (!visitedPlaceholders.add(placeholder)) {
throw new IllegalArgumentException(
"Circular placeholder reference '" + placeholder + "' in property definitions");
}
// 递归调用,解析包含在占位符里的内嵌占位符。
placeholder = parseStringValue(placeholder, placeholderResolver, visitedPlaceholders);
// 获取占位符的代替值。
String propVal = placeholderResolver.resolvePlaceholder(placeholder);
// 如果没有指标占位符的代替值,则处理存在分隔符的情况。
if (propVal == null && this.valueSeparator != null) {
int separatorIndex = placeholder.indexOf(this.valueSeparator);
if (separatorIndex != -1) {
String actualPlaceholder = placeholder.substring(0, separatorIndex);
String defaultValue = placeholder.substring(separatorIndex + this.valueSeparator.length());
propVal = placeholderResolver.resolvePlaceholder(actualPlaceholder);
if (propVal == null) {
propVal = defaultValue;
}
}
}
if (propVal != null) {
// 递归调用,处理存在于占位符代替值里的占位符。
propVal = parseStringValue(propVal, placeholderResolver, visitedPlaceholders);
buf.replace(startIndex, endIndex + this.placeholderSuffix.length(), propVal);
startIndex = buf.indexOf(this.placeholderPrefix, startIndex + propVal.length());
}
else if (this.ignoreUnresolvablePlaceholders) {
startIndex = buf.indexOf(this.placeholderPrefix, endIndex + this.placeholderSuffix.length());
}
else {
throw new IllegalArgumentException("Could not resolve placeholder '" + placeholder + "'");
}
visitedPlaceholders.remove(placeholder);
}
else {
startIndex = -1;
}
}
return buf.toString();
}
// 查找占位符的结束索引。如占位符为${},则查找}的索引。如果存在内嵌占位符,则跳过内嵌占位符,直接找到与前缀匹配的后缀。
private int findPlaceholderEndIndex(CharSequence buf, int startIndex) {
int index = startIndex + this.placeholderPrefix.length();
int withinNestedPlaceholder = 0;
while (index < buf.length()) {
if (StringUtils.substringMatch(buf, index, this.placeholderSuffix)) {
if (withinNestedPlaceholder > 0) {
withinNestedPlaceholder--;
index = index + this.placeholderSuffix.length();
}
else {
return index;
}
}
else if (StringUtils.substringMatch(buf, index, this.simplePrefix)) {
withinNestedPlaceholder++;
index = index + this.simplePrefix.length();
}
else {
index++;
}
}
return -1;
}
/**
* 用于获取占位符代替值的接口。该接口由SystemPropertyUtils类的内置类去实现。
*/
public static interface PlaceholderResolver {
String resolvePlaceholder(String placeholderName);
}
}
3 StringUtils类
这是一个简单的字符串辅助处理类。处理一些常见的字符串相关的事情。
public class StringUtils {
/**
* 测试给定的字符串是否包含给定的子字符串。
*/
public static boolean substringMatch(CharSequence str, int index, CharSequence substring) {
for (int j = 0; j < substring.length(); j++) {
int i = index + j;
if (i >= str.length() || str.charAt(i) != substring.charAt(j)) {
return false;
}
}
return true;
}
}
相关推荐
在用freemaker模板的时候,第一步都会将word转换为xml格式文件,解析成xml文件经常会出现(个别、很多)字段占位符、变量值被分离,被分离的字段少的还好能手动改改,字段多了能让你直接发疯,此代码脚本轻松解决...
主要介绍了基于SPRINGBOOT配置文件占位符过程解析,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
主要给大家介绍了关于Spring Boot环境属性占位符解析及类型转换的相关资料,文中通过示例代码介绍的非常详细,对大家的学习或工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
解析是由一个简单的状态机完成的,使用正则表达式进行占位符检测。 用法 使用“has_placeholders”定义类中的可用方法 class User include KingPlaceholder has_many :comments has_one :company has_...
Ethernet 帧结构解析程序,用C++的
c++写的一个CIM模型解析程序,这个程序主要解析电力系统中的标准CIM模型。任意打开一个CIM,都可以解析,只做了初步的解析。速度上还是比较快的。
本篇文章主要介绍了.properties文件读取及占位符${...}替换源码解析的相关知识,具有很好的参考价值。下面跟着小编一起来看下吧
ps流解析程序,可将标准ps流中的es流抽取出来
主要介绍了Spring及Mybatis整合占位符解析失败问题解决,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
完整的以太网帧解析程序,使用C++完成,可直接运行,内含程序源码与说明文档。
第2章 Ethernet帧结构解析程序源代码
Ethernet帧结构解析程序 里面有源代码
实用,简单,有参考价值的Gps解析程序。
威克斯全能网盘解析程序是一个由威克斯技术圈制作的一款网盘地址解析工具。有些网盘文件下载起来比较麻烦,就如百度的,一下太大的文件就需要百度云来下载必能直接调用迅雷下载,这怎么办了?全能网盘解析程序就能...
c++编写的表达式解析程序,四则运算的解析。
可以用,c++做的,帧的封装和解析程序,代码有注释。
对以太网2.0版本进行帧解析的C++程序
这是一个用c++写的csv文件解析程序,该程序可以读取三种分隔符格式的csv文件。程序简单,执行快速。