import com.amp.base.exception.BusinessException;
import org.springframework.util.PropertyPlaceholderHelper;
import java.lang.reflect.Field;
import java.util.Date;
import java.util.Map;
/**
* 占位符解析器
*
* @author system
* @version 1.0.0
* @date 2020-02-17 11:14
*/
public class PlaceholderResolver {
private static final PropertyPlaceholderHelper defaultHelper = new PropertyPlaceholderHelper("${", "}");
private static PlaceholderResolver defaultResolver = new PlaceholderResolver();
private PropertyPlaceholderHelper propertyPlaceholderHelper;
private PlaceholderResolver() {
propertyPlaceholderHelper = defaultHelper;
}
private PlaceholderResolver(String placeholderPrefix, String placeholderSuffix) {
propertyPlaceholderHelper = new PropertyPlaceholderHelper(placeholderPrefix, placeholderSuffix);
}
/**
* 獲取默認的占位符解析器,即占位符前綴為"${", 后綴為"}"
*
* @return
*/
public static PlaceholderResolver getDefaultResolver() {
return defaultResolver;
}
public static PlaceholderResolver getResolver(String placeholderPrefix, String placeholderSuffix) {
return new PlaceholderResolver(placeholderPrefix, placeholderSuffix);
}
/**
* 解析帶有指定占位符的模板字符串
* 如:content = ${0}今年${1}歲<br/>
* values = {"xiaoming", "18"}<br/>
* result 小明今年18歲<br/>
*
* @param content 要解析的帶有占位符的模板字符串
* @param values 按照模板占位符索引位置設置對應的值
* @return
*/
public String resolve(String content, String... values) {
return propertyPlaceholderHelper.replacePlaceholders(content, placeholderName -> {
return values[Integer.valueOf(placeholderName)];
});
}
/**
* 解析帶有指定占位符的模板字符串
* 如:content = ${0}今年${1}歲<br/>
* values = {"xiaoming", "18"}<br/>
* result 小明今年18歲<br/>
*
* @param content 要解析的帶有占位符的模板字符串
* @param values 按照模板占位符索引位置設置對應的值
* @return
*/
public String resolve(String content, Object[] values) {
return propertyPlaceholderHelper.replacePlaceholders(content, placeholderName -> {
return String.valueOf(values[Integer.valueOf(placeholderName)]);
});
}
/**
* 根據替換規則來替換指定模板中的占位符值
*
* @param content 要解析的字符串
* @param placeholderResolver 解析規則回調
* @return
*/
public String resolveByRule(String content, PropertyPlaceholderHelper.PlaceholderResolver placeholderResolver) {
return propertyPlaceholderHelper.replacePlaceholders(content, placeholderResolver);
}
/**
* 替換模板中占位符內容,占位符的內容即為map key對應的值,key為占位符中的內容。<br/><br/>
* 如:content = ${name}今年${age}歲<br/>
* valueMap = name -> 小明; age -> 18<br/>
* result 小明今年18歲<br/>
*
* @param content 模板內容。
* @param valueMap 值映射
* @return 替換完成后的字符串。
*/
public String resolveByMap(String content, final Map<String, Object> valueMap) {
return propertyPlaceholderHelper.replacePlaceholders(content, placeholderName -> {
return String.valueOf(valueMap.get(placeholderName));
});
}
/**
* 根據對象中字段路徑(即類似js訪問對象屬性值)替換模板中的占位符 <br/><br/>
* 如 content = product:${id}:detail:${detail.id} <br/>
* obj = Product.builder().id(1).detail(Detail.builder().id(2).build()).build(); <br/>
* 經過解析返回 product:1:detail:2 <br/>
*
* @param content 要解析的內容
* @param obj 填充解析內容的對象(如果是基本類型,則所有占位符都替換為當前基本類型)
* @return
*/
public String resolveByObject(String content, final Object obj) {
if (obj instanceof Map) {
return resolveByMap(content, (Map) obj);
}
return propertyPlaceholderHelper.replacePlaceholders(content, placeholderName -> {
return String.valueOf(getValueByFieldPath(obj, placeholderName));
});
}
/**
* 獲取指定對象中指定字段路徑的值
* $(user.name)
*
* @param obj 取值對象
* @param fieldPath 字段路徑(形如 user.name)
* @return
*/
private Object getValueByFieldPath(Object obj, String fieldPath) {
String[] fieldNames = fieldPath.split("\\.");
Object result = null;
for (String fieldName : fieldNames) {
result = getFieldValue(obj, fieldName);
if (result == null) {
throw new BusinessException(fieldName + "為空!");
}
obj = result;
}
return result;
}
private Object getFieldValue(Object obj, String fieldName) {
Class clazz = obj.getClass();
if (isBaseType(clazz)) {
return obj;
}
while (clazz != Object.class && clazz != null) {
try {
Field field = clazz.getDeclaredField(fieldName);
field.setAccessible(true);
return field.get(obj);
} catch (NoSuchFieldException e) {
clazz = clazz.getSuperclass();
} catch (IllegalAccessException e) {
throw new BusinessException("無法訪問 " + fieldName);
}
}
throw new IllegalStateException(fieldName + "字段不存在!");
}
/**
* 判斷class是否為常用類型
*
* @param clazz
* @return
*/
private boolean isBaseType(Class clazz) {
return Enum.class.isAssignableFrom(clazz) || CharSequence.class.isAssignableFrom(clazz)
|| Number.class.isAssignableFrom(clazz) || Date.class.isAssignableFrom(clazz);
}
}