diff --git a/pom.xml b/pom.xml index b591b7b..fb56057 100644 --- a/pom.xml +++ b/pom.xml @@ -77,12 +77,13 @@ com.imyeyu.java timi-java - 0.0.1 + 0.0.2 de.mkammerer argon2-jvm 2.12 + compile org.junit.jupiter diff --git a/src/main/java/com/imyeyu/utils/StringInterpolator.java b/src/main/java/com/imyeyu/utils/StringInterpolator.java index fef9566..5e38c88 100644 --- a/src/main/java/com/imyeyu/utils/StringInterpolator.java +++ b/src/main/java/com/imyeyu/utils/StringInterpolator.java @@ -6,6 +6,8 @@ import com.imyeyu.java.ref.Ref; import java.util.HashMap; import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; +import java.util.regex.Matcher; import java.util.regex.Pattern; /** @@ -16,26 +18,21 @@ import java.util.regex.Pattern; */ 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; - /** 为 true 时允许安全插值空,变量可以插值 null 而不抛出异常 */ + /** 过滤器映射 */ + private final Map> filterMap = new HashMap<>(); + + /** 空值处理策略 */ private boolean nullable = false; - /** 过滤器 */ - private Map> filterMap; - - /** - * 默认构造 - * - * @param regex 插槽正则 - */ public StringInterpolator(String regex) { this.pattern = Pattern.compile(regex); } @@ -43,79 +40,114 @@ public class StringInterpolator { /** * 注入变量 * - * @param string 字符串 - * @param argsMap 变量表 + * @param template 字符串 + * @param argsMap 变量表 * @return 插值结果 */ - public String inject(String string, Map argsMap) { - return pattern.matcher(string).replaceAll(result -> { - String group = result.group(1); - String key = group.trim(); - String[] filters = null; - if (group.contains("|")) { - // 过滤器 - String[] groups = group.split("\\|"); - key = groups[0].trim(); - filters = new String[groups.length - 1]; - System.arraycopy(groups, 1, filters, 0, filters.length); + 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); } - Object value = argsMap.get(key); - if (key.contains(".")) { - // 反射获取 - try { - String[] deepKey = key.split("\\."); - value = argsMap.get(deepKey[0]); - for (int i = 1; i < deepKey.length; i++) { - value = Ref.getFieldValue(value, deepKey[i], Object.class); - } - } catch (IllegalAccessException e) { - throw new RuntimeException("ref field file: " + e.getMessage(), e); - } + 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)); } - if (TimiJava.isNotEmpty(filterMap) && TimiJava.isNotEmpty(filters)) { - // 过滤 - for (int i = 0; i < filters.length; i++) { - CallbackArgReturn filter = filterMap.get(filters[i].trim()); - if (filter == null) { - throw new NullPointerException("not found %s filter for %s".formatted(filters[i], key)); - } - value = filter.handler((String) value); - } + 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)); } - if (value == null) { - if (!nullable) { - throw new NullPointerException("null pointer exception for arg: " + key); - } - value = ""; - } - return String.valueOf(value); - }); + return ""; + } + return String.valueOf(value); } public void putFilter(String name, CallbackArgReturn callback) { - if (filterMap == null) { - filterMap = new HashMap<>(); - } filterMap.put(name, callback); } - public void putAllFilter(Map> filterMap) { - if (this.filterMap == null) { - this.filterMap = new HashMap<>(); - } - this.filterMap.putAll(filterMap); + public void putAllFilter(Map> filters) { + filterMap.putAll(filters); } public void removeFilter(String name) { - if (TimiJava.isNotEmpty(filterMap)) { - filterMap.remove(name); - } + filterMap.remove(name); } public void clearFilter() { - if (TimiJava.isNotEmpty(filterMap)) { - filterMap.clear(); - } + filterMap.clear(); } public void setNullable(boolean nullable) { @@ -125,4 +157,16 @@ public class StringInterpolator { public boolean isNullable() { return nullable; } + + /** 表达式部分内部类 */ + 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); + } }