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);
+ }
}