|
|
|
|
@ -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<String, String[]> deepKeyCache = new ConcurrentHashMap<>();
|
|
|
|
|
|
|
|
|
|
/** 正则匹配器 */
|
|
|
|
|
private final Pattern pattern;
|
|
|
|
|
|
|
|
|
|
/** 为 true 时允许安全插值空,变量可以插值 null 而不抛出异常 */
|
|
|
|
|
/** 过滤器映射 */
|
|
|
|
|
private final Map<String, CallbackArgReturn<String, String>> filterMap = new HashMap<>();
|
|
|
|
|
|
|
|
|
|
/** 空值处理策略 */
|
|
|
|
|
private boolean nullable = false;
|
|
|
|
|
|
|
|
|
|
/** 过滤器 */
|
|
|
|
|
private Map<String, CallbackArgReturn<String, String>> filterMap;
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 默认构造
|
|
|
|
|
*
|
|
|
|
|
* @param regex 插槽正则
|
|
|
|
|
*/
|
|
|
|
|
public StringInterpolator(String regex) {
|
|
|
|
|
this.pattern = Pattern.compile(regex);
|
|
|
|
|
}
|
|
|
|
|
@ -43,80 +40,115 @@ public class StringInterpolator {
|
|
|
|
|
/**
|
|
|
|
|
* 注入变量
|
|
|
|
|
*
|
|
|
|
|
* @param string 字符串
|
|
|
|
|
* @param template 字符串
|
|
|
|
|
* @param argsMap 变量表
|
|
|
|
|
* @return 插值结果
|
|
|
|
|
*/
|
|
|
|
|
public String inject(String string, Map<String, Object> 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<String, Object> argsMap) {
|
|
|
|
|
if (TimiJava.isEmpty(template) || TimiJava.isEmpty(argsMap)) {
|
|
|
|
|
return template;
|
|
|
|
|
}
|
|
|
|
|
Object value = argsMap.get(key);
|
|
|
|
|
if (key.contains(".")) {
|
|
|
|
|
// 反射获取
|
|
|
|
|
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<String, Object> 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<String, Object> argsMap) {
|
|
|
|
|
Object value = argsMap.get(variable);
|
|
|
|
|
if (variable.contains(".")) {
|
|
|
|
|
value = getNestedPropertyValue(variable, argsMap);
|
|
|
|
|
}
|
|
|
|
|
return value;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/** 获取嵌套属性值 */
|
|
|
|
|
private Object getNestedPropertyValue(String deepKey, Map<String, Object> argsMap) {
|
|
|
|
|
String[] keyPath = deepKeyCache.computeIfAbsent(deepKey, k -> k.split("\\."));
|
|
|
|
|
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);
|
|
|
|
|
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 file: " + e.getMessage(), e);
|
|
|
|
|
throw new RuntimeException("ref field error: " + e.getMessage(), e);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if (TimiJava.isNotEmpty(filterMap) && TimiJava.isNotEmpty(filters)) {
|
|
|
|
|
// 过滤
|
|
|
|
|
for (int i = 0; i < filters.length; i++) {
|
|
|
|
|
CallbackArgReturn<String, String> filter = filterMap.get(filters[i].trim());
|
|
|
|
|
|
|
|
|
|
/** 应用过滤器链 */
|
|
|
|
|
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<String, String> filter = filterMap.get(filterName);
|
|
|
|
|
if (filter == null) {
|
|
|
|
|
throw new NullPointerException("not found %s filter for %s".formatted(filters[i], key));
|
|
|
|
|
throw new IllegalArgumentException("not found %s filter for".formatted(filterName));
|
|
|
|
|
}
|
|
|
|
|
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("null pointer exception for arg: " + key);
|
|
|
|
|
throw new NullPointerException("not found %s value".formatted(variable));
|
|
|
|
|
}
|
|
|
|
|
value = "";
|
|
|
|
|
return "";
|
|
|
|
|
}
|
|
|
|
|
return String.valueOf(value);
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public void putFilter(String name, CallbackArgReturn<String, String> callback) {
|
|
|
|
|
if (filterMap == null) {
|
|
|
|
|
filterMap = new HashMap<>();
|
|
|
|
|
}
|
|
|
|
|
filterMap.put(name, callback);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public void putAllFilter(Map<String, CallbackArgReturn<String, String>> filterMap) {
|
|
|
|
|
if (this.filterMap == null) {
|
|
|
|
|
this.filterMap = new HashMap<>();
|
|
|
|
|
}
|
|
|
|
|
this.filterMap.putAll(filterMap);
|
|
|
|
|
public void putAllFilter(Map<String, CallbackArgReturn<String, String>> filters) {
|
|
|
|
|
filterMap.putAll(filters);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public void removeFilter(String name) {
|
|
|
|
|
if (TimiJava.isNotEmpty(filterMap)) {
|
|
|
|
|
filterMap.remove(name);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public void clearFilter() {
|
|
|
|
|
if (TimiJava.isNotEmpty(filterMap)) {
|
|
|
|
|
filterMap.clear();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public void setNullable(boolean nullable) {
|
|
|
|
|
this.nullable = 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);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|