马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。
您需要 登录 才可以下载或查看,没有账号?立即注册
×
思绪
模板变量更换无非是探求出字符串(模板)中的特殊标志,用对应的变量举行字符串更换。
提到变量更换,各人第一能遐想到的大概是Mybatis的动态SQL语句的分析,又大概是mybatis.xml设置文件中用于分析"${username:root}"的默认值。
在Mybatis的源码中,这些功能则是通过GenericTokenParser类来实现的,通过查察源码可以很显着的知道GenericTokenParser只是查找指定的占位符,而具体的分析活动会根据其持有的TokenHandler实现的差别而有所差别,这里是采取了计谋模式。
那么接下来我们就使用Mybatis的思绪,试着写一个字符串更换器吧
字符串更换器
GenericTokenParser
- public class GenericTokenParser {
- private final String openToken;
- private final String closeToken;
- private final TokenHandler handler;
- public GenericTokenParser(String openToken, String closeToken, TokenHandler handler) {
- this.openToken = openToken;
- this.closeToken = closeToken;
- this.handler = handler;
- }
- public String parse(String text) {
- if (text == null || text.isEmpty()) {
- return "";
- }
- // search open token
- int start = text.indexOf(openToken, 0);
- if (start == -1) {
- return text;
- }
- char[] src = text.toCharArray();
- int offset = 0;
- final StringBuilder builder = new StringBuilder();
- StringBuilder expression = null;
- while (start > -1) {
- if (start > 0 && src[start - 1] == '\\') {
- // this open token is escaped. remove the backslash and continue.
- builder.append(src, offset, start - offset - 1).append(openToken);
- offset = start + openToken.length();
- } else {
- // found open token. let's search close token.
- if (expression == null) {
- expression = new StringBuilder();
- } else {
- expression.setLength(0);
- }
- builder.append(src, offset, start - offset);
- offset = start + openToken.length();
- int end = text.indexOf(closeToken, offset);
- while (end > -1) {
- if (end > offset && src[end - 1] == '\\') {
- // this close token is escaped. remove the backslash and continue.
- expression.append(src, offset, end - offset - 1).append(closeToken);
- offset = end + closeToken.length();
- end = text.indexOf(closeToken, offset);
- } else {
- expression.append(src, offset, end - offset);
- offset = end + closeToken.length();
- break;
- }
- }
- if (end == -1) {
- // close token was not found.
- builder.append(src, start, src.length - start);
- offset = src.length;
- } else {
- builder.append(handler.handleToken(expression.toString()));
- offset = end + closeToken.length();
- }
- }
- start = text.indexOf(openToken, offset);
- }
- if (offset < src.length) {
- builder.append(src, offset, src.length - offset);
- }
- return builder.toString();
- }
- }
复制代码 TokenHandler
- public interface TokenHandler {
- String handleToken(String content);
- }
复制代码 以上代码是Mybatis的源码,可以直接抄,接下来编写核心的逻辑:
这里的数据使用Map<String,Object>来存储,key是变量名称,value是变量值,value的范例下列代码仅支持了String和List,对应的可以实现直接变量更换和循环变量更换,有其他须要可以在此根本上新增功能,而List的泛型仅支持Map<String,String>,即不支持多层嵌套。
在这个例子中,我使用${变量名}表现平凡变量,<变量名></变量名>表现循环变量,标签内的内容将被循环展示
- public class ReportGenerator {
- public static void main(String[] args) {
- String reportTemplate = "这是一份贵司专属总结报告,请查收!\n" +
- "您的订单编号:${firstShowId},产品名称:${firstProductName},于${firstOnlineDate}正式生效,今天是贵司正式启用电子签名的第90天,截至今天,您的订单使用情况如下:#{${a}/${b}}\n" +
- "<orders>${orders.productName},累计消耗${orders.displayConsumeAmount}${orders.units}</orders>" +
- "如对使用情况和数据报告有任何疑问,可随时联系你的客户成功经理。\n" +
- "<contracts>合同编号${contracts.contractId},合同负责人:${contracts.contractManager},${name}</contracts>";
- Map<String, Object> data = new HashMap<>();
- data.put("firstShowId", "123456789");
- data.put("firstProductName", "测试产品");
- data.put("firstOnlineDate", "2023-01-01");
- data.put("productName", "测试产品");
- data.put("name", "张三");
- data.put("a", "100");
- data.put("b", "10");
- data.put("c", "10");
- List<Map<String, String>> orders = new ArrayList<>();
- Map<String, String> param1 = new HashMap<>();
- param1.put("productName", "测试产品1");
- param1.put("displayConsumeAmount", "100");
- param1.put("units", "元");
- orders.add(param1);
- Map<String, String> param2 = new HashMap<>();
- param2.put("productName", "测试产品2");
- param2.put("displayConsumeAmount", "200");
- param2.put("units", "元");
- orders.add(param2);
- data.put("orders", orders);
- List<Map<String, String>> contracts = new ArrayList<>();
- Map<String, String> param3 = new HashMap<>();
- param1.put("contractId", "12312543243213");
- param1.put("contractManager", "杜甫");
- contracts.add(param1);
- Map<String, String> param4 = new HashMap<>();
- param2.put("contractId", "1234353453");
- param2.put("contractManager", "李白");
- contracts.add(param2);
- data.put("contracts", contracts);
- String handler = handler(data, reportTemplate);
- // 打印报告
- System.out.println(handler);
- }
- public static String handler(Map<String, Object> data, String templateContent) {
- // 循环变量,仅支持一层嵌套
- Map<String, List<Map<String, String>>> loopVariables = new HashMap<>();
- // 基本参数
- Map<String, String> basicVariables = new HashMap<>();
- // 分类:循环变量,基本参数,计算参数
- for (Map.Entry<String, Object> entry : data.entrySet()) {
- String key = entry.getKey();
- Object value = entry.getValue();
- if (value instanceof List) {
- loopVariables.put(key, (List<Map<String, String>>) value);
- } else if (value instanceof String) {
- basicVariables.put(key, (String) value);
- }
- }
- // 处理循环变量
- for (Map.Entry<String, List<Map<String, String>>> entry : loopVariables.entrySet()) {
- String key = entry.getKey();
- List<Map<String, String>> value = entry.getValue();
- ForEachTokenParser forEachTokenParser = new ForEachTokenParser(key, value, "\n");
- GenericTokenParser genericTokenParser = new GenericTokenParser("<" + key + ">", "</" + key + ">", forEachTokenParser);
- templateContent = genericTokenParser.parse(templateContent);
- }
- // 处理基本参数
- VariableTokenParser variableTokenParser = new VariableTokenParser(basicVariables);
- GenericTokenParser genericTokenParser = new GenericTokenParser("${", "}", variableTokenParser);
- templateContent = genericTokenParser.parse(templateContent);
- // // 计算参数
- // EquationTokenParser equationTokenParser = new EquationTokenParser();
- // GenericTokenParser equationParser = new GenericTokenParser("#{", "}", equationTokenParser);
- // templateContent = equationParser.parse(templateContent);
- return templateContent;
- }
- /**
- * 循环变量参数
- */
- static class ForEachTokenParser implements TokenHandler {
- private final String variableName;
- private final List<Map<String, String>> variables;
- private final String separator;
- public ForEachTokenParser(String variableName, List<Map<String, String>> variable, String separator) {
- this.variableName = variableName;
- this.variables= variable;
- this.separator = separator;
- }
- @Override
- public String handleToken(String content) {
- if (variables == null || variables.isEmpty()) {
- throw new RuntimeException("变量不存在:" + content);
- }
- StringBuilder builder = new StringBuilder();
- for (Map<String, String> variable : variables) {
- VariableTokenParser variableTokenParser = new VariableTokenParser(variable);
- GenericTokenParser genericTokenParser = new GenericTokenParser("${" + variableName + ".", "}", variableTokenParser);
- builder.append(genericTokenParser.parse(content)).append(separator);
- }
- return builder.toString();
- }
- }
- /**
- * 基础变量参数
- */
- static class VariableTokenParser implements TokenHandler {
- private final Map<String, String> variables;
- public VariableTokenParser(Map<String, String> variable) {
- this.variables= variable;
- }
- @Override
- public String handleToken(String content) {
- String value = variables.get(content);
- if (value == null) {
- throw new RuntimeException("变量不存在:" + content);
- }
- return value;
- }
- }
- /**
- * 计算参数
- * 目前仅支持除法运算:#{100/10}
- */
- // static class EquationTokenParser implements TokenHandler {
- //
- // @Override
- // public String handleToken(String content) {
- // return String.valueOf(evaluateExpression(content));
- // }
- //
- // private static double evaluateExpression(String expression) {
- // String[] parts = expression.split("/");
- // if (parts.length == 2) {
- // BigDecimal numerator = new BigDecimal(parts[0].trim());
- // BigDecimal denominator = new BigDecimal(parts[1].trim());
- // return numerator.divide(denominator, 2, RoundingMode.HALF_UP).doubleValue();
- // }
- // return 0;
- // }
- // }
- }
复制代码 |