在软件项目开发过程中,经常会碰到这样的一种情况:假定有一个公式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;
}
}
相关推荐
总的来说,这个“占位符替换工具”是一个实用的开发辅助工具,旨在提升开发者的生产力,减少重复性工作,同时提供源代码使得用户可以根据项目需求进行个性化定制。对于熟悉C#编程的开发者来说,这是一个很好的学习和...
总之,JSON占位符是开发过程中的一种实用工具,它可以提供模拟的JSON数据,方便HTML和JavaScript进行数据绑定和展示。同时,了解如何在HTML中处理和展示JSON数据对于现代Web开发至关重要。通过使用AJAX和相关...
在使用Go-enmasse时,开发者首先需要准备一个邮件模板,这个模板可以包含静态文本和一些占位符,如{{.Recipient}}来代表收件人的邮箱地址。然后,他们需要创建一个JSON数据文件,其中包含与模板中占位符相对应的动态...
1. **创建占位符按钮**:首先通过`Toolbar1.Buttons.Add()`方法创建了一个分隔符按钮和一个占位符按钮,其中占位符按钮的Key值被设置为"ComboBox",表示它将用来存放`ComboBox`控件。 2. **设置ComboBox位置**:...
在实际报名过程中,可能存在部分用户未按要求上传图片的情况,此时,"noImage.jpg" 可以作为一个占位符,表示这部分用户缺失的图片信息。 "使用说明.txt" 文件提供了关于如何使用这个工具的详细步骤和注意事项。...
描述中提到的"通过排版好的word文档以及预留的参数,可以生成自己想要的任意word文档,简化很多操作",意味着这个项目提供了一种方法,用户可以预先设计好Word模板,包括各种格式设置、样式和占位符,然后通过提供...
1. **准备模板文件**:先准备好一个HTML模板文件,这个文件中包含了一些占位符,这些占位符会在生成静态页面时被实际的数据替换。 2. **读取模板文件**:使用PHP的文件操作函数(如`fopen()`和`fread()`)读取模板...
1. 初始化:加载模板文件,解析其内容并识别占位符。 2. 替换数据:用实际的业务数据替换模板中的占位符。 3. 保存或导出:将替换后的文档保存到本地存储,或者以流的形式通过网络发送。 4. 显示或分享:用户可以...
- 对于long long类型的输入输出,不同平台可能需要使用不同的占位符,如Linux下的`%lld`,Windows下的`%I64d`或`%lld`。 了解和熟练掌握for循环是C语言编程的基础,它在解决各种算法和问题中起到关键作用。通过...
“工具”这个标签则可能意味着这篇博文讨论了一款或几款实用的开发工具,如IDE(集成开发环境)、版本控制系统(如Git)、构建工具(如Maven)、调试器、性能分析工具等。工具的使用能够提升开发效率,优化工作流程...
2. 使用NPOI API解析模板,找到需要替换的字段和图像占位符。 3. 遍历对象的字段和List属性,将数据填充到对应的占位符中。 4. 对于图片,可能需要先将图片数据保存到内存或磁盘,然后通过NPOI添加到文档中。 5. ...
标题中的"GEMGEMGEMGEMGEM"似乎是一个占位符,没有提供实际的标题信息,但从标签中我们得知这个主题与"C#"编程语言有关。结合压缩包内的文件名称,我们可以推测这是一个关于工业自动化控制系统的资料包,特别是与PLC...
"占位符表示,客户端先发送SQL模板,服务器返回一个预处理语句标识符,之后客户端可以多次发送绑定参数和执行命令。 在源码层面,MySQL协议的实现位于MySQL服务器的`libmysqlclient`库中,对于开发者来说,可以利用...
1. **HTML结构**:构建基本的菜单结构,包括一级菜单和二级菜单的占位符。 2. **JavaScript/jQuery**:绑定事件监听器,通常是一级菜单的`onchange`事件,当用户做出选择时触发AJAX请求。 3. **AJAX请求**:使用`...
7. **轻量级**:作为一款小型库,routd的体积小,依赖少,易于集成到现有项目中,适合那些希望避免大型框架复杂性的开发者。 8. **开源项目**:作为一个开源项目,routd的源代码公开,允许开发者查看、学习和贡献...
LQIP 是一种优化网页加载速度的技术,通过在图片真实数据加载前显示一个低质量的占位符,提高用户体验,特别是在网络环境较慢的情况下。 ### 1. Laravel 框架基础 在深入理解 Laravellqip 之前,我们需要对 ...
这里的 `SConnectionString` 用于构建连接字符串,其中 `%s` 是占位符,分别代表数据库文件的路径和密码。 ### 总结 以上代码示例展示了如何在 Delphi 中使用 OLE 对象进行 Microsoft Access 数据库的创建、压缩等...
在提供的文件“SmartMarkerDesigner.xls”中,很可能是一个预设计好的模板,包含了一些占位符,这些占位符会在运行时被实际的数据替换。开发者可以使用Smart Marker解析XML或数据库中的数据,并将其填充到Excel模板...
在制作幻灯片时,占位符是一种非常实用的功能。它主要用于预设文本或图形的位置,方便用户按照一定的布局结构添加内容。占位符的作用在于: - **为文本预留位置**:用户可以在占位符中输入文本,确保文字排列整齐。...