package com.imyeyu.utils; import com.imyeyu.java.TimiJava; import com.imyeyu.java.bean.CallbackArgReturn; import com.imyeyu.java.ref.Ref; import lombok.Getter; import lombok.Setter; import java.util.HashMap; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; import java.util.regex.Matcher; import java.util.regex.Pattern; /** * 字符串插值器 * * @author 夜雨 * @version 2022-12-01 00:41 */ public class StringInterpolator { public static final String DOLLAR_OBJ = "\\$\\{(.+?)\\}"; public static final String SIMPLE_OBJ = "\\{(.+?)\\}"; /** 缓存解析过的嵌套属性路径 */ private final Map deepKeyCache = new ConcurrentHashMap<>(); /** 正则匹配器 */ private final Pattern pattern; /** 过滤器映射 */ private final Map> filterMap = new HashMap<>(); /** 空值处理策略 */ @Setter @Getter private boolean nullable = false; public StringInterpolator(String regex) { this.pattern = Pattern.compile(regex); } /** * 注入变量 * * @param template 字符串 * @param argsMap 变量表 * @return 插值结果 */ public String inject(String template, Map argsMap) { if (TimiJava.isEmpty(template) || TimiJava.isEmpty(argsMap)) { return template; } Matcher matcher = pattern.matcher(template); StringBuilder result = new StringBuilder(); while (matcher.find()) { String replacement = processReplacement(matcher.group(1), argsMap); matcher.appendReplacement(result, Matcher.quoteReplacement(replacement)); } matcher.appendTail(result); return result.toString(); } /** 处理单个替换项 */ private String processReplacement(String expression, Map argsMap) { // 解析表达式:变量名和过滤器 ExpressionParts parts = parseExpression(expression); // 获取变量值 Object value = getVariableValue(parts.variable, argsMap); // 应用过滤器 value = applyFilters(value, parts.filters); // 处理空值 return handleNullValue(value, parts.variable); } /** 解析表达式为变量名和过滤器数组 */ private ExpressionParts parseExpression(String expression) { if (!expression.contains("|")) { return new ExpressionParts(expression.trim(), new String[0]); } String[] parts = expression.split("\\|"); String variable = parts[0].trim(); String[] filters = new String[parts.length - 1]; for (int i = 1; i < parts.length; i++) { filters[i - 1] = parts[i].trim(); } return new ExpressionParts(variable, filters); } /** 获取变量值,支持嵌套属性 */ private Object getVariableValue(String variable, Map argsMap) { Object value = argsMap.get(variable); if (variable.contains(".")) { value = getNestedPropertyValue(variable, argsMap); } return value; } /** 获取嵌套属性值 */ private Object getNestedPropertyValue(String deepKey, Map argsMap) { String[] keyPath = deepKeyCache.computeIfAbsent(deepKey, k -> k.split("\\.")); try { Object value = argsMap.get(keyPath[0]); for (int i = 1; i < keyPath.length && value != null; i++) { value = Ref.getFieldValue(value, keyPath[i], Object.class); } return value; } catch (IllegalAccessException e) { throw new RuntimeException("ref field error: " + e.getMessage(), e); } } /** 应用过滤器链 */ private Object applyFilters(Object value, String[] filters) { if (TimiJava.isEmpty(filters) || TimiJava.isEmpty(filterMap) || value == null) { return value; } String currentValue = String.valueOf(value); for (String filterName : filters) { CallbackArgReturn filter = filterMap.get(filterName); if (filter == null) { throw new IllegalArgumentException("not found %s filter for".formatted(filterName)); } currentValue = filter.handler(currentValue); } return currentValue; } /** 处理空值情况 */ private String handleNullValue(Object value, String variable) { if (value == null) { if (!nullable) { throw new NullPointerException("not found %s value".formatted(variable)); } return ""; } return String.valueOf(value); } public void putFilter(String name, CallbackArgReturn callback) { filterMap.put(name, callback); } public void putAllFilter(Map> filters) { filterMap.putAll(filters); } public void removeFilter(String name) { filterMap.remove(name); } public void clearFilter() { filterMap.clear(); } /** 表达式部分内部类 */ private record ExpressionParts(String variable, String[] filters) { } public static StringInterpolator createDollarInterpolator() { return new StringInterpolator(DOLLAR_OBJ); } public static StringInterpolator createSimpleInterpolator() { return new StringInterpolator(SIMPLE_OBJ); } }