浏览代码

!173 枚举转换规则拓展
Merge pull request !173 from nierjia_hzd/v5-dev

Looly 5 年之前
父节点
当前提交
10878559fc

+ 19 - 0
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<Converter> list = ServiceLoaderUtil.loadList(Converter.class);
+		list.forEach(converter->{
+			try {
+				Type type = TypeUtil.getTypeArgument(ClassUtil.getClass(converter));
+				putCustom(type, converter);
+			} catch (Exception e) {
+				 // 忽略注册失败的
+			}
+		});
 	}
 
 	/**

+ 64 - 0
hutool-core/src/main/java/cn/hutool/core/convert/EnumItem.java

@@ -0,0 +1,64 @@
+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;
+	 }
+
+	
+}
+

+ 39 - 20
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<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) {
+			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<Class<?>, Method> valueOfMethods = getValueOfMethods(enumClass);
-			if (MapUtil.isNotEmpty(valueOfMethods)) {
-				final Class<?> valueClass = value.getClass();
-				for (Map.Entry<Class<?>, Method> entry : valueOfMethods.entrySet()) {
-					if (ClassUtil.isAssignable(entry.getKey(), valueClass)) {
-						enumResult = ReflectUtil.invokeStatic(entry.getValue(), value);
-					}
+		// 用户自定义方法优先,
+		final Map<Class<?>, Method> valueOfMethods = getValueOfMethods(enumClass);
+		if (MapUtil.isNotEmpty(valueOfMethods)) {
+			final Class<?> valueClass = value.getClass();
+			for (Map.Entry<Class<?>, 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;
 	}
 

+ 34 - 1
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 <T> ServiceLoader<T> load(Class<T> clazz) {
-		return ServiceLoader.load(clazz);
+		return load(clazz,null);
 	}
 
 	/**
@@ -73,6 +75,37 @@ 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 clazz 服务接口
+	 * @return 服务接口实现列表
+	 */
+	public static <T> List<T> loadList(Class<T> clazz){
+		return loadList(clazz);
+	}
+	/**
+	 * 加载服务 并已list列表返回
+	 * @param <T> 接口类型
+	 * @param clazz 服务接口
+	 * @param loader {@link ClassLoader}
+	 * @return 服务接口实现列表
+	 */
+	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;
+	}
 }

+ 1 - 1
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<String> getPrefixes(String namespaceURI) {
 			// Not implemented
 			return null;
 		}

+ 1 - 1
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
  *