diff --git a/hutool-core/src/main/java/cn/hutool/core/convert/ConverterRegistry.java b/hutool-core/src/main/java/cn/hutool/core/convert/ConverterRegistry.java index 5b6d2c700..a405e36a0 100644 --- a/hutool-core/src/main/java/cn/hutool/core/convert/ConverterRegistry.java +++ b/hutool-core/src/main/java/cn/hutool/core/convert/ConverterRegistry.java @@ -32,8 +32,10 @@ import cn.hutool.core.convert.impl.URLConverter; import cn.hutool.core.convert.impl.UUIDConverter; import cn.hutool.core.date.DateTime; import cn.hutool.core.lang.TypeReference; +import cn.hutool.core.util.ClassUtil; import cn.hutool.core.util.ObjectUtil; import cn.hutool.core.util.ReflectUtil; +import cn.hutool.core.util.ServiceLoaderUtil; import cn.hutool.core.util.TypeUtil; import java.io.Serializable; @@ -59,6 +61,7 @@ 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; @@ -107,6 +110,22 @@ public class ConverterRegistry implements Serializable{ public ConverterRegistry() { defaultConverter(); + putCustomBySpi(); + + + } + + @SuppressWarnings("rawtypes") + private void putCustomBySpi() { + List list = ServiceLoaderUtil.loadList(Converter.class); + list.forEach(converter->{ + try { + Type type = TypeUtil.getTypeArgument(ClassUtil.getClass(converter)); + putCustom(type, converter); + } catch (Exception e) { + // 忽略注册失败的 + } + }); } /** diff --git a/hutool-core/src/main/java/cn/hutool/core/convert/EnumItem.java b/hutool-core/src/main/java/cn/hutool/core/convert/EnumItem.java new file mode 100644 index 000000000..f620f6ea8 --- /dev/null +++ b/hutool-core/src/main/java/cn/hutool/core/convert/EnumItem.java @@ -0,0 +1,64 @@ +package cn.hutool.core.convert; + +import java.io.Serializable; + +/** + * + *枚举元素通用接口,在自定义枚举上实现此接口可以用于数据转换
+ *数据库保存时建议保存 intVal()而非ordinal()防备需求变更
+ * @param + */ +public interface EnumItem> 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; + } + + +} + diff --git a/hutool-core/src/main/java/cn/hutool/core/convert/impl/EnumConverter.java b/hutool-core/src/main/java/cn/hutool/core/convert/impl/EnumConverter.java index 292dc04c4..634222ecf 100644 --- a/hutool-core/src/main/java/cn/hutool/core/convert/impl/EnumConverter.java +++ b/hutool-core/src/main/java/cn/hutool/core/convert/impl/EnumConverter.java @@ -1,6 +1,7 @@ package cn.hutool.core.convert.impl; import cn.hutool.core.convert.AbstractConverter; +import cn.hutool.core.convert.EnumItem; import cn.hutool.core.map.MapUtil; import cn.hutool.core.util.ClassUtil; import cn.hutool.core.util.EnumUtil; @@ -52,37 +53,55 @@ public class EnumConverter extends AbstractConverter { } /** - * 尝试找到类似转换的静态方法调用实现转换 - * + * 尝试找到类似转换的静态方法调用实现转换且优先使用
+ * 约定枚举类应该提供 valueOf(String) 和 valueOf(Integer)用于转换 + * oriInt /name 转换托底 + * * @param value 被转换的值 * @param enumClass enum类 * @return 对应的枚举值 */ protected static Enum tryConvertEnum(Object value, Class enumClass) { + if(value==null) { + return null; + } Enum enumResult = null; - if (value instanceof Integer) { - enumResult = EnumUtil.getEnumAt(enumClass, (Integer)value); - } else if (value instanceof String) { - try { - enumResult = Enum.valueOf(enumClass, (String) value); - } catch (IllegalArgumentException e) { - //ignore + 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(null == enumResult){ - final Map, Method> valueOfMethods = getValueOfMethods(enumClass); - if (MapUtil.isNotEmpty(valueOfMethods)) { - final Class valueClass = value.getClass(); - for (Map.Entry, Method> entry : valueOfMethods.entrySet()) { - if (ClassUtil.isAssignable(entry.getKey(), valueClass)) { - enumResult = ReflectUtil.invokeStatic(entry.getValue(), value); - } + // 用户自定义方法优先, + final Map, Method> valueOfMethods = getValueOfMethods(enumClass); + if (MapUtil.isNotEmpty(valueOfMethods)) { + final Class valueClass = value.getClass(); + for (Map.Entry, Method> entry : valueOfMethods.entrySet()) { + if (ClassUtil.isAssignable(entry.getKey(), valueClass)) { + enumResult = ReflectUtil.invokeStatic(entry.getValue(), value); + } + } + } + //oriInt 应该滞后使用 以 GB/T 2261.1-2003 性别编码为例,对应整数并非连续数字会导致数字转枚举时失败 + //0 - 未知的性别 + //1 - 男性 + //2 - 女性 + //5 - 女性改(变)为男性 + //6 - 男性改(变)为女性 + //9 - 未说明的性别 + if(null == enumResult){ + if (value instanceof Integer) { + enumResult = EnumUtil.getEnumAt(enumClass, (Integer)value); + } else if (value instanceof String) { + try { + enumResult = Enum.valueOf(enumClass, (String) value); + } catch (IllegalArgumentException e) { + //ignore } } } - return enumResult; } diff --git a/hutool-core/src/main/java/cn/hutool/core/util/ServiceLoaderUtil.java b/hutool-core/src/main/java/cn/hutool/core/util/ServiceLoaderUtil.java index 21086f5f9..4fd3360bb 100644 --- a/hutool-core/src/main/java/cn/hutool/core/util/ServiceLoaderUtil.java +++ b/hutool-core/src/main/java/cn/hutool/core/util/ServiceLoaderUtil.java @@ -1,6 +1,8 @@ package cn.hutool.core.util; +import java.util.ArrayList; import java.util.Iterator; +import java.util.List; import java.util.ServiceConfigurationError; import java.util.ServiceLoader; @@ -61,7 +63,7 @@ public class ServiceLoaderUtil { * @return 服务接口实现列表 */ public static ServiceLoader load(Class clazz) { - return ServiceLoader.load(clazz); + return load(clazz,null); } /** @@ -73,6 +75,37 @@ public class ServiceLoaderUtil { * @return 服务接口实现列表 */ public static ServiceLoader load(Class clazz, ClassLoader loader) { + if(loader==null) { + loader = Thread.currentThread().getContextClassLoader(); + } return ServiceLoader.load(clazz, loader); } + /** + * 加载服务 并已list列表返回 + * @param 接口类型 + * @param clazz 服务接口 + * @return 服务接口实现列表 + */ + public static List loadList(Class clazz){ + return loadList(clazz); + } + /** + * 加载服务 并已list列表返回 + * @param 接口类型 + * @param clazz 服务接口 + * @param loader {@link ClassLoader} + * @return 服务接口实现列表 + */ + public static List loadList(Class clazz, ClassLoader loader){ + final Iterator iterator = load(clazz).iterator(); + List list=new ArrayList<>(); + while(iterator.hasNext()){ + try { + list.add(iterator.next()); + } catch (ServiceConfigurationError e) { + // ignore + } + } + return list; + } } diff --git a/hutool-core/src/main/java/cn/hutool/core/util/XmlUtil.java b/hutool-core/src/main/java/cn/hutool/core/util/XmlUtil.java index 366f81763..4369ebe94 100644 --- a/hutool-core/src/main/java/cn/hutool/core/util/XmlUtil.java +++ b/hutool-core/src/main/java/cn/hutool/core/util/XmlUtil.java @@ -1338,7 +1338,7 @@ public class XmlUtil { } @Override - public Iterator getPrefixes(String namespaceURI) { + public Iterator getPrefixes(String namespaceURI) { // Not implemented return null; } diff --git a/hutool-poi/src/main/java/cn/hutool/poi/excel/ExcelUtil.java b/hutool-poi/src/main/java/cn/hutool/poi/excel/ExcelUtil.java index 3d814749f..1fd7a470b 100644 --- a/hutool-poi/src/main/java/cn/hutool/poi/excel/ExcelUtil.java +++ b/hutool-poi/src/main/java/cn/hutool/poi/excel/ExcelUtil.java @@ -17,7 +17,7 @@ import java.io.File; import java.io.InputStream; /** - * Excel工具类 + * Excel工具类,不建议直接使用index直接操作sheet,在wps/excel中sheet显示顺序与index无关,还有隐藏sheet * * @author Looly *