add EnumItem and enhance converter

This commit is contained in:
Looly 2020-09-04 20:38:14 +08:00
parent 945a631f2b
commit 6a374133c7
6 changed files with 193 additions and 176 deletions

View File

@ -61,7 +61,6 @@ import java.time.temporal.TemporalAccessor;
import java.util.Calendar;
import java.util.Collection;
import java.util.Currency;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Optional;
@ -81,27 +80,34 @@ import java.util.concurrent.atomic.AtomicReference;
* <p>
* 在此类中存放着默认转换器和自定义转换器默认转换器是Hutool中预定义的一些转换器自定义转换器存放用户自定的转换器
* </p>
*
* @author Looly
*
* @author Looly
*/
public class ConverterRegistry implements Serializable{
public class ConverterRegistry implements Serializable {
private static final long serialVersionUID = 1L;
/** 默认类型转换器 */
/**
* 默认类型转换器
*/
private Map<Type, Converter<?>> defaultConverterMap;
/** 用户自定义类型转换器 */
/**
* 用户自定义类型转换器
*/
private volatile Map<Type, Converter<?>> customConverterMap;
/** 类级的内部类,也就是静态的成员式内部类,该内部类的实例与外部类的实例 没有绑定关系,而且只有被调用到才会装载,从而实现了延迟加载 */
/**
* 类级的内部类也就是静态的成员式内部类该内部类的实例与外部类的实例 没有绑定关系而且只有被调用到才会装载从而实现了延迟加载
*/
private static class SingletonHolder {
/** 静态初始化器由JVM来保证线程安全 */
/**
* 静态初始化器由JVM来保证线程安全
*/
private static final ConverterRegistry INSTANCE = new ConverterRegistry();
}
/**
* 获得单例的 {@link ConverterRegistry}
*
*
* @return {@link ConverterRegistry}
*/
public static ConverterRegistry getInstance() {
@ -111,27 +117,28 @@ public class ConverterRegistry implements Serializable{
public ConverterRegistry() {
defaultConverter();
putCustomBySpi();
}
@SuppressWarnings("rawtypes")
/**
* 使用SPI加载转换器
*/
private void putCustomBySpi() {
List<Converter> list = ServiceLoaderUtil.loadList(Converter.class);
list.forEach(converter->{
ServiceLoaderUtil.load(Converter.class).forEach(converter -> {
try {
Type type = TypeUtil.getTypeArgument(ClassUtil.getClass(converter));
putCustom(type, converter);
if(null != type){
putCustom(type, converter);
}
} catch (Exception e) {
// 忽略注册失败的
// 忽略注册失败的
}
});
}
/**
* 登记自定义转换器
*
* @param type 转换的目标类型
*
* @param type 转换的目标类型
* @param converterClass 转换器类必须有默认构造方法
* @return {@link ConverterRegistry}
*/
@ -141,8 +148,8 @@ public class ConverterRegistry implements Serializable{
/**
* 登记自定义转换器
*
* @param type 转换的目标类型
*
* @param type 转换的目标类型
* @param converter 转换器
* @return {@link ConverterRegistry}
*/
@ -160,10 +167,9 @@ public class ConverterRegistry implements Serializable{
/**
* 获得转换器<br>
*
* @param <T> 转换的目标类型
*
* @param type 类型
*
* @param <T> 转换的目标类型
* @param type 类型
* @param isCustomFirst 是否自定义转换器优先
* @return 转换器
*/
@ -185,8 +191,8 @@ public class ConverterRegistry implements Serializable{
/**
* 获得默认转换器
*
* @param <T> 转换的目标类型转换器转换到的类型
*
* @param <T> 转换的目标类型转换器转换到的类型
* @param type 类型
* @return 转换器
*/
@ -197,9 +203,8 @@ public class ConverterRegistry implements Serializable{
/**
* 获得自定义转换器
*
* @param <T> 转换的目标类型转换器转换到的类型
*
*
* @param <T> 转换的目标类型转换器转换到的类型
* @param type 类型
* @return 转换器
*/
@ -210,11 +215,11 @@ public class ConverterRegistry implements Serializable{
/**
* 转换值为指定类型
*
* @param <T> 转换的目标类型转换器转换到的类型
* @param type 类型目标
* @param value 被转换值
* @param defaultValue 默认值
*
* @param <T> 转换的目标类型转换器转换到的类型
* @param type 类型目标
* @param value 被转换值
* @param defaultValue 默认值
* @param isCustomFirst 是否自定义转换器优先
* @return 转换后的值
* @throws ConvertException 转换器不存在
@ -231,9 +236,9 @@ public class ConverterRegistry implements Serializable{
if (TypeUtil.isUnknow(type)) {
type = defaultValue.getClass();
}
if(type instanceof TypeReference) {
type = ((TypeReference<?>)type).getType();
if (type instanceof TypeReference) {
type = ((TypeReference<?>) type).getType();
}
// 标准转换器
@ -251,18 +256,18 @@ public class ConverterRegistry implements Serializable{
return (T) value;
}
}
// 特殊类型转换包括CollectionMap强转Array等
final T result = convertSpecial(type, rowType, value, defaultValue);
if (null != result) {
return result;
}
// 尝试转Bean
if (BeanUtil.isBean(rowType)) {
return new BeanConverter<T>(type).convert(value, defaultValue);
}
// 无法转换
throw new ConvertException("No Converter for type [{}]", rowType.getName());
}
@ -270,10 +275,10 @@ public class ConverterRegistry implements Serializable{
/**
* 转换值为指定类型<br>
* 自定义转换器优先
*
* @param <T> 转换的目标类型转换器转换到的类型
* @param type 类型
* @param value
*
* @param <T> 转换的目标类型转换器转换到的类型
* @param type 类型
* @param value
* @param defaultValue 默认值
* @return 转换后的值
* @throws ConvertException 转换器不存在
@ -284,9 +289,9 @@ public class ConverterRegistry implements Serializable{
/**
* 转换值为指定类型
*
* @param <T> 转换的目标类型转换器转换到的类型
* @param type 类型
*
* @param <T> 转换的目标类型转换器转换到的类型
* @param type 类型
* @param value
* @return 转换后的值默认为<code>null</code>
* @throws ConvertException 转换器不存在
@ -296,20 +301,21 @@ public class ConverterRegistry implements Serializable{
}
// ----------------------------------------------------------- Private method start
/**
* 特殊类型转换<br>
* 包括
*
*
* <pre>
* Collection
* Map
* 强转无需转换
* 数组
* </pre>
*
* @param <T> 转换的目标类型转换器转换到的类型
* @param type 类型
* @param value
*
* @param <T> 转换的目标类型转换器转换到的类型
* @param type 类型
* @param value
* @param defaultValue 默认值
* @return 转换后的值
*/
@ -357,7 +363,7 @@ public class ConverterRegistry implements Serializable{
/**
* 注册默认转换器
*
*
* @return 转换器
*/
private ConverterRegistry defaultConverter() {

View File

@ -1,64 +0,0 @@
package cn.hutool.core.convert;
import java.io.Serializable;
/**
*
*枚举元素通用接口在自定义枚举上实现此接口可以用于数据转换<br>
*数据库保存时建议保存 intVal()而非ordinal()防备需求变更<br>
* @param <E>
*/
public interface EnumItem<E extends EnumItem<E>> extends Serializable{
String name();
/**
* 在中文语境下多数时间枚举会配合一个中文说明
*/
default String text() {
return name();
}
int intVal();
@SuppressWarnings("unchecked")
default E[] items() {
return (E[]) this.getClass().getEnumConstants();
}
/**
* 通过int类型值查找兄弟其他枚举
* @param intVal
* @return
*/
default E fromInt(Integer intVal) {
if(intVal==null) {
return null;
}
E[] vs = items();
for (E enumItem : vs) {
if(enumItem.intVal()==intVal.intValue()) {
return enumItem;
}
}
return null;
}
/**
* 通过String类型的值转换根据实现可以用name/text
* @param intVal
* @return
*/
default E fromStr(String strVal) {
if(strVal==null) {
return null;
}
E[] vs = items();
for (E enumItem : vs) {
if(strVal.equalsIgnoreCase(enumItem.name())) {
return enumItem;
}
}
return null;
}
}

View File

@ -1,7 +1,8 @@
package cn.hutool.core.convert.impl;
import cn.hutool.core.convert.AbstractConverter;
import cn.hutool.core.convert.EnumItem;
import cn.hutool.core.lang.EnumItem;
import cn.hutool.core.lang.SimpleCache;
import cn.hutool.core.map.MapUtil;
import cn.hutool.core.util.ClassUtil;
import cn.hutool.core.util.EnumUtil;
@ -11,7 +12,6 @@ import cn.hutool.core.util.ReflectUtil;
import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.stream.Collectors;
/**
@ -24,7 +24,7 @@ import java.util.stream.Collectors;
public class EnumConverter extends AbstractConverter<Object> {
private static final long serialVersionUID = 1L;
private static final Map<Class<?>, Map<Class<?>, Method>> VALUE_OF_METHOD_CACHE = new ConcurrentHashMap<>();
private static final SimpleCache<Class<?>, Map<Class<?>, Method>> VALUE_OF_METHOD_CACHE = new SimpleCache<>();
private final Class enumClass;
@ -40,7 +40,7 @@ public class EnumConverter extends AbstractConverter<Object> {
@Override
protected Object convertInternal(Object value) {
Enum enumValue = tryConvertEnum(value, this.enumClass);
if(null == enumValue && false == value instanceof String){
if (null == enumValue && false == value instanceof String) {
// 最后尝试valueOf转换
enumValue = Enum.valueOf(this.enumClass, convertToStr(value));
}
@ -56,34 +56,41 @@ public class EnumConverter extends AbstractConverter<Object> {
* 尝试找到类似转换的静态方法调用实现转换且优先使用<br>
* 约定枚举类应该提供 valueOf(String) valueOf(Integer)用于转换
* oriInt /name 转换托底
*
*
* @param value 被转换的值
* @param enumClass enum类
* @return 对应的枚举值
*/
protected static Enum tryConvertEnum(Object value, Class enumClass) {
if(value==null) {
if (value == null) {
return null;
}
// EnumItem实现转换
Enum enumResult = null;
if(EnumItem.class.isAssignableFrom(enumClass)) {
EnumItem first = (EnumItem) EnumUtil.getEnumAt(enumClass, 0);
if(value instanceof Integer) {
return (Enum) first.fromInt((Integer) value);
}else if(value instanceof String){
return (Enum) first.fromStr( value.toString());
if (EnumItem.class.isAssignableFrom(enumClass)) {
final EnumItem first = (EnumItem) EnumUtil.getEnumAt(enumClass, 0);
if(null != first){
if (value instanceof Integer) {
return (Enum) first.fromInt((Integer) value);
} else if (value instanceof String) {
return (Enum) first.fromStr(value.toString());
}
}
}
// 用户自定义方法优先
final Map<Class<?>, Method> valueOfMethods = getValueOfMethods(enumClass);
if (MapUtil.isNotEmpty(valueOfMethods)) {
// 用户自定义方法
// 查找枚举中所有返回值为目标枚举对象的方法如果发现方法参数匹配就执行之
final Map<Class<?>, Method> methodMap = getMethodMap(enumClass);
if (MapUtil.isNotEmpty(methodMap)) {
final Class<?> valueClass = value.getClass();
for (Map.Entry<Class<?>, Method> entry : valueOfMethods.entrySet()) {
for (Map.Entry<Class<?>, Method> entry : methodMap.entrySet()) {
if (ClassUtil.isAssignable(entry.getKey(), valueClass)) {
enumResult = ReflectUtil.invokeStatic(entry.getValue(), value);
}
}
}
//oriInt 应该滞后使用 GB/T 2261.1-2003 性别编码为例对应整数并非连续数字会导致数字转枚举时失败
//0 - 未知的性别
//1 - 男性
@ -91,9 +98,9 @@ public class EnumConverter extends AbstractConverter<Object> {
//5 - 女性改()为男性
//6 - 男性改()为女性
//9 - 未说明的性别
if(null == enumResult){
if (null == enumResult) {
if (value instanceof Integer) {
enumResult = EnumUtil.getEnumAt(enumClass, (Integer)value);
enumResult = EnumUtil.getEnumAt(enumClass, (Integer) value);
} else if (value instanceof String) {
try {
enumResult = Enum.valueOf(enumClass, (String) value);
@ -109,19 +116,14 @@ public class EnumConverter extends AbstractConverter<Object> {
* 获取用于转换为enum的所有static方法
*
* @param enumClass 枚举类
* @return 转换方法map
* @return 转换方法mapkey为方法参数类型value为方法
*/
private static Map<Class<?>, Method> getValueOfMethods(Class<?> enumClass) {
Map<Class<?>, Method> valueOfMethods = VALUE_OF_METHOD_CACHE.get(enumClass);
if (null == valueOfMethods) {
valueOfMethods = Arrays.stream(enumClass.getMethods())
.filter(ModifierUtil::isStatic)
.filter(m -> m.getReturnType() == enumClass)
.filter(m -> m.getParameterCount() == 1)
.filter(m -> false == "valueOf".equals(m.getName()))
.collect(Collectors.toMap(m -> m.getParameterTypes()[0], m -> m, (existing, replacement) -> existing));
VALUE_OF_METHOD_CACHE.put(enumClass, valueOfMethods);
}
return valueOfMethods;
private static Map<Class<?>, Method> getMethodMap(Class<?> enumClass) {
return VALUE_OF_METHOD_CACHE.get(enumClass, ()-> Arrays.stream(enumClass.getMethods())
.filter(ModifierUtil::isStatic)
.filter(m -> m.getReturnType() == enumClass)
.filter(m -> m.getParameterCount() == 1)
.filter(m -> false == "valueOf".equals(m.getName()))
.collect(Collectors.toMap(m -> m.getParameterTypes()[0], m -> m)));
}
}

View File

@ -0,0 +1,78 @@
package cn.hutool.core.lang;
import java.io.Serializable;
/**
* 枚举元素通用接口在自定义枚举上实现此接口可以用于数据转换<br>
* 数据库保存时建议保存 intVal()而非ordinal()防备需求变更<br>
*
* @param <E> Enum类型
* @author nierjia
* @since 5.4.2
*/
public interface EnumItem<E extends EnumItem<E>> extends Serializable {
String name();
/**
* 在中文语境下多数时间枚举会配合一个中文说明
*
* @return enum名
*/
default String text() {
return name();
}
int intVal();
/**
* 获取所有枚举对象
*
* @return 枚举对象数组
*/
@SuppressWarnings("unchecked")
default E[] items() {
return (E[]) this.getClass().getEnumConstants();
}
/**
* 通过int类型值查找兄弟其他枚举
*
* @param intVal int值
* @return Enum
*/
default E fromInt(Integer intVal) {
if (intVal == null) {
return null;
}
E[] vs = items();
for (E enumItem : vs) {
if (enumItem.intVal() == intVal) {
return enumItem;
}
}
return null;
}
/**
* 通过String类型的值转换根据实现可以用name/text
*
* @param strVal String值
* @return Enum
*/
default E fromStr(String strVal) {
if (strVal == null) {
return null;
}
E[] vs = items();
for (E enumItem : vs) {
if (strVal.equalsIgnoreCase(enumItem.name())) {
return enumItem;
}
}
return null;
}
}

View File

@ -1,6 +1,7 @@
package cn.hutool.core.util;
import java.util.ArrayList;
import cn.hutool.core.collection.ListUtil;
import java.util.Iterator;
import java.util.List;
import java.util.ServiceConfigurationError;
@ -30,7 +31,7 @@ public class ServiceLoaderUtil {
*/
public static <T> T loadFirstAvailable(Class<T> clazz) {
final Iterator<T> iterator = load(clazz).iterator();
while(iterator.hasNext()){
while (iterator.hasNext()) {
try {
return iterator.next();
} catch (ServiceConfigurationError e) {
@ -49,7 +50,7 @@ public class ServiceLoaderUtil {
*/
public static <T> T loadFirst(Class<T> clazz) {
final Iterator<T> iterator = load(clazz).iterator();
if(iterator.hasNext()){
if (iterator.hasNext()) {
return iterator.next();
}
return null;
@ -63,7 +64,7 @@ public class ServiceLoaderUtil {
* @return 服务接口实现列表
*/
public static <T> ServiceLoader<T> load(Class<T> clazz) {
return load(clazz,null);
return load(clazz, null);
}
/**
@ -75,37 +76,31 @@ public class ServiceLoaderUtil {
* @return 服务接口实现列表
*/
public static <T> ServiceLoader<T> load(Class<T> clazz, ClassLoader loader) {
if(loader==null) {
loader = Thread.currentThread().getContextClassLoader();
}
return ServiceLoader.load(clazz, loader);
}
/**
* 加载服务 并已list列表返回
* @param <T> 接口类型
*
* @param <T> 接口类型
* @param clazz 服务接口
* @return 服务接口实现列表
* @since 5.4.2
*/
public static <T> List<T> loadList(Class<T> clazz){
return loadList(clazz);
public static <T> List<T> loadList(Class<T> clazz) {
return loadList(clazz, null);
}
/**
* 加载服务 并已list列表返回
* @param <T> 接口类型
* @param clazz 服务接口
*
* @param <T> 接口类型
* @param clazz 服务接口
* @param loader {@link ClassLoader}
* @return 服务接口实现列表
* @since 5.4.2
*/
public static <T> List<T> loadList(Class<T> clazz, ClassLoader loader){
final Iterator<T> iterator = load(clazz).iterator();
List<T> list=new ArrayList<>();
while(iterator.hasNext()){
try {
list.add(iterator.next());
} catch (ServiceConfigurationError e) {
// ignore
}
}
return list;
public static <T> List<T> loadList(Class<T> clazz, ClassLoader loader) {
return ListUtil.list(false, load(clazz));
}
}

View File

@ -836,7 +836,7 @@ public class XmlUtil {
*/
public static <T> T xmlToBean(Node node, Class<T> bean) {
final Map<String, Object> map = xmlToMap(node);
if(null != map && map.size() == 1){
if (null != map && map.size() == 1) {
return BeanUtil.toBean(map.get(bean.getSimpleName()), bean);
}
return BeanUtil.toBean(map, bean);
@ -1049,7 +1049,7 @@ public class XmlUtil {
/**
* 将Bean转换为XML
*
* @param bean Bean对象
* @param bean Bean对象
* @return XML
* @since 5.3.4
*/
@ -1269,7 +1269,7 @@ public class XmlUtil {
*/
private void examineNode(Node node, boolean attributesOnly) {
final NamedNodeMap attributes = node.getAttributes();
if(null != attributes){
if (null != attributes) {
for (int i = 0; i < attributes.getLength(); i++) {
Node attribute = attributes.item(i);
storeAttribute(attribute);
@ -1278,7 +1278,7 @@ public class XmlUtil {
if (false == attributesOnly) {
final NodeList childNodes = node.getChildNodes();
if(null != childNodes){
if (null != childNodes) {
Node item;
for (int i = 0; i < childNodes.getLength(); i++) {
item = childNodes.item(i);
@ -1296,7 +1296,7 @@ public class XmlUtil {
* @param attribute to examine
*/
private void storeAttribute(Node attribute) {
if(null == attribute){
if (null == attribute) {
return;
}
// examine the attributes in namespace xmlns