mirror of
https://gitee.com/chinabugotech/hutool.git
synced 2025-04-19 03:01:48 +08:00
Prepare release
This commit is contained in:
commit
3715024abd
16
CHANGELOG.md
16
CHANGELOG.md
@ -3,6 +3,22 @@
|
||||
|
||||
-------------------------------------------------------------------------------------------------------------
|
||||
|
||||
# 5.7.20 (2022-01-14)
|
||||
|
||||
### 🐣新特性
|
||||
* 【core 】 增加对null值友好的groupingBy操作的Collector实现,可指定map类型(pr#498@Gitee)
|
||||
* 【core 】 增加KetamaHash(issue#2084@Github)
|
||||
* 【crypto 】 增加SignUtil
|
||||
* 【json 】 JSONGetter增加getBeanList方法
|
||||
* 【core 】 ObjectUtil 添加三个defaultIfXxxx方法,用于节省CPU及内存损耗。(pr#2094@Github)
|
||||
*
|
||||
### 🐞Bug修复
|
||||
* 【core 】 修复setter重载导致匹配错误(issue#2082@Github)
|
||||
* 【core 】 修复RegexPool汉字匹配范围小问题(pr#2081@Github)
|
||||
* 【core 】 修复OS中的拼写错误(pr#500@Gitee)
|
||||
* 【core 】 修复CustomKeyMap的merge失效问题(issue#2086@Github)
|
||||
|
||||
-------------------------------------------------------------------------------------------------------------
|
||||
# 5.7.19 (2022-01-07)
|
||||
|
||||
### 🐣新特性
|
||||
|
@ -142,18 +142,18 @@ We provide the T-Shirt and Sweater with Hutool Logo, please visit the shop:
|
||||
<dependency>
|
||||
<groupId>cn.hutool</groupId>
|
||||
<artifactId>hutool-all</artifactId>
|
||||
<version>5.7.19</version>
|
||||
<version>5.7.20</version>
|
||||
</dependency>
|
||||
```
|
||||
|
||||
### 🍐Gradle
|
||||
```
|
||||
implementation 'cn.hutool:hutool-all:5.7.19'
|
||||
implementation 'cn.hutool:hutool-all:5.7.20'
|
||||
```
|
||||
|
||||
## 📥Download
|
||||
|
||||
- [Maven Repo](https://repo1.maven.org/maven2/cn/hutool/hutool-all/5.7.19/)
|
||||
- [Maven Repo](https://repo1.maven.org/maven2/cn/hutool/hutool-all/5.7.20/)
|
||||
|
||||
> 🔔️note:
|
||||
> Hutool 5.x supports JDK8+ and is not tested on Android platforms, and cannot guarantee that all tool classes or tool methods are available.
|
||||
|
@ -142,20 +142,20 @@ Hutool的存在就是为了减少代码搜索成本,避免网络上参差不
|
||||
<dependency>
|
||||
<groupId>cn.hutool</groupId>
|
||||
<artifactId>hutool-all</artifactId>
|
||||
<version>5.7.19</version>
|
||||
<version>5.7.20</version>
|
||||
</dependency>
|
||||
```
|
||||
|
||||
### 🍐Gradle
|
||||
```
|
||||
implementation 'cn.hutool:hutool-all:5.7.19'
|
||||
implementation 'cn.hutool:hutool-all:5.7.20'
|
||||
```
|
||||
|
||||
### 📥下载jar
|
||||
|
||||
点击以下链接,下载`hutool-all-X.X.X.jar`即可:
|
||||
|
||||
- [Maven中央库](https://repo1.maven.org/maven2/cn/hutool/hutool-all/5.7.19/)
|
||||
- [Maven中央库](https://repo1.maven.org/maven2/cn/hutool/hutool-all/5.7.20/)
|
||||
|
||||
> 🔔️注意
|
||||
> Hutool 5.x支持JDK8+,对Android平台没有测试,不能保证所有工具类或工具方法可用。
|
||||
|
@ -1 +1 @@
|
||||
5.7.19
|
||||
5.7.20
|
||||
|
@ -1 +1 @@
|
||||
var version = '5.7.19'
|
||||
var version = '5.7.20'
|
@ -9,7 +9,7 @@
|
||||
<parent>
|
||||
<groupId>cn.hutool</groupId>
|
||||
<artifactId>hutool-parent</artifactId>
|
||||
<version>5.7.19</version>
|
||||
<version>5.7.20-SNAPSHOT</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>hutool-all</artifactId>
|
||||
|
@ -9,7 +9,7 @@
|
||||
<parent>
|
||||
<groupId>cn.hutool</groupId>
|
||||
<artifactId>hutool-parent</artifactId>
|
||||
<version>5.7.19</version>
|
||||
<version>5.7.20-SNAPSHOT</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>hutool-aop</artifactId>
|
||||
|
@ -9,7 +9,7 @@
|
||||
<parent>
|
||||
<groupId>cn.hutool</groupId>
|
||||
<artifactId>hutool-parent</artifactId>
|
||||
<version>5.7.19</version>
|
||||
<version>5.7.20-SNAPSHOT</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>hutool-bloomFilter</artifactId>
|
||||
|
@ -9,7 +9,7 @@
|
||||
<parent>
|
||||
<groupId>cn.hutool</groupId>
|
||||
<artifactId>hutool-parent</artifactId>
|
||||
<version>5.7.19</version>
|
||||
<version>5.7.20-SNAPSHOT</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>hutool-bom</artifactId>
|
||||
|
@ -9,7 +9,7 @@
|
||||
<parent>
|
||||
<groupId>cn.hutool</groupId>
|
||||
<artifactId>hutool-parent</artifactId>
|
||||
<version>5.7.19</version>
|
||||
<version>5.7.20-SNAPSHOT</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>hutool-cache</artifactId>
|
||||
|
@ -9,7 +9,7 @@
|
||||
<parent>
|
||||
<groupId>cn.hutool</groupId>
|
||||
<artifactId>hutool-parent</artifactId>
|
||||
<version>5.7.19</version>
|
||||
<version>5.7.20-SNAPSHOT</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>hutool-captcha</artifactId>
|
||||
|
@ -9,7 +9,7 @@
|
||||
<parent>
|
||||
<groupId>cn.hutool</groupId>
|
||||
<artifactId>hutool-parent</artifactId>
|
||||
<version>5.7.19</version>
|
||||
<version>5.7.20-SNAPSHOT</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>hutool-core</artifactId>
|
||||
|
@ -140,14 +140,12 @@ public class BeanDesc implements Serializable {
|
||||
* @return this
|
||||
*/
|
||||
private BeanDesc init() {
|
||||
final Method[] methods = ReflectUtil.getMethods(this.beanClass);
|
||||
final Method[] gettersAndSetters = ReflectUtil.getMethods(this.beanClass, ReflectUtil::isGetterOrSetterIgnoreCase);
|
||||
PropDesc prop;
|
||||
for (Field field : ReflectUtil.getFields(this.beanClass)) {
|
||||
if (false == ModifierUtil.isStatic(field) &&
|
||||
// 排除对象子类
|
||||
false == "this$0".equals(field.getName())) {
|
||||
//只针对非static属性
|
||||
prop = createProp(field, methods);
|
||||
// 排除静态属性和对象子类
|
||||
if (false == ModifierUtil.isStatic(field) && false == ReflectUtil.isOuterClassField(field)) {
|
||||
prop = createProp(field, gettersAndSetters);
|
||||
// 只有不存在时才放入,防止父类属性覆盖子类属性
|
||||
this.propMap.putIfAbsent(prop.getFieldName(), prop);
|
||||
}
|
||||
@ -190,12 +188,12 @@ public class BeanDesc implements Serializable {
|
||||
/**
|
||||
* 查找字段对应的Getter和Setter方法
|
||||
*
|
||||
* @param field 字段
|
||||
* @param methods 类中所有的方法
|
||||
* @param ignoreCase 是否忽略大小写匹配
|
||||
* @param field 字段
|
||||
* @param gettersOrSetters 类中所有的Getter或Setter方法
|
||||
* @param ignoreCase 是否忽略大小写匹配
|
||||
* @return PropDesc
|
||||
*/
|
||||
private PropDesc findProp(Field field, Method[] methods, boolean ignoreCase) {
|
||||
private PropDesc findProp(Field field, Method[] gettersOrSetters, boolean ignoreCase) {
|
||||
final String fieldName = field.getName();
|
||||
final Class<?> fieldType = field.getType();
|
||||
final boolean isBooleanField = BooleanUtil.isBoolean(fieldType);
|
||||
@ -203,24 +201,19 @@ public class BeanDesc implements Serializable {
|
||||
Method getter = null;
|
||||
Method setter = null;
|
||||
String methodName;
|
||||
Class<?>[] parameterTypes;
|
||||
for (Method method : methods) {
|
||||
parameterTypes = method.getParameterTypes();
|
||||
if (parameterTypes.length > 1) {
|
||||
// 多于1个参数说明非Getter或Setter
|
||||
continue;
|
||||
}
|
||||
|
||||
for (Method method : gettersOrSetters) {
|
||||
methodName = method.getName();
|
||||
if (parameterTypes.length == 0) {
|
||||
if (method.getParameterCount() == 0) {
|
||||
// 无参数,可能为Getter方法
|
||||
if (isMatchGetter(methodName, fieldName, isBooleanField, ignoreCase)) {
|
||||
// 方法名与字段名匹配,则为Getter方法
|
||||
getter = method;
|
||||
}
|
||||
} else if (isMatchSetter(methodName, fieldName, isBooleanField, ignoreCase)) {
|
||||
// 只有一个参数的情况下方法名与字段名对应匹配,则为Setter方法
|
||||
setter = method;
|
||||
// setter方法的参数类型和字段类型必须一致,或参数类型是字段类型的子类
|
||||
if(fieldType.isAssignableFrom(method.getParameterTypes()[0])){
|
||||
setter = method;
|
||||
}
|
||||
}
|
||||
if (null != getter && null != setter) {
|
||||
// 如果Getter和Setter方法都找到了,不再继续寻找
|
||||
@ -261,15 +254,6 @@ public class BeanDesc implements Serializable {
|
||||
handledFieldName = StrUtil.upperFirst(fieldName);
|
||||
}
|
||||
|
||||
if (false == methodName.startsWith("get") && false == methodName.startsWith("is")) {
|
||||
// 非标准Getter方法
|
||||
return false;
|
||||
}
|
||||
if ("getclass".equals(methodName)) {
|
||||
//跳过getClass方法
|
||||
return false;
|
||||
}
|
||||
|
||||
// 针对Boolean类型特殊检查
|
||||
if (isBooleanField) {
|
||||
if (fieldName.startsWith("is")) {
|
||||
|
@ -88,9 +88,8 @@ public class BeanUtil {
|
||||
*/
|
||||
public static boolean hasSetter(Class<?> clazz) {
|
||||
if (ClassUtil.isNormalClass(clazz)) {
|
||||
final Method[] methods = clazz.getMethods();
|
||||
for (Method method : methods) {
|
||||
if (method.getParameterTypes().length == 1 && method.getName().startsWith("set")) {
|
||||
for (Method method : clazz.getMethods()) {
|
||||
if (method.getParameterCount() == 1 && method.getName().startsWith("set")) {
|
||||
// 检测包含标准的setXXX方法即视为标准的JavaBean
|
||||
return true;
|
||||
}
|
||||
@ -110,7 +109,7 @@ public class BeanUtil {
|
||||
public static boolean hasGetter(Class<?> clazz) {
|
||||
if (ClassUtil.isNormalClass(clazz)) {
|
||||
for (Method method : clazz.getMethods()) {
|
||||
if (method.getParameterTypes().length == 0) {
|
||||
if (method.getParameterCount() == 0) {
|
||||
if (method.getName().startsWith("get") || method.getName().startsWith("is")) {
|
||||
return true;
|
||||
}
|
||||
@ -739,7 +738,7 @@ public class BeanUtil {
|
||||
* @param copyOptions 拷贝选项,见 {@link CopyOptions}
|
||||
*/
|
||||
public static void copyProperties(Object source, Object target, CopyOptions copyOptions) {
|
||||
BeanCopier.create(source, target, ObjectUtil.defaultIfNull(copyOptions, CopyOptions.create())).copy();
|
||||
BeanCopier.create(source, target, ObjectUtil.defaultIfNull(copyOptions, CopyOptions::create)).copy();
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -3,6 +3,7 @@ package cn.hutool.core.collection;
|
||||
|
||||
import cn.hutool.core.lang.Opt;
|
||||
import cn.hutool.core.map.MapUtil;
|
||||
import cn.hutool.core.stream.CollectorUtil;
|
||||
import cn.hutool.core.stream.StreamUtil;
|
||||
|
||||
import java.util.Collection;
|
||||
@ -161,7 +162,7 @@ public class CollStreamUtil {
|
||||
if (CollUtil.isEmpty(collection)) {
|
||||
return Collections.emptyMap();
|
||||
}
|
||||
return groupBy(collection, key1, Collectors.groupingBy(key2, Collectors.toList()), isParallel);
|
||||
return groupBy(collection, key1, CollectorUtil.groupingBy(key2, Collectors.toList()), isParallel);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -236,7 +237,7 @@ public class CollStreamUtil {
|
||||
if (CollUtil.isEmpty(collection)) {
|
||||
return Collections.emptyMap();
|
||||
}
|
||||
return groupBy(collection, key, Collectors.mapping(value, Collectors.toList()), isParallel);
|
||||
return groupBy(collection, key, Collectors.mapping(v -> Opt.ofNullable(v).map(value).orElse(null), Collectors.toList()), isParallel);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -276,7 +277,7 @@ public class CollStreamUtil {
|
||||
if (CollUtil.isEmpty(collection)) {
|
||||
return Collections.emptyMap();
|
||||
}
|
||||
return StreamUtil.of(collection, isParallel).collect(Collectors.groupingBy(key, downstream));
|
||||
return StreamUtil.of(collection, isParallel).collect(CollectorUtil.groupingBy(key, downstream));
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -44,7 +44,7 @@ class JavaClassFileManager extends ForwardingJavaFileManager<JavaFileManager> {
|
||||
*/
|
||||
protected JavaClassFileManager(ClassLoader parent, JavaFileManager fileManager) {
|
||||
super(fileManager);
|
||||
this.parent = ObjectUtil.defaultIfNull(parent, ClassLoaderUtil.getClassLoader());
|
||||
this.parent = ObjectUtil.defaultIfNull(parent, ClassLoaderUtil::getClassLoader);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -89,7 +89,7 @@ public class JavaSourceCompiler {
|
||||
* @param parent 父类加载器,null则使用默认类加载器
|
||||
*/
|
||||
private JavaSourceCompiler(ClassLoader parent) {
|
||||
this.parentClassLoader = ObjectUtil.defaultIfNull(parent, ClassLoaderUtil.getClassLoader());
|
||||
this.parentClassLoader = ObjectUtil.defaultIfNull(parent, ClassLoaderUtil::getClassLoader);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -227,7 +227,7 @@ public class TemporalAccessorConverter extends AbstractConverter<TemporalAccesso
|
||||
return instant;
|
||||
}
|
||||
|
||||
zoneId = ObjectUtil.defaultIfNull(zoneId, ZoneId.systemDefault());
|
||||
zoneId = ObjectUtil.defaultIfNull(zoneId, ZoneId::systemDefault);
|
||||
|
||||
TemporalAccessor result = null;
|
||||
if (LocalDateTime.class.equals(this.targetType)) {
|
||||
|
@ -143,7 +143,7 @@ public class DateTime extends Date {
|
||||
* @since 4.1.2
|
||||
*/
|
||||
public DateTime(Date date, TimeZone timeZone) {
|
||||
this(ObjectUtil.defaultIfNull(date, new Date()).getTime(), timeZone);
|
||||
this(ObjectUtil.defaultIfNull(date, Date::new).getTime(), timeZone);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -216,7 +216,7 @@ public class DateTime extends Date {
|
||||
*/
|
||||
public DateTime(long timeMillis, TimeZone timeZone) {
|
||||
super(timeMillis);
|
||||
this.timeZone = ObjectUtil.defaultIfNull(timeZone, TimeZone.getDefault());
|
||||
this.timeZone = ObjectUtil.defaultIfNull(timeZone, TimeZone::getDefault);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -913,7 +913,7 @@ public class DateTime extends Date {
|
||||
* @since 4.1.2
|
||||
*/
|
||||
public DateTime setTimeZone(TimeZone timeZone) {
|
||||
this.timeZone = ObjectUtil.defaultIfNull(timeZone, TimeZone.getDefault());
|
||||
this.timeZone = ObjectUtil.defaultIfNull(timeZone, TimeZone::getDefault);
|
||||
return this;
|
||||
}
|
||||
|
||||
|
@ -88,7 +88,7 @@ public class LocalDateTimeUtil {
|
||||
return null;
|
||||
}
|
||||
|
||||
return LocalDateTime.ofInstant(instant, ObjectUtil.defaultIfNull(zoneId, ZoneId.systemDefault()));
|
||||
return LocalDateTime.ofInstant(instant, ObjectUtil.defaultIfNull(zoneId, ZoneId::systemDefault));
|
||||
}
|
||||
|
||||
/**
|
||||
@ -103,7 +103,7 @@ public class LocalDateTimeUtil {
|
||||
return null;
|
||||
}
|
||||
|
||||
return of(instant, ObjectUtil.defaultIfNull(timeZone, TimeZone.getDefault()).toZoneId());
|
||||
return of(instant, ObjectUtil.defaultIfNull(timeZone, TimeZone::getDefault).toZoneId());
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -117,7 +117,7 @@ public class FastByteArrayOutputStream extends OutputStream {
|
||||
*/
|
||||
public String toString(Charset charset) {
|
||||
return new String(toByteArray(),
|
||||
ObjectUtil.defaultIfNull(charset, CharsetUtil.defaultCharset()));
|
||||
ObjectUtil.defaultIfNull(charset, CharsetUtil::defaultCharset));
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -16,7 +16,6 @@ import java.io.InputStream;
|
||||
import java.io.OutputStreamWriter;
|
||||
import java.io.PrintWriter;
|
||||
import java.nio.charset.Charset;
|
||||
import java.util.Collection;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
|
||||
@ -165,7 +164,7 @@ public class FileWriter extends FileWrapper {
|
||||
* @return 目标文件
|
||||
* @throws IORuntimeException IO异常
|
||||
*/
|
||||
public <T> File writeLines(Collection<T> list) throws IORuntimeException {
|
||||
public <T> File writeLines(Iterable<T> list) throws IORuntimeException {
|
||||
return writeLines(list, false);
|
||||
}
|
||||
|
||||
@ -177,7 +176,7 @@ public class FileWriter extends FileWrapper {
|
||||
* @return 目标文件
|
||||
* @throws IORuntimeException IO异常
|
||||
*/
|
||||
public <T> File appendLines(Collection<T> list) throws IORuntimeException {
|
||||
public <T> File appendLines(Iterable<T> list) throws IORuntimeException {
|
||||
return writeLines(list, true);
|
||||
}
|
||||
|
||||
@ -190,7 +189,7 @@ public class FileWriter extends FileWrapper {
|
||||
* @return 目标文件
|
||||
* @throws IORuntimeException IO异常
|
||||
*/
|
||||
public <T> File writeLines(Collection<T> list, boolean isAppend) throws IORuntimeException {
|
||||
public <T> File writeLines(Iterable<T> list, boolean isAppend) throws IORuntimeException {
|
||||
return writeLines(list, null, isAppend);
|
||||
}
|
||||
|
||||
@ -205,12 +204,22 @@ public class FileWriter extends FileWrapper {
|
||||
* @throws IORuntimeException IO异常
|
||||
* @since 3.1.0
|
||||
*/
|
||||
public <T> File writeLines(Collection<T> list, LineSeparator lineSeparator, boolean isAppend) throws IORuntimeException {
|
||||
public <T> File writeLines(Iterable<T> list, LineSeparator lineSeparator, boolean isAppend) throws IORuntimeException {
|
||||
try (PrintWriter writer = getPrintWriter(isAppend)) {
|
||||
boolean isFirst = true;
|
||||
for (T t : list) {
|
||||
if (null != t) {
|
||||
if(isFirst){
|
||||
isFirst = false;
|
||||
if(isAppend && FileUtil.isNotEmpty(this.file)){
|
||||
// 追加模式下且文件非空,补充换行符
|
||||
printNewLine(writer, lineSeparator);
|
||||
}
|
||||
} else{
|
||||
printNewLine(writer, lineSeparator);
|
||||
}
|
||||
writer.print(t);
|
||||
printNewLine(writer, lineSeparator);
|
||||
|
||||
writer.flush();
|
||||
}
|
||||
}
|
||||
|
@ -69,7 +69,7 @@ public class ClassPathResource extends UrlResource {
|
||||
this.path = path;
|
||||
this.name = StrUtil.isBlank(path) ? null : FileUtil.getName(path);
|
||||
|
||||
this.classLoader = ObjectUtil.defaultIfNull(classLoader, ClassUtil.getClassLoader());
|
||||
this.classLoader = ObjectUtil.defaultIfNull(classLoader, ClassUtil::getClassLoader);
|
||||
this.clazz = clazz;
|
||||
initUrl();
|
||||
}
|
||||
|
@ -60,7 +60,7 @@ public class FileResource implements Resource, Serializable {
|
||||
public FileResource(File file, String fileName) {
|
||||
Assert.notNull(file, "File must be not null !");
|
||||
this.file = file;
|
||||
this.name = ObjectUtil.defaultIfNull(fileName, file.getName());
|
||||
this.name = ObjectUtil.defaultIfNull(fileName, file::getName);
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------- Constructor end
|
||||
|
@ -36,7 +36,7 @@ public class UrlResource implements Resource, Serializable{
|
||||
*/
|
||||
public UrlResource(URL url, String name) {
|
||||
this.url = url;
|
||||
this.name = ObjectUtil.defaultIfNull(name, (null != url) ? FileUtil.getName(url.getPath()) : null);
|
||||
this.name = ObjectUtil.defaultIfNull(name, () -> (null != url ? FileUtil.getName(url.getPath()) : null));
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -20,9 +20,10 @@ public interface RegexPool {
|
||||
*/
|
||||
String WORD = "[a-zA-Z]+";
|
||||
/**
|
||||
* 单个中文汉字
|
||||
* 单个中文汉字<br>
|
||||
* 参照维基百科汉字Unicode范围(https://zh.wikipedia.org/wiki/%E6%B1%89%E5%AD%97 页面右侧)
|
||||
*/
|
||||
String CHINESE = "[\u4E00-\u9FFF]";
|
||||
String CHINESE = "[\u2E80-\u2EFF\u2F00-\u2FDF\u31C0-\u31EF\u3400-\u4DBF\u4E00-\u9FFF\uF900-\uFAFF\uD840\uDC00-\uD869\uDEDF\uD869\uDF00-\uD86D\uDF3F\uD86D\uDF40-\uD86E\uDC1F\uD86E\uDC20-\uD873\uDEAF\uD87E\uDC00-\uD87E\uDE1F]";
|
||||
/**
|
||||
* 中文汉字
|
||||
*/
|
||||
|
@ -30,8 +30,8 @@ public class ResourceClassLoader<T extends Resource> extends SecureClassLoader {
|
||||
* @param resourceMap 资源map
|
||||
*/
|
||||
public ResourceClassLoader(ClassLoader parentClassLoader, Map<String, T> resourceMap) {
|
||||
super(ObjectUtil.defaultIfNull(parentClassLoader, ClassLoaderUtil.getClassLoader()));
|
||||
this.resourceMap = ObjectUtil.defaultIfNull(resourceMap, new HashMap<>());
|
||||
super(ObjectUtil.defaultIfNull(parentClassLoader, ClassLoaderUtil::getClassLoader));
|
||||
this.resourceMap = ObjectUtil.defaultIfNull(resourceMap, HashMap::new);
|
||||
this.cacheClassMap = new HashMap<>();
|
||||
}
|
||||
|
||||
|
51
hutool-core/src/main/java/cn/hutool/core/lang/hash/KetamaHash.java
Executable file
51
hutool-core/src/main/java/cn/hutool/core/lang/hash/KetamaHash.java
Executable file
@ -0,0 +1,51 @@
|
||||
package cn.hutool.core.lang.hash;
|
||||
|
||||
import cn.hutool.core.exceptions.UtilException;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
|
||||
import java.security.MessageDigest;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
|
||||
/**
|
||||
* Ketama算法,用于在一致性Hash中快速定位服务器位置
|
||||
*
|
||||
* @author looly
|
||||
* @since 5.7.20
|
||||
*/
|
||||
public class KetamaHash implements Hash64<String>, Hash32<String> {
|
||||
|
||||
@Override
|
||||
public long hash64(String key) {
|
||||
byte[] bKey = md5(key);
|
||||
return ((long) (bKey[3] & 0xFF) << 24)
|
||||
| ((long) (bKey[2] & 0xFF) << 16)
|
||||
| ((long) (bKey[1] & 0xFF) << 8)
|
||||
| (bKey[0] & 0xFF);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hash32(String key) {
|
||||
return (int) (hash64(key) & 0xffffffffL);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Number hash(String key) {
|
||||
return hash64(key);
|
||||
}
|
||||
|
||||
/**
|
||||
* 计算MD5值,使用UTF-8编码
|
||||
*
|
||||
* @param key 被计算的键
|
||||
* @return MD5值
|
||||
*/
|
||||
private static byte[] md5(String key) {
|
||||
final MessageDigest md5;
|
||||
try {
|
||||
md5 = MessageDigest.getInstance("MD5");
|
||||
} catch (NoSuchAlgorithmException e) {
|
||||
throw new UtilException("MD5 algorithm not suooport!", e);
|
||||
}
|
||||
return md5.digest(StrUtil.utf8Bytes(key));
|
||||
}
|
||||
}
|
@ -12,6 +12,7 @@ import java.lang.reflect.Method;
|
||||
|
||||
/**
|
||||
* 方法句柄{@link MethodHandle}封装工具类<br>
|
||||
* 方法句柄是一个有类型的,可以直接执行的指向底层方法、构造器、field等的引用,可以简单理解为函数指针,它是一种更加底层的查找、调整和调用方法的机制。
|
||||
* 参考:
|
||||
* <ul>
|
||||
* <li>https://stackoverflow.com/questions/22614746/how-do-i-invoke-java-8-default-methods-reflectively</li>
|
||||
@ -113,7 +114,7 @@ public class MethodHandleUtil {
|
||||
}
|
||||
|
||||
/**
|
||||
* 执行接口或对象中的方法<br>
|
||||
* 执行接口或对象中的特殊方法(private、static等)<br>
|
||||
*
|
||||
* <pre class="code">
|
||||
* interface Duck {
|
||||
@ -159,7 +160,7 @@ public class MethodHandleUtil {
|
||||
}
|
||||
|
||||
/**
|
||||
* 执行接口或对象中的方法<br>
|
||||
* 执行接口或对象中的特殊方法(private、static等)<br>
|
||||
*
|
||||
* <pre class="code">
|
||||
* interface Duck {
|
||||
|
@ -1,6 +1,7 @@
|
||||
package cn.hutool.core.map;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.function.BiFunction;
|
||||
|
||||
/**
|
||||
* 自定义键的Map,默认HashMap实现
|
||||
@ -67,6 +68,31 @@ public abstract class CustomKeyMap<K, V> extends MapWrapper<K, V> {
|
||||
return super.replace((K) customKey(key), value);
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------- Override default methods start
|
||||
@Override
|
||||
public V getOrDefault(Object key, V defaultValue) {
|
||||
return super.getOrDefault(customKey(key), defaultValue);
|
||||
}
|
||||
|
||||
@Override
|
||||
public V computeIfPresent(K key, BiFunction<? super K, ? super V, ? extends V> remappingFunction) {
|
||||
//noinspection unchecked
|
||||
return super.computeIfPresent((K) customKey(key), remappingFunction);
|
||||
}
|
||||
|
||||
@Override
|
||||
public V compute(K key, BiFunction<? super K, ? super V, ? extends V> remappingFunction) {
|
||||
//noinspection unchecked
|
||||
return super.compute((K) customKey(key), remappingFunction);
|
||||
}
|
||||
|
||||
@Override
|
||||
public V merge(K key, V value, BiFunction<? super V, ? super V, ? extends V> remappingFunction) {
|
||||
//noinspection unchecked
|
||||
return super.merge((K) customKey(key), value, remappingFunction);
|
||||
}
|
||||
//---------------------------------------------------------------------------- Override default methods end
|
||||
|
||||
/**
|
||||
* 自定义键
|
||||
*
|
||||
|
@ -16,7 +16,6 @@ import java.util.function.Function;
|
||||
* @param <K> 键类型
|
||||
* @param <V> 值类型
|
||||
* @author looly
|
||||
* @author looly
|
||||
* @since 4.3.3
|
||||
*/
|
||||
public class MapWrapper<K, V> implements Map<K, V>, Iterable<Map.Entry<K, V>>, Serializable, Cloneable {
|
||||
@ -179,6 +178,7 @@ public class MapWrapper<K, V> implements Map<K, V>, Iterable<Map.Entry<K, V>>, S
|
||||
return raw.computeIfAbsent(key, mappingFunction);
|
||||
}
|
||||
|
||||
// 重写默认方法的意义在于,如果被包装的Map自定义了这些默认方法,包装类就可以保持这些行为的一致性
|
||||
//---------------------------------------------------------------------------- Override default methods start
|
||||
@Override
|
||||
public V getOrDefault(Object key, V defaultValue) {
|
||||
|
@ -1,10 +1,17 @@
|
||||
package cn.hutool.core.stream;
|
||||
|
||||
import cn.hutool.core.lang.Opt;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.EnumSet;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.StringJoiner;
|
||||
import java.util.function.BiConsumer;
|
||||
import java.util.function.BinaryOperator;
|
||||
import java.util.function.Function;
|
||||
import java.util.function.Supplier;
|
||||
import java.util.stream.Collector;
|
||||
|
||||
/**
|
||||
@ -61,4 +68,70 @@ public class CollectorUtil {
|
||||
Collections.emptySet()
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 提供对null值友好的groupingBy操作的{@link Collector}实现,可指定map类型
|
||||
*
|
||||
* @param classifier 分组依据
|
||||
* @param mapFactory 提供的map
|
||||
* @param downstream 下游操作
|
||||
* @param <T> 实体类型
|
||||
* @param <K> 实体中的分组依据对应类型,也是Map中key的类型
|
||||
* @param <D> 下游操作对应返回类型,也是Map中value的类型
|
||||
* @param <A> 下游操作在进行中间操作时对应类型
|
||||
* @param <M> 最后返回结果Map类型
|
||||
* @return {@link Collector}
|
||||
*/
|
||||
public static <T, K, D, A, M extends Map<K, D>> Collector<T, ?, M> groupingBy(Function<? super T, ? extends K> classifier,
|
||||
Supplier<M> mapFactory,
|
||||
Collector<? super T, A, D> downstream) {
|
||||
Supplier<A> downstreamSupplier = downstream.supplier();
|
||||
BiConsumer<A, ? super T> downstreamAccumulator = downstream.accumulator();
|
||||
BiConsumer<Map<K, A>, T> accumulator = (m, t) -> {
|
||||
K key = Opt.ofNullable(t).map(classifier).orElse(null);
|
||||
A container = m.computeIfAbsent(key, k -> downstreamSupplier.get());
|
||||
downstreamAccumulator.accept(container, t);
|
||||
};
|
||||
BinaryOperator<Map<K, A>> merger = (m1, m2) -> {
|
||||
for (Map.Entry<K, A> e : m2.entrySet()) {
|
||||
m1.merge(e.getKey(), e.getValue(), downstream.combiner());
|
||||
}
|
||||
return m1;
|
||||
};
|
||||
@SuppressWarnings("unchecked")
|
||||
Supplier<Map<K, A>> mangledFactory = (Supplier<Map<K, A>>) mapFactory;
|
||||
|
||||
if (downstream.characteristics().contains(Collector.Characteristics.IDENTITY_FINISH)) {
|
||||
return new SimpleCollector<>(mangledFactory, accumulator, merger, Collections.unmodifiableSet(EnumSet.of(Collector.Characteristics.IDENTITY_FINISH)));
|
||||
} else {
|
||||
@SuppressWarnings("unchecked")
|
||||
Function<A, A> downstreamFinisher = (Function<A, A>) downstream.finisher();
|
||||
Function<Map<K, A>, M> finisher = intermediate -> {
|
||||
intermediate.replaceAll((k, v) -> downstreamFinisher.apply(v));
|
||||
@SuppressWarnings("unchecked")
|
||||
M castResult = (M) intermediate;
|
||||
return castResult;
|
||||
};
|
||||
return new SimpleCollector<>(mangledFactory, accumulator, merger, finisher, Collections.emptySet());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 提供对null值友好的groupingBy操作的{@link Collector}实现
|
||||
*
|
||||
* @param classifier 分组依据
|
||||
* @param downstream 下游操作
|
||||
* @param <T> 实体类型
|
||||
* @param <K> 实体中的分组依据对应类型,也是Map中key的类型
|
||||
* @param <D> 下游操作对应返回类型,也是Map中value的类型
|
||||
* @param <A> 下游操作在进行中间操作时对应类型
|
||||
* @return {@link Collector}
|
||||
*/
|
||||
public static <T, K, A, D>
|
||||
Collector<T, ?, Map<K, D>> groupingBy(Function<? super T, ? extends K> classifier,
|
||||
Collector<? super T, A, D> downstream) {
|
||||
return groupingBy(classifier, HashMap::new, downstream);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -49,7 +49,7 @@ public class CsvBaseReader implements Serializable {
|
||||
* @param config 配置项
|
||||
*/
|
||||
public CsvBaseReader(CsvReadConfig config) {
|
||||
this.config = ObjectUtil.defaultIfNull(config, CsvReadConfig.defaultConfig());
|
||||
this.config = ObjectUtil.defaultIfNull(config, CsvReadConfig::defaultConfig);
|
||||
}
|
||||
//--------------------------------------------------------------------------------------------- Constructor end
|
||||
|
||||
|
@ -80,7 +80,7 @@ public final class CsvParser extends ComputeIter<CsvRow> implements Closeable, S
|
||||
*/
|
||||
public CsvParser(final Reader reader, CsvReadConfig config) {
|
||||
this.reader = Objects.requireNonNull(reader, "reader must not be null");
|
||||
this.config = ObjectUtil.defaultIfNull(config, CsvReadConfig.defaultConfig());
|
||||
this.config = ObjectUtil.defaultIfNull(config, CsvReadConfig::defaultConfig);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -149,7 +149,7 @@ public final class CsvWriter implements Closeable, Flushable, Serializable {
|
||||
*/
|
||||
public CsvWriter(Writer writer, CsvWriteConfig config) {
|
||||
this.writer = (writer instanceof BufferedWriter) ? writer : new BufferedWriter(writer);
|
||||
this.config = ObjectUtil.defaultIfNull(config, CsvWriteConfig.defaultConfig());
|
||||
this.config = ObjectUtil.defaultIfNull(config, CsvWriteConfig::defaultConfig);
|
||||
}
|
||||
// --------------------------------------------------------------------------------------------------- Constructor end
|
||||
|
||||
|
@ -242,7 +242,7 @@ public class ExecutorBuilder implements Builder<ThreadPoolExecutor> {
|
||||
workQueue = (corePoolSize <= 0) ? new SynchronousQueue<>() : new LinkedBlockingQueue<>(DEFAULT_QUEUE_CAPACITY);
|
||||
}
|
||||
final ThreadFactory threadFactory = (null != builder.threadFactory) ? builder.threadFactory : Executors.defaultThreadFactory();
|
||||
RejectedExecutionHandler handler = ObjectUtil.defaultIfNull(builder.handler, new ThreadPoolExecutor.AbortPolicy());
|
||||
RejectedExecutionHandler handler = ObjectUtil.defaultIfNull(builder.handler, ThreadPoolExecutor.AbortPolicy::new);
|
||||
|
||||
final ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(//
|
||||
corePoolSize, //
|
||||
|
@ -295,6 +295,22 @@ public class ObjectUtil {
|
||||
return isNull(object) ? defaultValue : object;
|
||||
}
|
||||
|
||||
/**
|
||||
* 如果被检查对象为 {@code null}, 返回默认值(由 defaultValueSupplier 提供);否则直接返回
|
||||
*
|
||||
* @param source 被检查对象
|
||||
* @param defaultValueSupplier 默认值提供者
|
||||
* @param <T> 对象类型
|
||||
* @return 被检查对象为{@code null}返回默认值,否则返回自定义handle处理后的返回值
|
||||
* @throws NullPointerException {@code defaultValueSupplier == null} 时,抛出
|
||||
* @since 5.7.20
|
||||
*/
|
||||
public static <T> T defaultIfNull(T source, Supplier<? extends T> defaultValueSupplier) {
|
||||
if (isNull(source)) {
|
||||
return defaultValueSupplier.get();
|
||||
}
|
||||
return source;
|
||||
}
|
||||
|
||||
/**
|
||||
* 如果给定对象为{@code null} 返回默认值, 如果不为null 返回自定义handle处理后的返回值
|
||||
@ -351,6 +367,23 @@ public class ObjectUtil {
|
||||
return StrUtil.isEmpty(str) ? defaultValue : str;
|
||||
}
|
||||
|
||||
/**
|
||||
* 如果被检查对象为 {@code null} 或 "" 时,返回默认值(由 defaultValueSupplier 提供);否则直接返回
|
||||
*
|
||||
* @param str 被检查对象
|
||||
* @param defaultValueSupplier 默认值提供者
|
||||
* @param <T> 对象类型(必须实现CharSequence接口)
|
||||
* @return 被检查对象为{@code null}返回默认值,否则返回自定义handle处理后的返回值
|
||||
* @throws NullPointerException {@code defaultValueSupplier == null} 时,抛出
|
||||
* @since 5.7.20
|
||||
*/
|
||||
public static <T extends CharSequence> T defaultIfEmpty(T str, Supplier<? extends T> defaultValueSupplier) {
|
||||
if (StrUtil.isEmpty(str)) {
|
||||
return defaultValueSupplier.get();
|
||||
}
|
||||
return str;
|
||||
}
|
||||
|
||||
/**
|
||||
* 如果给定对象为{@code null}或者""或者空白符返回默认值
|
||||
*
|
||||
@ -372,6 +405,23 @@ public class ObjectUtil {
|
||||
return StrUtil.isBlank(str) ? defaultValue : str;
|
||||
}
|
||||
|
||||
/**
|
||||
* 如果被检查对象为 {@code null} 或 "" 或 空白字符串时,返回默认值(由 defaultValueSupplier 提供);否则直接返回
|
||||
*
|
||||
* @param str 被检查对象
|
||||
* @param defaultValueSupplier 默认值提供者
|
||||
* @param <T> 对象类型(必须实现CharSequence接口)
|
||||
* @return 被检查对象为{@code null}返回默认值,否则返回自定义handle处理后的返回值
|
||||
* @throws NullPointerException {@code defaultValueSupplier == null} 时,抛出
|
||||
* @since 5.7.20
|
||||
*/
|
||||
public static <T extends CharSequence> T defaultIfBlank(T str, Supplier<? extends T> defaultValueSupplier) {
|
||||
if (StrUtil.isBlank(str)) {
|
||||
return defaultValueSupplier.get();
|
||||
}
|
||||
return str;
|
||||
}
|
||||
|
||||
/**
|
||||
* 克隆对象<br>
|
||||
* 如果对象实现Cloneable接口,调用其clone方法<br>
|
||||
|
@ -344,6 +344,17 @@ public class ReflectUtil {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 是否为父类引用字段<br>
|
||||
* 当字段所在类是对象子类时(对象中定义的非static的class),会自动生成一个以"this$0"为名称的字段,指向父类对象
|
||||
* @param field 字段
|
||||
* @return 是否为父类引用字段
|
||||
* @since 5.7.20
|
||||
*/
|
||||
public static boolean isOuterClassField(Field field){
|
||||
return "this$0".equals(field.getName());
|
||||
}
|
||||
|
||||
// --------------------------------------------------------------------------------------------------------- method
|
||||
|
||||
/**
|
||||
@ -673,11 +684,12 @@ public class ReflectUtil {
|
||||
* @return 是否为equals方法
|
||||
*/
|
||||
public static boolean isEqualsMethod(Method method) {
|
||||
if (method == null || false == "equals".equals(method.getName())) {
|
||||
if (method == null ||
|
||||
1 != method.getParameterCount() ||
|
||||
false == "equals".equals(method.getName())) {
|
||||
return false;
|
||||
}
|
||||
final Class<?>[] paramTypes = method.getParameterTypes();
|
||||
return (1 == paramTypes.length && paramTypes[0] == Object.class);
|
||||
return (method.getParameterTypes()[0] == Object.class);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -712,9 +724,67 @@ public class ReflectUtil {
|
||||
* @since 5.1.1
|
||||
*/
|
||||
public static boolean isEmptyParam(Method method) {
|
||||
return method.getParameterTypes().length == 0;
|
||||
return method.getParameterCount() == 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查给定方法是否为Getter或者Setter方法,规则为:<br>
|
||||
* <ul>
|
||||
* <li>方法参数必须为0个或1个</li>
|
||||
* <li>如果是无参方法,则判断是否以“get”或“is”开头</li>
|
||||
* <li>如果方法参数1个,则判断是否以“set”开头</li>
|
||||
* </ul>
|
||||
*
|
||||
* @param method 方法
|
||||
* @return 是否为Getter或者Setter方法
|
||||
* @since 5.7.20
|
||||
*/
|
||||
public static boolean isGetterOrSetterIgnoreCase(Method method) {
|
||||
return isGetterOrSetter(method, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查给定方法是否为Getter或者Setter方法,规则为:<br>
|
||||
* <ul>
|
||||
* <li>方法参数必须为0个或1个</li>
|
||||
* <li>方法名称不能是getClass</li>
|
||||
* <li>如果是无参方法,则判断是否以“get”或“is”开头</li>
|
||||
* <li>如果方法参数1个,则判断是否以“set”开头</li>
|
||||
* </ul>
|
||||
*
|
||||
* @param method 方法
|
||||
* @param ignoreCase 是否忽略方法名的大小写
|
||||
* @return 是否为Getter或者Setter方法
|
||||
* @since 5.7.20
|
||||
*/
|
||||
public static boolean isGetterOrSetter(Method method, boolean ignoreCase) {
|
||||
if (null == method) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// 参数个数必须为0或1
|
||||
final int parameterCount = method.getParameterCount();
|
||||
if (parameterCount > 1) {
|
||||
return false;
|
||||
}
|
||||
|
||||
String name = method.getName();
|
||||
// 跳过getClass这个特殊方法
|
||||
if("getClass".equals(name)){
|
||||
return false;
|
||||
}
|
||||
if(ignoreCase){
|
||||
name = name.toLowerCase();
|
||||
}
|
||||
switch (parameterCount) {
|
||||
case 0:
|
||||
return name.startsWith("get") || name.startsWith("is");
|
||||
case 1:
|
||||
return name.startsWith("set");
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
// --------------------------------------------------------------------------------------------------------- newInstance
|
||||
|
||||
/**
|
||||
|
@ -76,7 +76,7 @@ public class ServiceLoaderUtil {
|
||||
* @return 服务接口实现列表
|
||||
*/
|
||||
public static <T> ServiceLoader<T> load(Class<T> clazz, ClassLoader loader) {
|
||||
return ServiceLoader.load(clazz, ObjectUtil.defaultIfNull(loader, ClassLoaderUtil.getClassLoader()));
|
||||
return ServiceLoader.load(clazz, ObjectUtil.defaultIfNull(loader, ClassLoaderUtil::getClassLoader));
|
||||
}
|
||||
|
||||
/**
|
||||
|
33
hutool-core/src/test/java/cn/hutool/core/bean/Issue2082Test.java
Executable file
33
hutool-core/src/test/java/cn/hutool/core/bean/Issue2082Test.java
Executable file
@ -0,0 +1,33 @@
|
||||
package cn.hutool.core.bean;
|
||||
|
||||
import lombok.Data;
|
||||
import org.junit.Assert;
|
||||
import org.junit.Test;
|
||||
|
||||
/**
|
||||
* https://github.com/dromara/hutool/issues/2082<br>
|
||||
* 当setXXX有重载方法的时候,BeanDesc中会匹配到重载方法,增加类型检查来规避之
|
||||
*/
|
||||
public class Issue2082Test {
|
||||
|
||||
@Test
|
||||
public void toBeanTest() {
|
||||
TestBean2 testBean2 = new TestBean2();
|
||||
TestBean test = BeanUtil.toBean(testBean2, TestBean.class);
|
||||
Assert.assertNull(test.getId());
|
||||
}
|
||||
|
||||
@Data
|
||||
static class TestBean {
|
||||
private Long id;
|
||||
|
||||
public void setId(String id) {
|
||||
this.id = Long.valueOf(id);
|
||||
}
|
||||
}
|
||||
|
||||
@Data
|
||||
static class TestBean2 {
|
||||
private String id;
|
||||
}
|
||||
}
|
@ -203,6 +203,20 @@ public class CollStreamUtilTest {
|
||||
groupThen);
|
||||
|
||||
// 总之,如果你是想要group分组后还要进行别的操作,用它就对了!
|
||||
// 并且对null值进行了友好处理,例如
|
||||
List<Student> students = Arrays.asList(null, null, new Student(1, 1, 1, "张三"),
|
||||
new Student(1, 2, 1, "李四"));
|
||||
Map<Long, List<Student>> termIdStudentsMap = CollStreamUtil.groupBy(students, Student::getTermId, Collectors.toList());
|
||||
Map<Long, List<Student>> termIdStudentsCompareMap = new HashMap<>();
|
||||
termIdStudentsCompareMap.put(null, Arrays.asList(null, null));
|
||||
termIdStudentsCompareMap.put(1L, Arrays.asList(new Student(1L, 1, 1, "张三"), new Student(1L, 2, 1, "李四")));
|
||||
Assert.assertEquals(termIdStudentsCompareMap, termIdStudentsMap);
|
||||
|
||||
Map<Long, Long> termIdCountMap = CollStreamUtil.groupBy(students, Student::getTermId, Collectors.counting());
|
||||
Map<Long, Long> termIdCountCompareMap = new HashMap<>();
|
||||
termIdCountCompareMap.put(null, 2L);
|
||||
termIdCountCompareMap.put(1L, 2L);
|
||||
Assert.assertEquals(termIdCountCompareMap, termIdCountMap);
|
||||
}
|
||||
|
||||
|
||||
|
@ -162,9 +162,18 @@ public class ValidatorTest {
|
||||
@Test
|
||||
public void isChineseTest(){
|
||||
Assert.assertTrue(Validator.isChinese("全都是中文"));
|
||||
Assert.assertTrue(Validator.isChinese("㐓㐘"));
|
||||
Assert.assertFalse(Validator.isChinese("not全都是中文"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void hasChineseTest() {
|
||||
Assert.assertTrue(Validator.hasChinese("黄单桑米"));
|
||||
Assert.assertTrue(Validator.hasChinese("Kn 四兄弟"));
|
||||
Assert.assertTrue(Validator.hasChinese("\uD840\uDDA3"));
|
||||
Assert.assertFalse(Validator.hasChinese("Abc"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void isUUIDTest(){
|
||||
Assert.assertTrue(Validator.isUUID(IdUtil.randomUUID()));
|
||||
|
@ -1,10 +1,11 @@
|
||||
package cn.hutool.core.map;
|
||||
|
||||
import cn.hutool.core.lang.Pair;
|
||||
import org.junit.Assert;
|
||||
import org.junit.Test;
|
||||
|
||||
public class CaseInsensitiveMapTest {
|
||||
|
||||
|
||||
@Test
|
||||
public void caseInsensitiveMapTest() {
|
||||
CaseInsensitiveMap<String, String> map = new CaseInsensitiveMap<>();
|
||||
@ -12,7 +13,7 @@ public class CaseInsensitiveMapTest {
|
||||
Assert.assertEquals("OK", map.get("aaa"));
|
||||
Assert.assertEquals("OK", map.get("AAA"));
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void caseInsensitiveLinkedMapTest() {
|
||||
CaseInsensitiveLinkedMap<String, String> map = new CaseInsensitiveLinkedMap<>();
|
||||
@ -20,4 +21,16 @@ public class CaseInsensitiveMapTest {
|
||||
Assert.assertEquals("OK", map.get("aaa"));
|
||||
Assert.assertEquals("OK", map.get("AAA"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void mergeTest(){
|
||||
//https://github.com/dromara/hutool/issues/2086
|
||||
Pair<String, String> b = new Pair<>("a", "value");
|
||||
Pair<String, String> a = new Pair<>("A", "value");
|
||||
final CaseInsensitiveMap<Object, Object> map = new CaseInsensitiveMap<>();
|
||||
map.merge(b.getKey(), b.getValue(), (A, B) -> A);
|
||||
map.merge(a.getKey(), a.getValue(), (A, B) -> A);
|
||||
|
||||
Assert.assertEquals(1, map.size());
|
||||
}
|
||||
}
|
||||
|
@ -9,7 +9,7 @@
|
||||
<parent>
|
||||
<groupId>cn.hutool</groupId>
|
||||
<artifactId>hutool-parent</artifactId>
|
||||
<version>5.7.19</version>
|
||||
<version>5.7.20-SNAPSHOT</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>hutool-cron</artifactId>
|
||||
|
@ -9,7 +9,7 @@
|
||||
<parent>
|
||||
<groupId>cn.hutool</groupId>
|
||||
<artifactId>hutool-parent</artifactId>
|
||||
<version>5.7.19</version>
|
||||
<version>5.7.20-SNAPSHOT</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>hutool-crypto</artifactId>
|
||||
|
@ -3,7 +3,6 @@ package cn.hutool.crypto;
|
||||
import cn.hutool.core.codec.Base64;
|
||||
import cn.hutool.core.io.FileUtil;
|
||||
import cn.hutool.core.lang.Validator;
|
||||
import cn.hutool.core.map.MapUtil;
|
||||
import cn.hutool.core.util.HexUtil;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import cn.hutool.crypto.asymmetric.AsymmetricAlgorithm;
|
||||
@ -830,7 +829,7 @@ public class SecureUtil {
|
||||
* @since 3.3.0
|
||||
*/
|
||||
public static Sign sign(SignAlgorithm algorithm) {
|
||||
return new Sign(algorithm);
|
||||
return SignUtil.sign(algorithm);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -845,7 +844,7 @@ public class SecureUtil {
|
||||
* @since 3.3.0
|
||||
*/
|
||||
public static Sign sign(SignAlgorithm algorithm, String privateKeyBase64, String publicKeyBase64) {
|
||||
return new Sign(algorithm, privateKeyBase64, publicKeyBase64);
|
||||
return SignUtil.sign(algorithm, privateKeyBase64, publicKeyBase64);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -860,7 +859,7 @@ public class SecureUtil {
|
||||
* @since 3.3.0
|
||||
*/
|
||||
public static Sign sign(SignAlgorithm algorithm, byte[] privateKey, byte[] publicKey) {
|
||||
return new Sign(algorithm, privateKey, publicKey);
|
||||
return SignUtil.sign(algorithm, privateKey, publicKey);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -875,7 +874,7 @@ public class SecureUtil {
|
||||
* @since 4.0.1
|
||||
*/
|
||||
public static String signParams(SymmetricCrypto crypto, Map<?, ?> params, String... otherParams) {
|
||||
return signParams(crypto, params, StrUtil.EMPTY, StrUtil.EMPTY, true, otherParams);
|
||||
return SignUtil.signParams(crypto, params, otherParams);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -893,7 +892,7 @@ public class SecureUtil {
|
||||
*/
|
||||
public static String signParams(SymmetricCrypto crypto, Map<?, ?> params, String separator,
|
||||
String keyValueSeparator, boolean isIgnoreNull, String... otherParams) {
|
||||
return crypto.encryptHex(MapUtil.sortJoin(params, separator, keyValueSeparator, isIgnoreNull, otherParams));
|
||||
return SignUtil.signParams(crypto, params, separator, keyValueSeparator, isIgnoreNull, otherParams);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -907,7 +906,7 @@ public class SecureUtil {
|
||||
* @since 4.0.1
|
||||
*/
|
||||
public static String signParamsMd5(Map<?, ?> params, String... otherParams) {
|
||||
return signParams(DigestAlgorithm.MD5, params, otherParams);
|
||||
return SignUtil.signParamsMd5(params, otherParams);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -921,7 +920,7 @@ public class SecureUtil {
|
||||
* @since 4.0.8
|
||||
*/
|
||||
public static String signParamsSha1(Map<?, ?> params, String... otherParams) {
|
||||
return signParams(DigestAlgorithm.SHA1, params, otherParams);
|
||||
return SignUtil.signParamsSha1(params, otherParams);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -935,7 +934,7 @@ public class SecureUtil {
|
||||
* @since 4.0.1
|
||||
*/
|
||||
public static String signParamsSha256(Map<?, ?> params, String... otherParams) {
|
||||
return signParams(DigestAlgorithm.SHA256, params, otherParams);
|
||||
return SignUtil.signParamsSha256(params, otherParams);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -950,7 +949,7 @@ public class SecureUtil {
|
||||
* @since 4.0.1
|
||||
*/
|
||||
public static String signParams(DigestAlgorithm digestAlgorithm, Map<?, ?> params, String... otherParams) {
|
||||
return signParams(digestAlgorithm, params, StrUtil.EMPTY, StrUtil.EMPTY, true, otherParams);
|
||||
return SignUtil.signParams(digestAlgorithm, params, otherParams);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -968,7 +967,7 @@ public class SecureUtil {
|
||||
*/
|
||||
public static String signParams(DigestAlgorithm digestAlgorithm, Map<?, ?> params, String separator,
|
||||
String keyValueSeparator, boolean isIgnoreNull, String... otherParams) {
|
||||
return new Digester(digestAlgorithm).digestHex(MapUtil.sortJoin(params, separator, keyValueSeparator, isIgnoreNull, otherParams));
|
||||
return SignUtil.signParams(digestAlgorithm, params, separator, keyValueSeparator, isIgnoreNull, otherParams);
|
||||
}
|
||||
|
||||
/**
|
||||
|
176
hutool-crypto/src/main/java/cn/hutool/crypto/SignUtil.java
Executable file
176
hutool-crypto/src/main/java/cn/hutool/crypto/SignUtil.java
Executable file
@ -0,0 +1,176 @@
|
||||
package cn.hutool.crypto;
|
||||
|
||||
import cn.hutool.core.map.MapUtil;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import cn.hutool.crypto.asymmetric.Sign;
|
||||
import cn.hutool.crypto.asymmetric.SignAlgorithm;
|
||||
import cn.hutool.crypto.digest.DigestAlgorithm;
|
||||
import cn.hutool.crypto.digest.Digester;
|
||||
import cn.hutool.crypto.symmetric.SymmetricCrypto;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* 签名工具类<br>
|
||||
* 封装包括:
|
||||
* <ul>
|
||||
* <li>非堆成签名,签名算法支持见{@link SignAlgorithm}</li>
|
||||
* <li>对称签名,支持Map类型参数排序后签名</li>
|
||||
* <li>摘要签名,支持Map类型参数排序后签名,签名方法见:{@link DigestAlgorithm}</li>
|
||||
* </ul>
|
||||
*
|
||||
* @author looly
|
||||
* @since 5.7.20
|
||||
*/
|
||||
public class SignUtil {
|
||||
|
||||
/**
|
||||
* 创建签名算法对象<br>
|
||||
* 生成新的私钥公钥对
|
||||
*
|
||||
* @param algorithm 签名算法
|
||||
* @return {@link Sign}
|
||||
* @since 3.3.0
|
||||
*/
|
||||
public static Sign sign(SignAlgorithm algorithm) {
|
||||
return new Sign(algorithm);
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建签名算法对象<br>
|
||||
* 私钥和公钥同时为空时生成一对新的私钥和公钥<br>
|
||||
* 私钥和公钥可以单独传入一个,如此则只能使用此钥匙来做签名或验证
|
||||
*
|
||||
* @param algorithm 签名算法
|
||||
* @param privateKeyBase64 私钥Base64
|
||||
* @param publicKeyBase64 公钥Base64
|
||||
* @return {@link Sign}
|
||||
* @since 3.3.0
|
||||
*/
|
||||
public static Sign sign(SignAlgorithm algorithm, String privateKeyBase64, String publicKeyBase64) {
|
||||
return new Sign(algorithm, privateKeyBase64, publicKeyBase64);
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建Sign算法对象<br>
|
||||
* 私钥和公钥同时为空时生成一对新的私钥和公钥<br>
|
||||
* 私钥和公钥可以单独传入一个,如此则只能使用此钥匙来做签名或验证
|
||||
*
|
||||
* @param algorithm 算法枚举
|
||||
* @param privateKey 私钥
|
||||
* @param publicKey 公钥
|
||||
* @return {@link Sign}
|
||||
* @since 3.3.0
|
||||
*/
|
||||
public static Sign sign(SignAlgorithm algorithm, byte[] privateKey, byte[] publicKey) {
|
||||
return new Sign(algorithm, privateKey, publicKey);
|
||||
}
|
||||
|
||||
/**
|
||||
* 对参数做签名<br>
|
||||
* 参数签名为对Map参数按照key的顺序排序后拼接为字符串,然后根据提供的签名算法生成签名字符串<br>
|
||||
* 拼接后的字符串键值对之间无符号,键值对之间无符号,忽略null值
|
||||
*
|
||||
* @param crypto 对称加密算法
|
||||
* @param params 参数
|
||||
* @param otherParams 其它附加参数字符串(例如密钥)
|
||||
* @return 签名
|
||||
* @since 4.0.1
|
||||
*/
|
||||
public static String signParams(SymmetricCrypto crypto, Map<?, ?> params, String... otherParams) {
|
||||
return signParams(crypto, params, StrUtil.EMPTY, StrUtil.EMPTY, true, otherParams);
|
||||
}
|
||||
|
||||
/**
|
||||
* 对参数做签名<br>
|
||||
* 参数签名为对Map参数按照key的顺序排序后拼接为字符串,然后根据提供的签名算法生成签名字符串
|
||||
*
|
||||
* @param crypto 对称加密算法
|
||||
* @param params 参数
|
||||
* @param separator entry之间的连接符
|
||||
* @param keyValueSeparator kv之间的连接符
|
||||
* @param isIgnoreNull 是否忽略null的键和值
|
||||
* @param otherParams 其它附加参数字符串(例如密钥)
|
||||
* @return 签名
|
||||
* @since 4.0.1
|
||||
*/
|
||||
public static String signParams(SymmetricCrypto crypto, Map<?, ?> params, String separator,
|
||||
String keyValueSeparator, boolean isIgnoreNull, String... otherParams) {
|
||||
return crypto.encryptHex(MapUtil.sortJoin(params, separator, keyValueSeparator, isIgnoreNull, otherParams));
|
||||
}
|
||||
|
||||
/**
|
||||
* 对参数做md5签名<br>
|
||||
* 参数签名为对Map参数按照key的顺序排序后拼接为字符串,然后根据提供的签名算法生成签名字符串<br>
|
||||
* 拼接后的字符串键值对之间无符号,键值对之间无符号,忽略null值
|
||||
*
|
||||
* @param params 参数
|
||||
* @param otherParams 其它附加参数字符串(例如密钥)
|
||||
* @return 签名
|
||||
* @since 4.0.1
|
||||
*/
|
||||
public static String signParamsMd5(Map<?, ?> params, String... otherParams) {
|
||||
return signParams(DigestAlgorithm.MD5, params, otherParams);
|
||||
}
|
||||
|
||||
/**
|
||||
* 对参数做Sha1签名<br>
|
||||
* 参数签名为对Map参数按照key的顺序排序后拼接为字符串,然后根据提供的签名算法生成签名字符串<br>
|
||||
* 拼接后的字符串键值对之间无符号,键值对之间无符号,忽略null值
|
||||
*
|
||||
* @param params 参数
|
||||
* @param otherParams 其它附加参数字符串(例如密钥)
|
||||
* @return 签名
|
||||
* @since 4.0.8
|
||||
*/
|
||||
public static String signParamsSha1(Map<?, ?> params, String... otherParams) {
|
||||
return signParams(DigestAlgorithm.SHA1, params, otherParams);
|
||||
}
|
||||
|
||||
/**
|
||||
* 对参数做Sha256签名<br>
|
||||
* 参数签名为对Map参数按照key的顺序排序后拼接为字符串,然后根据提供的签名算法生成签名字符串<br>
|
||||
* 拼接后的字符串键值对之间无符号,键值对之间无符号,忽略null值
|
||||
*
|
||||
* @param params 参数
|
||||
* @param otherParams 其它附加参数字符串(例如密钥)
|
||||
* @return 签名
|
||||
* @since 4.0.1
|
||||
*/
|
||||
public static String signParamsSha256(Map<?, ?> params, String... otherParams) {
|
||||
return signParams(DigestAlgorithm.SHA256, params, otherParams);
|
||||
}
|
||||
|
||||
/**
|
||||
* 对参数做签名<br>
|
||||
* 参数签名为对Map参数按照key的顺序排序后拼接为字符串,然后根据提供的签名算法生成签名字符串<br>
|
||||
* 拼接后的字符串键值对之间无符号,键值对之间无符号,忽略null值
|
||||
*
|
||||
* @param digestAlgorithm 摘要算法
|
||||
* @param params 参数
|
||||
* @param otherParams 其它附加参数字符串(例如密钥)
|
||||
* @return 签名
|
||||
* @since 4.0.1
|
||||
*/
|
||||
public static String signParams(DigestAlgorithm digestAlgorithm, Map<?, ?> params, String... otherParams) {
|
||||
return signParams(digestAlgorithm, params, StrUtil.EMPTY, StrUtil.EMPTY, true, otherParams);
|
||||
}
|
||||
|
||||
/**
|
||||
* 对参数做签名<br>
|
||||
* 参数签名为对Map参数按照key的顺序排序后拼接为字符串,然后根据提供的签名算法生成签名字符串
|
||||
*
|
||||
* @param digestAlgorithm 摘要算法
|
||||
* @param params 参数
|
||||
* @param separator entry之间的连接符
|
||||
* @param keyValueSeparator kv之间的连接符
|
||||
* @param isIgnoreNull 是否忽略null的键和值
|
||||
* @param otherParams 其它附加参数字符串(例如密钥)
|
||||
* @return 签名
|
||||
* @since 4.0.1
|
||||
*/
|
||||
public static String signParams(DigestAlgorithm digestAlgorithm, Map<?, ?> params, String separator,
|
||||
String keyValueSeparator, boolean isIgnoreNull, String... otherParams) {
|
||||
return new Digester(digestAlgorithm).digestHex(MapUtil.sortJoin(params, separator, keyValueSeparator, isIgnoreNull, otherParams));
|
||||
}
|
||||
}
|
@ -9,7 +9,7 @@
|
||||
<parent>
|
||||
<groupId>cn.hutool</groupId>
|
||||
<artifactId>hutool-parent</artifactId>
|
||||
<version>5.7.19</version>
|
||||
<version>5.7.20-SNAPSHOT</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>hutool-db</artifactId>
|
||||
|
@ -9,7 +9,7 @@
|
||||
<parent>
|
||||
<groupId>cn.hutool</groupId>
|
||||
<artifactId>hutool-parent</artifactId>
|
||||
<version>5.7.19</version>
|
||||
<version>5.7.20-SNAPSHOT</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>hutool-dfa</artifactId>
|
||||
|
@ -9,7 +9,7 @@
|
||||
<parent>
|
||||
<groupId>cn.hutool</groupId>
|
||||
<artifactId>hutool-parent</artifactId>
|
||||
<version>5.7.19</version>
|
||||
<version>5.7.20-SNAPSHOT</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>hutool-extra</artifactId>
|
||||
|
@ -9,7 +9,7 @@
|
||||
<parent>
|
||||
<groupId>cn.hutool</groupId>
|
||||
<artifactId>hutool-parent</artifactId>
|
||||
<version>5.7.19</version>
|
||||
<version>5.7.20-SNAPSHOT</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>hutool-http</artifactId>
|
||||
|
@ -25,8 +25,8 @@ public class OS extends UserAgentInfo {
|
||||
*/
|
||||
public static final List<OS> oses = CollUtil.newArrayList(//
|
||||
new OS("Windows 10 or Windows Server 2016", "windows nt 10\\.0", "windows nt (10\\.0)"),//
|
||||
new OS("Windows 8.1 or Winsows Server 2012R2", "windows nt 6\\.3", "windows nt (6\\.3)"),//
|
||||
new OS("Windows 8 or Winsows Server 2012", "windows nt 6\\.2", "windows nt (6\\.2)"),//
|
||||
new OS("Windows 8.1 or Windows Server 2012R2", "windows nt 6\\.3", "windows nt (6\\.3)"),//
|
||||
new OS("Windows 8 or Windows Server 2012", "windows nt 6\\.2", "windows nt (6\\.2)"),//
|
||||
new OS("Windows Vista", "windows nt 6\\.0", "windows nt (6\\.0)"), //
|
||||
new OS("Windows 7 or Windows Server 2008R2", "windows nt 6\\.1", "windows nt (6\\.1)"), //
|
||||
new OS("Windows 2003", "windows nt 5\\.2", "windows nt (5\\.2)"), //
|
||||
|
@ -169,7 +169,7 @@ public class UserAgentUtilTest {
|
||||
Assert.assertEquals("63.0.3239.132", ua.getVersion());
|
||||
Assert.assertEquals("Webkit", ua.getEngine().toString());
|
||||
Assert.assertEquals("537.36", ua.getEngineVersion());
|
||||
Assert.assertEquals("Windows 8.1 or Winsows Server 2012R2", ua.getOs().toString());
|
||||
Assert.assertEquals("Windows 8.1 or Windows Server 2012R2", ua.getOs().toString());
|
||||
Assert.assertEquals("6.3", ua.getOsVersion());
|
||||
Assert.assertEquals("Windows", ua.getPlatform().toString());
|
||||
Assert.assertFalse(ua.isMobile());
|
||||
|
@ -9,7 +9,7 @@
|
||||
<parent>
|
||||
<groupId>cn.hutool</groupId>
|
||||
<artifactId>hutool-parent</artifactId>
|
||||
<version>5.7.19</version>
|
||||
<version>5.7.20-SNAPSHOT</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>hutool-json</artifactId>
|
||||
|
@ -95,7 +95,7 @@ public class JSONArray implements JSON, JSONGetter<Integer>, List<Object>, Rando
|
||||
*/
|
||||
public JSONArray(int initialCapacity, JSONConfig config) {
|
||||
this.rawList = new ArrayList<>(initialCapacity);
|
||||
this.config = ObjectUtil.defaultIfNull(config, JSONConfig.create());
|
||||
this.config = ObjectUtil.defaultIfNull(config, JSONConfig::create);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -9,6 +9,7 @@ import cn.hutool.core.util.StrUtil;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
|
||||
/**
|
||||
@ -113,6 +114,21 @@ public interface JSONGetter<K> extends OptNullBasicTypeFromObjectGetter<K> {
|
||||
return (null == obj) ? null : obj.toBean(beanType);
|
||||
}
|
||||
|
||||
/**
|
||||
* 从JSON中直接获取Bean的List列表<br>
|
||||
* 先获取JSONArray对象,然后转为Bean的List
|
||||
*
|
||||
* @param <T> Bean类型
|
||||
* @param key KEY
|
||||
* @param beanType Bean类型
|
||||
* @return Bean的List,如果值为null或者非JSONObject类型,返回null
|
||||
* @since 5.7.20
|
||||
*/
|
||||
default <T> List<T> getBeanList(K key, Class<T> beanType) {
|
||||
final JSONArray jsonArray = getJSONArray(key);
|
||||
return (null == jsonArray) ? null : jsonArray.toList(beanType);
|
||||
}
|
||||
|
||||
@Override
|
||||
default Date getDate(K key, Date defaultValue) {
|
||||
// 默认转换
|
||||
|
@ -9,7 +9,7 @@
|
||||
<parent>
|
||||
<groupId>cn.hutool</groupId>
|
||||
<artifactId>hutool-parent</artifactId>
|
||||
<version>5.7.19</version>
|
||||
<version>5.7.20-SNAPSHOT</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>hutool-jwt</artifactId>
|
||||
|
@ -9,7 +9,7 @@
|
||||
<parent>
|
||||
<groupId>cn.hutool</groupId>
|
||||
<artifactId>hutool-parent</artifactId>
|
||||
<version>5.7.19</version>
|
||||
<version>5.7.20-SNAPSHOT</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>hutool-log</artifactId>
|
||||
|
@ -9,7 +9,7 @@
|
||||
<parent>
|
||||
<groupId>cn.hutool</groupId>
|
||||
<artifactId>hutool-parent</artifactId>
|
||||
<version>5.7.19</version>
|
||||
<version>5.7.20-SNAPSHOT</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>hutool-poi</artifactId>
|
||||
|
@ -376,7 +376,7 @@ public class CellUtil {
|
||||
public static Cell getMergedRegionCell(Sheet sheet, int x, int y) {
|
||||
return ObjectUtil.defaultIfNull(
|
||||
getCellIfMergedRegion(sheet, x, y),
|
||||
SheetUtil.getCell(sheet, y, x));
|
||||
() -> SheetUtil.getCell(sheet, y, x));
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -294,7 +294,7 @@ public class SheetDataSaxHandler extends DefaultHandler {
|
||||
final int numFmtIndex = xssfCellStyle.getDataFormat();
|
||||
this.numFmtString = ObjectUtil.defaultIfNull(
|
||||
xssfCellStyle.getDataFormatString(),
|
||||
BuiltinFormats.getBuiltinFormat(numFmtIndex));
|
||||
() -> BuiltinFormats.getBuiltinFormat(numFmtIndex));
|
||||
if (CellDataType.NUMBER == this.cellDataType && ExcelSaxUtil.isDateFormat(numFmtIndex, numFmtString)) {
|
||||
cellDataType = CellDataType.DATE;
|
||||
}
|
||||
|
@ -9,7 +9,7 @@
|
||||
<parent>
|
||||
<groupId>cn.hutool</groupId>
|
||||
<artifactId>hutool-parent</artifactId>
|
||||
<version>5.7.19</version>
|
||||
<version>5.7.20-SNAPSHOT</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>hutool-script</artifactId>
|
||||
|
@ -9,7 +9,7 @@
|
||||
<parent>
|
||||
<groupId>cn.hutool</groupId>
|
||||
<artifactId>hutool-parent</artifactId>
|
||||
<version>5.7.19</version>
|
||||
<version>5.7.20-SNAPSHOT</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>hutool-setting</artifactId>
|
||||
|
@ -9,7 +9,7 @@
|
||||
<parent>
|
||||
<groupId>cn.hutool</groupId>
|
||||
<artifactId>hutool-parent</artifactId>
|
||||
<version>5.7.19</version>
|
||||
<version>5.7.20-SNAPSHOT</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>hutool-socket</artifactId>
|
||||
|
@ -9,7 +9,7 @@
|
||||
<parent>
|
||||
<groupId>cn.hutool</groupId>
|
||||
<artifactId>hutool-parent</artifactId>
|
||||
<version>5.7.19</version>
|
||||
<version>5.7.20-SNAPSHOT</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>hutool-system</artifactId>
|
||||
|
2
pom.xml
2
pom.xml
@ -8,7 +8,7 @@
|
||||
|
||||
<groupId>cn.hutool</groupId>
|
||||
<artifactId>hutool-parent</artifactId>
|
||||
<version>5.7.19</version>
|
||||
<version>5.7.20-SNAPSHOT</version>
|
||||
<name>hutool</name>
|
||||
<description>Hutool是一个小而全的Java工具类库,通过静态方法封装,降低相关API的学习成本,提高工作效率,使Java拥有函数式语言般的优雅,让Java语言也可以“甜甜的”。</description>
|
||||
<url>https://github.com/dromara/hutool</url>
|
||||
|
Loading…
x
Reference in New Issue
Block a user