From 3cf00894fa8700e2809d160be9547c8e8406f53e Mon Sep 17 00:00:00 2001
From: Looly
Date: Tue, 1 Mar 2022 13:23:05 +0800
Subject: [PATCH 01/57] prepare 5.7.23
---
CHANGELOG.md | 6 ++++++
README-EN.md | 6 +++---
README.md | 6 +++---
bin/version.txt | 2 +-
docs/js/version.js | 2 +-
hutool-all/pom.xml | 2 +-
hutool-aop/pom.xml | 2 +-
hutool-bloomFilter/pom.xml | 2 +-
hutool-bom/pom.xml | 2 +-
hutool-cache/pom.xml | 2 +-
hutool-captcha/pom.xml | 2 +-
hutool-core/pom.xml | 2 +-
hutool-cron/pom.xml | 2 +-
hutool-crypto/pom.xml | 2 +-
hutool-db/pom.xml | 2 +-
hutool-dfa/pom.xml | 2 +-
hutool-extra/pom.xml | 2 +-
hutool-http/pom.xml | 2 +-
hutool-json/pom.xml | 2 +-
hutool-jwt/pom.xml | 2 +-
hutool-log/pom.xml | 2 +-
hutool-poi/pom.xml | 2 +-
hutool-script/pom.xml | 2 +-
hutool-setting/pom.xml | 2 +-
hutool-socket/pom.xml | 2 +-
hutool-system/pom.xml | 2 +-
pom.xml | 2 +-
27 files changed, 36 insertions(+), 30 deletions(-)
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 2136dba910..06e6039f8b 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,6 +1,12 @@
# 🚀Changelog
+-------------------------------------------------------------------------------------------------------------
+# 5.7.23 (2022-03-01)
+
+### 🐣新特性
+### 🐞Bug修复
+
-------------------------------------------------------------------------------------------------------------
# 5.7.22 (2022-03-01)
diff --git a/README-EN.md b/README-EN.md
index 75fcfed120..05475e644e 100644
--- a/README-EN.md
+++ b/README-EN.md
@@ -142,18 +142,18 @@ We provide the T-Shirt and Sweater with Hutool Logo, please visit the shop:
cn.hutool
hutool-all
- 5.7.22
+ 5.7.23
```
### 🍐Gradle
```
-implementation 'cn.hutool:hutool-all:5.7.22'
+implementation 'cn.hutool:hutool-all:5.7.23'
```
## 📥Download
-- [Maven Repo](https://repo1.maven.org/maven2/cn/hutool/hutool-all/5.7.22/)
+- [Maven Repo](https://repo1.maven.org/maven2/cn/hutool/hutool-all/5.7.23/)
> 🔔️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.
diff --git a/README.md b/README.md
index 4516e5e0de..f8834f52a6 100644
--- a/README.md
+++ b/README.md
@@ -142,20 +142,20 @@ Hutool的存在就是为了减少代码搜索成本,避免网络上参差不
cn.hutool
hutool-all
- 5.7.22
+ 5.7.23
```
### 🍐Gradle
```
-implementation 'cn.hutool:hutool-all:5.7.22'
+implementation 'cn.hutool:hutool-all:5.7.23'
```
### 📥下载jar
点击以下链接,下载`hutool-all-X.X.X.jar`即可:
-- [Maven中央库](https://repo1.maven.org/maven2/cn/hutool/hutool-all/5.7.22/)
+- [Maven中央库](https://repo1.maven.org/maven2/cn/hutool/hutool-all/5.7.23/)
> 🔔️注意
> Hutool 5.x支持JDK8+,对Android平台没有测试,不能保证所有工具类或工具方法可用。
diff --git a/bin/version.txt b/bin/version.txt
index e2ff09a282..a5c3e01d2d 100755
--- a/bin/version.txt
+++ b/bin/version.txt
@@ -1 +1 @@
-5.7.22
+5.7.23
diff --git a/docs/js/version.js b/docs/js/version.js
index e637f5f205..37b3ee602c 100644
--- a/docs/js/version.js
+++ b/docs/js/version.js
@@ -1 +1 @@
-var version = '5.7.22'
\ No newline at end of file
+var version = '5.7.23'
\ No newline at end of file
diff --git a/hutool-all/pom.xml b/hutool-all/pom.xml
index a516862eee..d99b05ea1d 100644
--- a/hutool-all/pom.xml
+++ b/hutool-all/pom.xml
@@ -9,7 +9,7 @@
cn.hutool
hutool-parent
- 5.7.22
+ 5.7.23-SNAPSHOT
hutool-all
diff --git a/hutool-aop/pom.xml b/hutool-aop/pom.xml
index f9dc30ef04..9a53b9232e 100644
--- a/hutool-aop/pom.xml
+++ b/hutool-aop/pom.xml
@@ -9,7 +9,7 @@
cn.hutool
hutool-parent
- 5.7.22
+ 5.7.23-SNAPSHOT
hutool-aop
diff --git a/hutool-bloomFilter/pom.xml b/hutool-bloomFilter/pom.xml
index 4abc55f45a..6820aa9968 100644
--- a/hutool-bloomFilter/pom.xml
+++ b/hutool-bloomFilter/pom.xml
@@ -9,7 +9,7 @@
cn.hutool
hutool-parent
- 5.7.22
+ 5.7.23-SNAPSHOT
hutool-bloomFilter
diff --git a/hutool-bom/pom.xml b/hutool-bom/pom.xml
index 3490b7ceed..35c3ab9aa9 100644
--- a/hutool-bom/pom.xml
+++ b/hutool-bom/pom.xml
@@ -9,7 +9,7 @@
cn.hutool
hutool-parent
- 5.7.22
+ 5.7.23-SNAPSHOT
hutool-bom
diff --git a/hutool-cache/pom.xml b/hutool-cache/pom.xml
index 298a97a6c2..430d0b0925 100644
--- a/hutool-cache/pom.xml
+++ b/hutool-cache/pom.xml
@@ -9,7 +9,7 @@
cn.hutool
hutool-parent
- 5.7.22
+ 5.7.23-SNAPSHOT
hutool-cache
diff --git a/hutool-captcha/pom.xml b/hutool-captcha/pom.xml
index 17564bff02..b2dbcf27fb 100644
--- a/hutool-captcha/pom.xml
+++ b/hutool-captcha/pom.xml
@@ -9,7 +9,7 @@
cn.hutool
hutool-parent
- 5.7.22
+ 5.7.23-SNAPSHOT
hutool-captcha
diff --git a/hutool-core/pom.xml b/hutool-core/pom.xml
index 9c225e9c78..6ca1dd7894 100644
--- a/hutool-core/pom.xml
+++ b/hutool-core/pom.xml
@@ -9,7 +9,7 @@
cn.hutool
hutool-parent
- 5.7.22
+ 5.7.23-SNAPSHOT
hutool-core
diff --git a/hutool-cron/pom.xml b/hutool-cron/pom.xml
index 94c4538cc9..190721d023 100644
--- a/hutool-cron/pom.xml
+++ b/hutool-cron/pom.xml
@@ -9,7 +9,7 @@
cn.hutool
hutool-parent
- 5.7.22
+ 5.7.23-SNAPSHOT
hutool-cron
diff --git a/hutool-crypto/pom.xml b/hutool-crypto/pom.xml
index cff3649a96..8dbd928e28 100644
--- a/hutool-crypto/pom.xml
+++ b/hutool-crypto/pom.xml
@@ -9,7 +9,7 @@
cn.hutool
hutool-parent
- 5.7.22
+ 5.7.23-SNAPSHOT
hutool-crypto
diff --git a/hutool-db/pom.xml b/hutool-db/pom.xml
index c8c5ccff73..2d586bf0bc 100644
--- a/hutool-db/pom.xml
+++ b/hutool-db/pom.xml
@@ -9,7 +9,7 @@
cn.hutool
hutool-parent
- 5.7.22
+ 5.7.23-SNAPSHOT
hutool-db
diff --git a/hutool-dfa/pom.xml b/hutool-dfa/pom.xml
index 65737363bb..c16db7ae21 100644
--- a/hutool-dfa/pom.xml
+++ b/hutool-dfa/pom.xml
@@ -9,7 +9,7 @@
cn.hutool
hutool-parent
- 5.7.22
+ 5.7.23-SNAPSHOT
hutool-dfa
diff --git a/hutool-extra/pom.xml b/hutool-extra/pom.xml
index adff1d26e6..b86f833cfe 100644
--- a/hutool-extra/pom.xml
+++ b/hutool-extra/pom.xml
@@ -9,7 +9,7 @@
cn.hutool
hutool-parent
- 5.7.22
+ 5.7.23-SNAPSHOT
hutool-extra
diff --git a/hutool-http/pom.xml b/hutool-http/pom.xml
index 5f686c62e4..f3853f53c3 100644
--- a/hutool-http/pom.xml
+++ b/hutool-http/pom.xml
@@ -9,7 +9,7 @@
cn.hutool
hutool-parent
- 5.7.22
+ 5.7.23-SNAPSHOT
hutool-http
diff --git a/hutool-json/pom.xml b/hutool-json/pom.xml
index 7c769ba2ba..12f8201be0 100644
--- a/hutool-json/pom.xml
+++ b/hutool-json/pom.xml
@@ -9,7 +9,7 @@
cn.hutool
hutool-parent
- 5.7.22
+ 5.7.23-SNAPSHOT
hutool-json
diff --git a/hutool-jwt/pom.xml b/hutool-jwt/pom.xml
index 32213fa80b..4836888d00 100644
--- a/hutool-jwt/pom.xml
+++ b/hutool-jwt/pom.xml
@@ -9,7 +9,7 @@
cn.hutool
hutool-parent
- 5.7.22
+ 5.7.23-SNAPSHOT
hutool-jwt
diff --git a/hutool-log/pom.xml b/hutool-log/pom.xml
index 68e3b1ccc3..3169cdc7ec 100644
--- a/hutool-log/pom.xml
+++ b/hutool-log/pom.xml
@@ -9,7 +9,7 @@
cn.hutool
hutool-parent
- 5.7.22
+ 5.7.23-SNAPSHOT
hutool-log
diff --git a/hutool-poi/pom.xml b/hutool-poi/pom.xml
index e9f5652911..c737053c72 100644
--- a/hutool-poi/pom.xml
+++ b/hutool-poi/pom.xml
@@ -9,7 +9,7 @@
cn.hutool
hutool-parent
- 5.7.22
+ 5.7.23-SNAPSHOT
hutool-poi
diff --git a/hutool-script/pom.xml b/hutool-script/pom.xml
index fd29dd9a61..35b6f525be 100644
--- a/hutool-script/pom.xml
+++ b/hutool-script/pom.xml
@@ -9,7 +9,7 @@
cn.hutool
hutool-parent
- 5.7.22
+ 5.7.23-SNAPSHOT
hutool-script
diff --git a/hutool-setting/pom.xml b/hutool-setting/pom.xml
index d7f1a860e9..8cf940d71c 100644
--- a/hutool-setting/pom.xml
+++ b/hutool-setting/pom.xml
@@ -9,7 +9,7 @@
cn.hutool
hutool-parent
- 5.7.22
+ 5.7.23-SNAPSHOT
hutool-setting
diff --git a/hutool-socket/pom.xml b/hutool-socket/pom.xml
index 838df72ec3..7659cce359 100644
--- a/hutool-socket/pom.xml
+++ b/hutool-socket/pom.xml
@@ -9,7 +9,7 @@
cn.hutool
hutool-parent
- 5.7.22
+ 5.7.23-SNAPSHOT
hutool-socket
diff --git a/hutool-system/pom.xml b/hutool-system/pom.xml
index 76f6ae1d0e..0f28acc207 100644
--- a/hutool-system/pom.xml
+++ b/hutool-system/pom.xml
@@ -9,7 +9,7 @@
cn.hutool
hutool-parent
- 5.7.22
+ 5.7.23-SNAPSHOT
hutool-system
diff --git a/pom.xml b/pom.xml
index 771da27957..4800b8a1f6 100644
--- a/pom.xml
+++ b/pom.xml
@@ -8,7 +8,7 @@
cn.hutool
hutool-parent
- 5.7.22
+ 5.7.23-SNAPSHOT
hutool
Hutool是一个小而全的Java工具类库,通过静态方法封装,降低相关API的学习成本,提高工作效率,使Java拥有函数式语言般的优雅,让Java语言也可以“甜甜的”。
https://github.com/dromara/hutool
--
Gitee
From 0e5329850a7eda0e558ed20854e58a90d70db7e8 Mon Sep 17 00:00:00 2001
From: VampireAchao
Date: Tue, 1 Mar 2022 22:59:28 +0800
Subject: [PATCH 02/57] =?UTF-8?q?=E8=8E=B7=E5=8F=96=E5=88=AB=E5=90=8D?=
=?UTF-8?q?=E6=94=AF=E6=8C=81=E5=90=8E=E7=9A=84=E6=B3=A8=E8=A7=A3?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.../core/annotation/AnnotationUtil.java | 32 +++++++++++++++----
.../core/annotation/AnnotationForTest.java | 6 ++--
.../core/annotation/AnnotationUtilTest.java | 10 ++++++
3 files changed, 39 insertions(+), 9 deletions(-)
diff --git a/hutool-core/src/main/java/cn/hutool/core/annotation/AnnotationUtil.java b/hutool-core/src/main/java/cn/hutool/core/annotation/AnnotationUtil.java
index 1049a517ae..071cf1b13d 100644
--- a/hutool-core/src/main/java/cn/hutool/core/annotation/AnnotationUtil.java
+++ b/hutool-core/src/main/java/cn/hutool/core/annotation/AnnotationUtil.java
@@ -2,15 +2,11 @@ package cn.hutool.core.annotation;
import cn.hutool.core.exceptions.UtilException;
import cn.hutool.core.util.ArrayUtil;
+import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.ReflectUtil;
+import cn.hutool.core.util.StrUtil;
-import java.lang.annotation.Annotation;
-import java.lang.annotation.Documented;
-import java.lang.annotation.ElementType;
-import java.lang.annotation.Inherited;
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-import java.lang.annotation.Target;
+import java.lang.annotation.*;
import java.lang.reflect.AnnotatedElement;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
@@ -215,4 +211,26 @@ public class AnnotationUtil {
final Map memberValues = (Map) ReflectUtil.getFieldValue(Proxy.getInvocationHandler(annotation), "memberValues");
memberValues.put(annotationField, value);
}
+
+ /**
+ * 获取别名支持后的注解
+ *
+ * @param annotationEle 被注解的类
+ * @param annotationType 注解类型Class
+ * @param 注解类型
+ * @return 别名支持后的注解
+ */
+ @SuppressWarnings("unchecked")
+ public static T getAnnotationAlias(AnnotatedElement annotationEle, Class annotationType) {
+ T annotation = getAnnotation(annotationEle, annotationType);
+ Object o = Proxy.newProxyInstance(annotationType.getClassLoader(), new Class[]{annotationType}, (proxy, method, args) -> {
+ Alias alias = method.getAnnotation(Alias.class);
+ if (ObjectUtil.isNotNull(alias) && StrUtil.isNotBlank(alias.value())) {
+ Method aliasMethod = annotationType.getMethod(alias.value());
+ return ReflectUtil.invoke(annotation, aliasMethod);
+ }
+ return method.invoke(args);
+ });
+ return (T) o;
+ }
}
diff --git a/hutool-core/src/test/java/cn/hutool/core/annotation/AnnotationForTest.java b/hutool-core/src/test/java/cn/hutool/core/annotation/AnnotationForTest.java
index 6b41de3adb..83210ae901 100644
--- a/hutool-core/src/test/java/cn/hutool/core/annotation/AnnotationForTest.java
+++ b/hutool-core/src/test/java/cn/hutool/core/annotation/AnnotationForTest.java
@@ -10,7 +10,6 @@ import java.lang.annotation.Target;
* 注解类相关说明见:https://www.cnblogs.com/xdp-gacl/p/3622275.html
*
* @author looly
- *
*/
// Retention注解决定MyAnnotation注解的生命周期
@Retention(RetentionPolicy.RUNTIME)
@@ -23,5 +22,8 @@ public @interface AnnotationForTest {
*
* @return 属性值
*/
- String value();
+ String value() default "";
+
+ @Alias("value")
+ String retry() default "";
}
diff --git a/hutool-core/src/test/java/cn/hutool/core/annotation/AnnotationUtilTest.java b/hutool-core/src/test/java/cn/hutool/core/annotation/AnnotationUtilTest.java
index b565cbcb6c..3340dcf37b 100644
--- a/hutool-core/src/test/java/cn/hutool/core/annotation/AnnotationUtilTest.java
+++ b/hutool-core/src/test/java/cn/hutool/core/annotation/AnnotationUtilTest.java
@@ -12,6 +12,16 @@ public class AnnotationUtilTest {
}
+ @Test
+ public void getAnnotationSyncAlias() {
+ // 直接获取
+ Assert.assertEquals("", ClassWithAnnotation.class.getAnnotation(AnnotationForTest.class).retry());
+
+ // 加别名适配
+ AnnotationForTest annotation = AnnotationUtil.getAnnotationAlias(ClassWithAnnotation.class, AnnotationForTest.class);
+ Assert.assertEquals("测试", annotation.retry());
+ }
+
@AnnotationForTest("测试")
static class ClassWithAnnotation{
public void test(){
--
Gitee
From 0c3ef87647ea97cc3301649b13fd550272c14452 Mon Sep 17 00:00:00 2001
From: jiazhengquan <2466896229@qq.com>
Date: Thu, 3 Mar 2022 10:27:08 +0800
Subject: [PATCH 03/57] =?UTF-8?q?=E9=92=88=E5=AF=B9issue->ObjectUtil.hasNu?=
=?UTF-8?q?ll(Object...=20objs)=E5=9C=A8=E5=A4=84=E7=90=86null=E6=97=B6?=
=?UTF-8?q?=E6=9C=89bug=E4=BF=AE=E5=A4=8D?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
hutool-core/src/main/java/cn/hutool/core/util/ObjectUtil.java | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/hutool-core/src/main/java/cn/hutool/core/util/ObjectUtil.java b/hutool-core/src/main/java/cn/hutool/core/util/ObjectUtil.java
index 8fe082b437..5c9fc00dcb 100644
--- a/hutool-core/src/main/java/cn/hutool/core/util/ObjectUtil.java
+++ b/hutool-core/src/main/java/cn/hutool/core/util/ObjectUtil.java
@@ -625,7 +625,7 @@ public class ObjectUtil {
* @see ArrayUtil#hasNull(Object[])
*/
public static boolean hasNull(Object... objs) {
- return ArrayUtil.hasNull(objs);
+ return objs == null || ArrayUtil.hasNull(objs);
}
/**
--
Gitee
From 3c19a939f097081bfd90fce6a5034b9e7f11063a Mon Sep 17 00:00:00 2001
From: jiazhengquan <2466896229@qq.com>
Date: Thu, 3 Mar 2022 14:26:41 +0800
Subject: [PATCH 04/57] =?UTF-8?q?=E9=92=88=E5=AF=B9issue->ObjectUtil.hasNu?=
=?UTF-8?q?ll(Object...=20objs)=E5=9C=A8=E5=A4=84=E7=90=86null=E6=97=B6?=
=?UTF-8?q?=E6=9C=89bug=E4=BF=AE=E5=A4=8D?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
hutool-core/src/main/java/cn/hutool/core/util/ArrayUtil.java | 2 +-
hutool-core/src/main/java/cn/hutool/core/util/ObjectUtil.java | 2 +-
2 files changed, 2 insertions(+), 2 deletions(-)
diff --git a/hutool-core/src/main/java/cn/hutool/core/util/ArrayUtil.java b/hutool-core/src/main/java/cn/hutool/core/util/ArrayUtil.java
index b0c9cbacb0..8c948e8a92 100644
--- a/hutool-core/src/main/java/cn/hutool/core/util/ArrayUtil.java
+++ b/hutool-core/src/main/java/cn/hutool/core/util/ArrayUtil.java
@@ -122,7 +122,7 @@ public class ArrayUtil extends PrimitiveArrayUtil {
}
}
}
- return false;
+ return array == null;
}
/**
diff --git a/hutool-core/src/main/java/cn/hutool/core/util/ObjectUtil.java b/hutool-core/src/main/java/cn/hutool/core/util/ObjectUtil.java
index 5c9fc00dcb..8fe082b437 100644
--- a/hutool-core/src/main/java/cn/hutool/core/util/ObjectUtil.java
+++ b/hutool-core/src/main/java/cn/hutool/core/util/ObjectUtil.java
@@ -625,7 +625,7 @@ public class ObjectUtil {
* @see ArrayUtil#hasNull(Object[])
*/
public static boolean hasNull(Object... objs) {
- return objs == null || ArrayUtil.hasNull(objs);
+ return ArrayUtil.hasNull(objs);
}
/**
--
Gitee
From 8b49984594b11b4bf320376909e5a744c4bfab3d Mon Sep 17 00:00:00 2001
From: Looly
Date: Thu, 3 Mar 2022 17:15:35 +0800
Subject: [PATCH 05/57] change map type
---
CHANGELOG.md | 3 ++-
hutool-http/src/main/java/cn/hutool/http/HttpRequest.java | 4 ++--
2 files changed, 4 insertions(+), 3 deletions(-)
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 06e6039f8b..df16f1b201 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -2,9 +2,10 @@
# 🚀Changelog
-------------------------------------------------------------------------------------------------------------
-# 5.7.23 (2022-03-01)
+# 5.7.23 (2022-03-03)
### 🐣新特性
+* 【http 】 HttpRequest.form采用TableMap方式(issue#I4W427@gitee)
### 🐞Bug修复
-------------------------------------------------------------------------------------------------------------
diff --git a/hutool-http/src/main/java/cn/hutool/http/HttpRequest.java b/hutool-http/src/main/java/cn/hutool/http/HttpRequest.java
index 864363e19d..927c8bd4d9 100644
--- a/hutool-http/src/main/java/cn/hutool/http/HttpRequest.java
+++ b/hutool-http/src/main/java/cn/hutool/http/HttpRequest.java
@@ -9,6 +9,7 @@ import cn.hutool.core.io.resource.MultiFileResource;
import cn.hutool.core.io.resource.Resource;
import cn.hutool.core.lang.Assert;
import cn.hutool.core.map.MapUtil;
+import cn.hutool.core.map.TableMap;
import cn.hutool.core.net.SSLUtil;
import cn.hutool.core.net.url.UrlBuilder;
import cn.hutool.core.util.ArrayUtil;
@@ -32,7 +33,6 @@ import java.net.Proxy;
import java.net.URLStreamHandler;
import java.nio.charset.Charset;
import java.util.Collection;
-import java.util.LinkedHashMap;
import java.util.Map;
import java.util.function.Consumer;
@@ -1314,7 +1314,7 @@ public class HttpRequest extends HttpBase {
return this;
}
if (null == this.form) {
- this.form = new LinkedHashMap<>();
+ this.form = new TableMap<>(16);
}
this.form.put(name, value);
return this;
--
Gitee
From 4c1a729e978755b7b472b1676650dc75f3a9614d Mon Sep 17 00:00:00 2001
From: Looly
Date: Thu, 3 Mar 2022 17:24:00 +0800
Subject: [PATCH 06/57] fix bug
---
CHANGELOG.md | 1 +
1 file changed, 1 insertion(+)
diff --git a/CHANGELOG.md b/CHANGELOG.md
index df16f1b201..940921f732 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -7,6 +7,7 @@
### 🐣新特性
* 【http 】 HttpRequest.form采用TableMap方式(issue#I4W427@gitee)
### 🐞Bug修复
+* 【core 】 修复ObjectUtil.hasNull传入null返回true的问题(pr#555@gitee)
-------------------------------------------------------------------------------------------------------------
# 5.7.22 (2022-03-01)
--
Gitee
From d1dea5c88ef600fd8d7d3a0a412b98ad1637df58 Mon Sep 17 00:00:00 2001
From: Looly
Date: Thu, 3 Mar 2022 21:52:30 +0800
Subject: [PATCH 07/57] fix bug
---
.../cn/hutool/core/collection/CollUtil.java | 14 ++---------
.../main/java/cn/hutool/core/map/MapUtil.java | 25 +++++--------------
.../java/cn/hutool/core/map/MapWrapper.java | 13 +++++++++-
.../java/cn/hutool/core/util/ReflectUtil.java | 2 +-
.../java/cn/hutool/core/map/MapUtilTest.java | 18 +++++++++++++
5 files changed, 39 insertions(+), 33 deletions(-)
diff --git a/hutool-core/src/main/java/cn/hutool/core/collection/CollUtil.java b/hutool-core/src/main/java/cn/hutool/core/collection/CollUtil.java
index dd22d5275b..21120cd252 100644
--- a/hutool-core/src/main/java/cn/hutool/core/collection/CollUtil.java
+++ b/hutool-core/src/main/java/cn/hutool/core/collection/CollUtil.java
@@ -1178,20 +1178,10 @@ public class CollUtil {
return collection;
}
- Collection collection2 = ObjectUtil.clone(collection);
- if (null == collection2) {
- // 不支持clone
- collection2 = create(collection.getClass());
- }
- if (isEmpty(collection2)) {
+ final Collection collection2 = create(collection.getClass());
+ if (isEmpty(collection)) {
return collection2;
}
- try {
- collection2.clear();
- } catch (UnsupportedOperationException e) {
- // 克隆后的对象不支持清空,说明为不可变集合对象,使用默认的ArrayList保存结果
- collection2 = new ArrayList<>();
- }
T modified;
for (T t : collection) {
diff --git a/hutool-core/src/main/java/cn/hutool/core/map/MapUtil.java b/hutool-core/src/main/java/cn/hutool/core/map/MapUtil.java
index b0fb792505..194777224c 100644
--- a/hutool-core/src/main/java/cn/hutool/core/map/MapUtil.java
+++ b/hutool-core/src/main/java/cn/hutool/core/map/MapUtil.java
@@ -7,7 +7,6 @@ import cn.hutool.core.lang.Filter;
import cn.hutool.core.lang.Pair;
import cn.hutool.core.lang.TypeReference;
import cn.hutool.core.util.ArrayUtil;
-import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.ReflectUtil;
import cn.hutool.core.util.StrUtil;
@@ -623,25 +622,19 @@ public class MapUtil {
* @param editor 编辑器接口
* @return 编辑后的Map
*/
+ @SuppressWarnings("unchecked")
public static Map edit(Map map, Editor> editor) {
if (null == map || null == editor) {
return map;
}
- Map map2 = ObjectUtil.clone(map);
+ Map map2 = ReflectUtil.newInstanceIfPossible(map.getClass());
if(null == map2){
- // 不支持clone
map2 = new HashMap<>(map.size(), 1f);
}
- if (isEmpty(map2)) {
+ if (isEmpty(map)) {
return map2;
}
- try {
- map2.clear();
- } catch (UnsupportedOperationException e) {
- // 克隆后的对象不支持清空,说明为不可变集合对象,使用默认的ArrayList保存结果
- map2 = new HashMap<>(map.size(), 1f);
- }
Entry modified;
for (Entry entry : map.entrySet()) {
@@ -690,20 +683,14 @@ public class MapUtil {
if(null == map || null == keys){
return map;
}
- Map map2 = ObjectUtil.clone(map);
+
+ Map map2 = ReflectUtil.newInstanceIfPossible(map.getClass());
if(null == map2){
- // 不支持clone
map2 = new HashMap<>(map.size(), 1f);
}
- if (isEmpty(map2)) {
+ if (isEmpty(map)) {
return map2;
}
- try {
- map2.clear();
- } catch (UnsupportedOperationException e) {
- // 克隆后的对象不支持清空,说明为不可变集合对象,使用默认的ArrayList保存结果
- map2 = new HashMap<>();
- }
for (K key : keys) {
if (map.containsKey(key)) {
diff --git a/hutool-core/src/main/java/cn/hutool/core/map/MapWrapper.java b/hutool-core/src/main/java/cn/hutool/core/map/MapWrapper.java
index 000b15ea6f..bfd81dd86d 100644
--- a/hutool-core/src/main/java/cn/hutool/core/map/MapWrapper.java
+++ b/hutool-core/src/main/java/cn/hutool/core/map/MapWrapper.java
@@ -1,5 +1,7 @@
package cn.hutool.core.map;
+import cn.hutool.core.util.ObjectUtil;
+
import java.io.Serializable;
import java.util.Collection;
import java.util.Iterator;
@@ -30,7 +32,7 @@ public class MapWrapper implements Map, Iterable>, S
*/
protected static final int DEFAULT_INITIAL_CAPACITY = 1 << 4; // aka 16
- private final Map raw;
+ private Map raw;
/**
* 构造
@@ -199,5 +201,14 @@ public class MapWrapper implements Map, Iterable>, S
public V merge(K key, V value, BiFunction super V, ? super V, ? extends V> remappingFunction) {
return raw.merge(key, value, remappingFunction);
}
+
+ @Override
+ public MapWrapper clone() throws CloneNotSupportedException {
+ @SuppressWarnings("unchecked")
+ final MapWrapper clone = (MapWrapper) super.clone();
+ clone.raw = ObjectUtil.clone(raw);
+ return clone;
+ }
+
//---------------------------------------------------------------------------- Override default methods end
}
diff --git a/hutool-core/src/main/java/cn/hutool/core/util/ReflectUtil.java b/hutool-core/src/main/java/cn/hutool/core/util/ReflectUtil.java
index 6f31764337..43440bb321 100644
--- a/hutool-core/src/main/java/cn/hutool/core/util/ReflectUtil.java
+++ b/hutool-core/src/main/java/cn/hutool/core/util/ReflectUtil.java
@@ -848,7 +848,7 @@ public class ReflectUtil {
*
* @param 对象类型
* @param beanClass 被构造的类
- * @return 构造后的对象
+ * @return 构造后的对象,构造失败返回{@code null}
*/
@SuppressWarnings("unchecked")
public static T newInstanceIfPossible(Class beanClass) {
diff --git a/hutool-core/src/test/java/cn/hutool/core/map/MapUtilTest.java b/hutool-core/src/test/java/cn/hutool/core/map/MapUtilTest.java
index 19fdc83166..ed47980fed 100644
--- a/hutool-core/src/test/java/cn/hutool/core/map/MapUtilTest.java
+++ b/hutool-core/src/test/java/cn/hutool/core/map/MapUtilTest.java
@@ -26,6 +26,24 @@ public class MapUtilTest {
Assert.assertEquals("4", map2.get("d"));
}
+ @Test
+ public void filterMapWrapperTest() {
+ Map map = MapUtil.newHashMap();
+ map.put("a", "1");
+ map.put("b", "2");
+ map.put("c", "3");
+ map.put("d", "4");
+
+ final Map camelCaseMap = MapUtil.toCamelCaseMap(map);
+
+ Map map2 = MapUtil.filter(camelCaseMap, t -> Convert.toInt(t.getValue()) % 2 == 0);
+
+ Assert.assertEquals(2, map2.size());
+
+ Assert.assertEquals("2", map2.get("b"));
+ Assert.assertEquals("4", map2.get("d"));
+ }
+
@Test
public void filterContainsTest() {
Map map = MapUtil.newHashMap();
--
Gitee
From ccc9f2568ffdff028e09107e0bbe879b89817b30 Mon Sep 17 00:00:00 2001
From: Looly
Date: Thu, 3 Mar 2022 23:54:47 +0800
Subject: [PATCH 08/57] add AnnotationProxy
---
CHANGELOG.md | 1 +
.../core/annotation/AnnotationProxy.java | 88 +++++++++++++++++++
.../core/annotation/AnnotationUtil.java | 29 +++---
.../java/cn/hutool/core/util/ReflectUtil.java | 3 +-
4 files changed, 104 insertions(+), 17 deletions(-)
create mode 100644 hutool-core/src/main/java/cn/hutool/core/annotation/AnnotationProxy.java
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 940921f732..2cf4af00a0 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -6,6 +6,7 @@
### 🐣新特性
* 【http 】 HttpRequest.form采用TableMap方式(issue#I4W427@gitee)
+* 【core 】 AnnotationUtil增加getAnnotationAlias方法(pr#554@gitee)
### 🐞Bug修复
* 【core 】 修复ObjectUtil.hasNull传入null返回true的问题(pr#555@gitee)
diff --git a/hutool-core/src/main/java/cn/hutool/core/annotation/AnnotationProxy.java b/hutool-core/src/main/java/cn/hutool/core/annotation/AnnotationProxy.java
new file mode 100644
index 0000000000..a61ff66681
--- /dev/null
+++ b/hutool-core/src/main/java/cn/hutool/core/annotation/AnnotationProxy.java
@@ -0,0 +1,88 @@
+package cn.hutool.core.annotation;
+
+import cn.hutool.core.util.ReflectUtil;
+import cn.hutool.core.util.StrUtil;
+
+import java.io.Serializable;
+import java.lang.annotation.Annotation;
+import java.lang.reflect.InvocationHandler;
+import java.lang.reflect.Method;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * 注解代理
+ * 通过代理指定注解,可以自定义调用注解的方法逻辑,如支持{@link Alias} 注解
+ *
+ * @param 注解类型
+ * @since 5.7.23
+ */
+public class AnnotationProxy implements Annotation, InvocationHandler, Serializable {
+ private static final long serialVersionUID = 1L;
+
+ private final T annotation;
+ private final Class type;
+ private final Map attributes;
+
+ /**
+ * 构造
+ *
+ * @param annotation 注解
+ */
+ public AnnotationProxy(T annotation) {
+ this.annotation = annotation;
+ //noinspection unchecked
+ this.type = (Class) annotation.annotationType();
+ this.attributes = initAttributes();
+ }
+
+
+ @Override
+ public Class extends Annotation> annotationType() {
+ return null;
+ }
+
+ @Override
+ public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
+
+ // 注解别名
+ Alias alias = method.getAnnotation(Alias.class);
+ if(null != alias){
+ final String name = alias.value();
+ if(StrUtil.isNotBlank(name)){
+ if(false == attributes.containsKey(name)){
+ throw new IllegalArgumentException(StrUtil.format("No method for alias: [{}]", name));
+ }
+ return attributes.get(name);
+ }
+ }
+
+ final Object value = attributes.get(method.getName());
+ if (value != null) {
+ return value;
+ }
+ return method.invoke(this, args);
+ }
+
+ /**
+ * 初始化注解的属性
+ * 此方法预先调用所有注解的方法,将注解方法值缓存于attributes中
+ *
+ * @return 属性(方法结果)映射
+ */
+ private Map initAttributes() {
+ final Method[] methods = ReflectUtil.getMethods(this.type);
+ final Map attributes = new HashMap<>(methods.length, 1);
+
+ for (Method method : methods) {
+ // 跳过匿名内部类自动生成的方法
+ if (method.isSynthetic()) {
+ continue;
+ }
+
+ attributes.put(method.getName(), ReflectUtil.invoke(this.annotation, method));
+ }
+
+ return attributes;
+ }
+}
diff --git a/hutool-core/src/main/java/cn/hutool/core/annotation/AnnotationUtil.java b/hutool-core/src/main/java/cn/hutool/core/annotation/AnnotationUtil.java
index 071cf1b13d..be44f080a5 100644
--- a/hutool-core/src/main/java/cn/hutool/core/annotation/AnnotationUtil.java
+++ b/hutool-core/src/main/java/cn/hutool/core/annotation/AnnotationUtil.java
@@ -2,11 +2,15 @@ package cn.hutool.core.annotation;
import cn.hutool.core.exceptions.UtilException;
import cn.hutool.core.util.ArrayUtil;
-import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.ReflectUtil;
-import cn.hutool.core.util.StrUtil;
-import java.lang.annotation.*;
+import java.lang.annotation.Annotation;
+import java.lang.annotation.Documented;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Inherited;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
import java.lang.reflect.AnnotatedElement;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
@@ -38,7 +42,7 @@ public class AnnotationUtil {
/**
* 获取指定注解
*
- * @param annotationEle {@link AnnotatedElement},可以是Class、Method、Field、Constructor、ReflectPermission
+ * @param annotationEle {@link AnnotatedElement},可以是Class、Method、Field、Constructor、ReflectPermission
* @param isToCombination 是否为转换为组合注解
* @return 注解对象
*/
@@ -201,9 +205,9 @@ public class AnnotationUtil {
/**
* 设置新的注解的属性(字段)值
*
- * @param annotation 注解对象
+ * @param annotation 注解对象
* @param annotationField 注解属性(字段)名称
- * @param value 要更新的属性值
+ * @param value 要更新的属性值
* @since 5.5.2
*/
@SuppressWarnings({"rawtypes", "unchecked"})
@@ -219,18 +223,11 @@ public class AnnotationUtil {
* @param annotationType 注解类型Class
* @param 注解类型
* @return 别名支持后的注解
+ * @since 5.7.23
*/
@SuppressWarnings("unchecked")
public static T getAnnotationAlias(AnnotatedElement annotationEle, Class annotationType) {
- T annotation = getAnnotation(annotationEle, annotationType);
- Object o = Proxy.newProxyInstance(annotationType.getClassLoader(), new Class[]{annotationType}, (proxy, method, args) -> {
- Alias alias = method.getAnnotation(Alias.class);
- if (ObjectUtil.isNotNull(alias) && StrUtil.isNotBlank(alias.value())) {
- Method aliasMethod = annotationType.getMethod(alias.value());
- return ReflectUtil.invoke(annotation, aliasMethod);
- }
- return method.invoke(args);
- });
- return (T) o;
+ final T annotation = getAnnotation(annotationEle, annotationType);
+ return (T) Proxy.newProxyInstance(annotationType.getClassLoader(), new Class[]{annotationType}, new AnnotationProxy<>(annotation));
}
}
diff --git a/hutool-core/src/main/java/cn/hutool/core/util/ReflectUtil.java b/hutool-core/src/main/java/cn/hutool/core/util/ReflectUtil.java
index 43440bb321..de406bfbfd 100644
--- a/hutool-core/src/main/java/cn/hutool/core/util/ReflectUtil.java
+++ b/hutool-core/src/main/java/cn/hutool/core/util/ReflectUtil.java
@@ -386,7 +386,8 @@ public class ReflectUtil {
}
/**
- * 获得指定类过滤后的Public方法列表
+ * 获得指定类过滤后的Public方法列表
+ * TODO 6.x此方法更改返回Method[]
*
* @param clazz 查找方法的类
* @param filter 过滤器
--
Gitee
From ae8849c6651f0373cf92f13df09a9c485356e73e Mon Sep 17 00:00:00 2001
From: Looly
Date: Fri, 4 Mar 2022 00:09:56 +0800
Subject: [PATCH 09/57] change extName
---
CHANGELOG.md | 15 ++++++++-------
.../java/cn/hutool/core/io/file/FileNameUtil.java | 5 +++++
.../test/java/cn/hutool/core/io/FileUtilTest.java | 4 ++++
3 files changed, 17 insertions(+), 7 deletions(-)
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 2cf4af00a0..fcd3d51bd8 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -2,20 +2,21 @@
# 🚀Changelog
-------------------------------------------------------------------------------------------------------------
-# 5.7.23 (2022-03-03)
+# 5.7.23 (2022-03-04)
### 🐣新特性
-* 【http 】 HttpRequest.form采用TableMap方式(issue#I4W427@gitee)
-* 【core 】 AnnotationUtil增加getAnnotationAlias方法(pr#554@gitee)
+* 【http 】 HttpRequest.form采用TableMap方式(issue#I4W427@Gitee)
+* 【core 】 AnnotationUtil增加getAnnotationAlias方法(pr#554@Gitee)
+* 【core 】 FileUtil.extName增加对tar.gz特殊处理(issue#I4W5FS@Gitee)
### 🐞Bug修复
-* 【core 】 修复ObjectUtil.hasNull传入null返回true的问题(pr#555@gitee)
+* 【core 】 修复ObjectUtil.hasNull传入null返回true的问题(pr#555@Gitee)
-------------------------------------------------------------------------------------------------------------
# 5.7.22 (2022-03-01)
### 🐣新特性
-* 【poi 】 ExcelUtil.readBySax增加对POI-5.2.0的兼容性(issue#I4TJF4@gitee)
-* 【extra 】 Ftp增加构造(issue#I4TKXP@gitee)
+* 【poi 】 ExcelUtil.readBySax增加对POI-5.2.0的兼容性(issue#I4TJF4@Gitee)
+* 【extra 】 Ftp增加构造(issue#I4TKXP@Gitee)
* 【core 】 GenericBuilder支持Map构建(pr#540@Github)
* 【json 】 新增TemporalAccessorSerializer
* 【core 】 使多个xxxBuilder实现Builder接口,扩展CheckedUtil(pr#545@Gitee)
@@ -547,7 +548,7 @@
* 【json 】 增加JSONWriter
* 【core 】 IdUtil增加getWorkerId和getDataCenterId(issueI3Y5NI@Gitee)
* 【core 】 JWTValidator增加leeway重载
-* 【core 】 增加RegexPool(issue#I3W9ZF@gitee)
+* 【core 】 增加RegexPool(issue#I3W9ZF@Gitee)
### 🐞Bug修复
* 【json 】 修复XML转义字符的问题(issue#I3XH09@Gitee)
diff --git a/hutool-core/src/main/java/cn/hutool/core/io/file/FileNameUtil.java b/hutool-core/src/main/java/cn/hutool/core/io/file/FileNameUtil.java
index 6dca23e7d2..07436e1518 100644
--- a/hutool-core/src/main/java/cn/hutool/core/io/file/FileNameUtil.java
+++ b/hutool-core/src/main/java/cn/hutool/core/io/file/FileNameUtil.java
@@ -222,6 +222,11 @@ public class FileNameUtil {
if (index == -1) {
return StrUtil.EMPTY;
} else {
+ // issue#I4W5FS@Gitee
+ if(fileName.endsWith("tar.gz")){
+ return "tar.gz";
+ }
+
String ext = fileName.substring(index + 1);
// 扩展名中不能包含路径相关的符号
return StrUtil.containsAny(ext, UNIX_SEPARATOR, WINDOWS_SEPARATOR) ? StrUtil.EMPTY : ext;
diff --git a/hutool-core/src/test/java/cn/hutool/core/io/FileUtilTest.java b/hutool-core/src/test/java/cn/hutool/core/io/FileUtilTest.java
index d70490e8ba..4e72c827fb 100644
--- a/hutool-core/src/test/java/cn/hutool/core/io/FileUtilTest.java
+++ b/hutool-core/src/test/java/cn/hutool/core/io/FileUtilTest.java
@@ -392,6 +392,10 @@ public class FileUtilTest {
path = FileUtil.isWindows() ? "d:\\aaa\\bbb\\cc\\fff.xlsx" : "~/Desktop/hutool/fff.xlsx";
mainName = FileUtil.extName(path);
Assert.assertEquals("xlsx", mainName);
+
+ path = FileUtil.isWindows() ? "d:\\aaa\\bbb\\cc\\fff.tar.gz" : "~/Desktop/hutool/fff.tar.gz";
+ mainName = FileUtil.extName(path);
+ Assert.assertEquals("tar.gz", mainName);
}
@Test
--
Gitee
From 919dbca570536a57df14f08a3a05604bcd3b45ec Mon Sep 17 00:00:00 2001
From: jiazhengquan <2466896229@qq.com>
Date: Fri, 4 Mar 2022 11:13:39 +0800
Subject: [PATCH 10/57] =?UTF-8?q?=E9=92=88=E5=AF=B9issue#I4W5FS@Gitee?=
=?UTF-8?q?=E5=AD=98=E5=9C=A8=E5=85=B6=E4=BB=96=E7=89=B9=E6=AE=8A=E6=83=85?=
=?UTF-8?q?=E5=86=B5=E5=A4=84=E7=90=86?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.../java/cn/hutool/core/io/file/FileNameUtil.java | 11 +++++++++--
.../src/test/java/cn/hutool/core/io/FileUtilTest.java | 8 ++++++++
2 files changed, 17 insertions(+), 2 deletions(-)
diff --git a/hutool-core/src/main/java/cn/hutool/core/io/file/FileNameUtil.java b/hutool-core/src/main/java/cn/hutool/core/io/file/FileNameUtil.java
index 07436e1518..2d8ef52bba 100644
--- a/hutool-core/src/main/java/cn/hutool/core/io/file/FileNameUtil.java
+++ b/hutool-core/src/main/java/cn/hutool/core/io/file/FileNameUtil.java
@@ -42,6 +42,11 @@ public class FileNameUtil {
*/
private static final Pattern FILE_NAME_INVALID_PATTERN_WIN = Pattern.compile("[\\\\/:*?\"<>|]");
+ /**
+ * 特殊后缀
+ */
+ private static final CharSequence[] SPECIAL_SUFFIX = {"tar.bz2", "tar.Z", "tar.gz"};
+
// -------------------------------------------------------------------------------------------- name start
@@ -223,8 +228,10 @@ public class FileNameUtil {
return StrUtil.EMPTY;
} else {
// issue#I4W5FS@Gitee
- if(fileName.endsWith("tar.gz")){
- return "tar.gz";
+ int secondToLastIndex = fileName.substring(0, index).lastIndexOf(StrUtil.DOT);
+ String substr = fileName.substring(secondToLastIndex == -1 ? index : secondToLastIndex + 1);
+ if (StrUtil.containsAny(substr, SPECIAL_SUFFIX)) {
+ return substr;
}
String ext = fileName.substring(index + 1);
diff --git a/hutool-core/src/test/java/cn/hutool/core/io/FileUtilTest.java b/hutool-core/src/test/java/cn/hutool/core/io/FileUtilTest.java
index 4e72c827fb..4486757058 100644
--- a/hutool-core/src/test/java/cn/hutool/core/io/FileUtilTest.java
+++ b/hutool-core/src/test/java/cn/hutool/core/io/FileUtilTest.java
@@ -396,6 +396,14 @@ public class FileUtilTest {
path = FileUtil.isWindows() ? "d:\\aaa\\bbb\\cc\\fff.tar.gz" : "~/Desktop/hutool/fff.tar.gz";
mainName = FileUtil.extName(path);
Assert.assertEquals("tar.gz", mainName);
+
+ path = FileUtil.isWindows() ? "d:\\aaa\\bbb\\cc\\fff.tar.Z" : "~/Desktop/hutool/fff.tar.Z";
+ mainName = FileUtil.extName(path);
+ Assert.assertEquals("tar.Z", mainName);
+
+ path = FileUtil.isWindows() ? "d:\\aaa\\bbb\\cc\\fff.tar.bz2" : "~/Desktop/hutool/fff.tar.bz2";
+ mainName = FileUtil.extName(path);
+ Assert.assertEquals("tar.bz2", mainName);
}
@Test
--
Gitee
From 8f8abcd91846191b2983d057def0147cab5361f3 Mon Sep 17 00:00:00 2001
From: jiazhengquan <2466896229@qq.com>
Date: Fri, 4 Mar 2022 11:39:58 +0800
Subject: [PATCH 11/57] =?UTF-8?q?=E9=92=88=E5=AF=B9issue#I4W5FS@Gitee?=
=?UTF-8?q?=E5=AD=98=E5=9C=A8=E5=85=B6=E4=BB=96=E7=89=B9=E6=AE=8A=E6=83=85?=
=?UTF-8?q?=E5=86=B5=E5=A4=84=E7=90=86?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.../src/main/java/cn/hutool/core/io/file/FileNameUtil.java | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/hutool-core/src/main/java/cn/hutool/core/io/file/FileNameUtil.java b/hutool-core/src/main/java/cn/hutool/core/io/file/FileNameUtil.java
index 2d8ef52bba..e7f58b9a0a 100644
--- a/hutool-core/src/main/java/cn/hutool/core/io/file/FileNameUtil.java
+++ b/hutool-core/src/main/java/cn/hutool/core/io/file/FileNameUtil.java
@@ -45,7 +45,7 @@ public class FileNameUtil {
/**
* 特殊后缀
*/
- private static final CharSequence[] SPECIAL_SUFFIX = {"tar.bz2", "tar.Z", "tar.gz"};
+ private static final CharSequence[] SPECIAL_SUFFIX = {"tar.bz2", "tar.Z", "tar.gz", ".tar.xz"};
// -------------------------------------------------------------------------------------------- name start
--
Gitee
From 8dae97d691431fe9884d1d6918295c633074b9b8 Mon Sep 17 00:00:00 2001
From: jiazhengquan <2466896229@qq.com>
Date: Fri, 4 Mar 2022 14:25:32 +0800
Subject: [PATCH 12/57] =?UTF-8?q?=E9=92=88=E5=AF=B9issue#I4W5FS@Gitee?=
=?UTF-8?q?=E5=AD=98=E5=9C=A8=E5=85=B6=E4=BB=96=E7=89=B9=E6=AE=8A=E6=83=85?=
=?UTF-8?q?=E5=86=B5=E5=A4=84=E7=90=86?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.../src/main/java/cn/hutool/core/io/file/FileNameUtil.java | 2 +-
hutool-core/src/test/java/cn/hutool/core/io/FileUtilTest.java | 4 ++++
2 files changed, 5 insertions(+), 1 deletion(-)
diff --git a/hutool-core/src/main/java/cn/hutool/core/io/file/FileNameUtil.java b/hutool-core/src/main/java/cn/hutool/core/io/file/FileNameUtil.java
index e7f58b9a0a..5670f43832 100644
--- a/hutool-core/src/main/java/cn/hutool/core/io/file/FileNameUtil.java
+++ b/hutool-core/src/main/java/cn/hutool/core/io/file/FileNameUtil.java
@@ -45,7 +45,7 @@ public class FileNameUtil {
/**
* 特殊后缀
*/
- private static final CharSequence[] SPECIAL_SUFFIX = {"tar.bz2", "tar.Z", "tar.gz", ".tar.xz"};
+ private static final CharSequence[] SPECIAL_SUFFIX = {"tar.bz2", "tar.Z", "tar.gz", "tar.xz"};
// -------------------------------------------------------------------------------------------- name start
diff --git a/hutool-core/src/test/java/cn/hutool/core/io/FileUtilTest.java b/hutool-core/src/test/java/cn/hutool/core/io/FileUtilTest.java
index 4486757058..8a50a76413 100644
--- a/hutool-core/src/test/java/cn/hutool/core/io/FileUtilTest.java
+++ b/hutool-core/src/test/java/cn/hutool/core/io/FileUtilTest.java
@@ -404,6 +404,10 @@ public class FileUtilTest {
path = FileUtil.isWindows() ? "d:\\aaa\\bbb\\cc\\fff.tar.bz2" : "~/Desktop/hutool/fff.tar.bz2";
mainName = FileUtil.extName(path);
Assert.assertEquals("tar.bz2", mainName);
+
+ path = FileUtil.isWindows() ? "d:\\aaa\\bbb\\cc\\fff.tar.xz" : "~/Desktop/hutool/fff.tar.xz";
+ mainName = FileUtil.extName(path);
+ Assert.assertEquals("tar.xz", mainName);
}
@Test
--
Gitee
From 899f1384f6b3bcd62b626e4fdc46289b3d94dc43 Mon Sep 17 00:00:00 2001
From: Looly
Date: Sat, 5 Mar 2022 22:47:07 +0800
Subject: [PATCH 13/57] add test
---
.../hutool/crypto/test/symmetric/TEATest.java | 39 +++++++++++++++++++
1 file changed, 39 insertions(+)
create mode 100644 hutool-crypto/src/test/java/cn/hutool/crypto/test/symmetric/TEATest.java
diff --git a/hutool-crypto/src/test/java/cn/hutool/crypto/test/symmetric/TEATest.java b/hutool-crypto/src/test/java/cn/hutool/crypto/test/symmetric/TEATest.java
new file mode 100644
index 0000000000..8a9d008d6e
--- /dev/null
+++ b/hutool-crypto/src/test/java/cn/hutool/crypto/test/symmetric/TEATest.java
@@ -0,0 +1,39 @@
+package cn.hutool.crypto.test.symmetric;
+
+import cn.hutool.crypto.symmetric.SymmetricCrypto;
+import org.junit.Assert;
+import org.junit.Test;
+
+/**
+ * TEA(Tiny Encryption Algorithm)和 XTEA算法单元测试
+ */
+public class TEATest {
+
+ @Test
+ public void teaTest(){
+ String data = "测试的加密数据 by Hutool";
+
+ // 密钥必须为128bit
+ final SymmetricCrypto tea = new SymmetricCrypto("TEA", "MyPassword123456".getBytes());
+ final byte[] encrypt = tea.encrypt(data);
+
+ // 解密
+ final String decryptStr = tea.decryptStr(encrypt);
+
+ Assert.assertEquals(data, decryptStr);
+ }
+
+ @Test
+ public void xteaTest(){
+ String data = "测试的加密数据 by Hutool";
+
+ // 密钥必须为128bit
+ final SymmetricCrypto tea = new SymmetricCrypto("XTEA", "MyPassword123456".getBytes());
+ final byte[] encrypt = tea.encrypt(data);
+
+ // 解密
+ final String decryptStr = tea.decryptStr(encrypt);
+
+ Assert.assertEquals(data, decryptStr);
+ }
+}
--
Gitee
From 115871a355b1c7b212fe52b6c80d127dba14d263 Mon Sep 17 00:00:00 2001
From: VampireAchao
Date: Sat, 5 Mar 2022 23:43:27 +0800
Subject: [PATCH 14/57] =?UTF-8?q?=E4=BF=AE=E5=A4=8DAnnotationProxy#annotat?=
=?UTF-8?q?ionType=E8=8E=B7=E5=8F=96=E4=B8=8D=E5=88=B0=E6=B3=A8=E8=A7=A3?=
=?UTF-8?q?=E7=B1=BB=E5=9E=8B=E7=9A=84=E9=97=AE=E9=A2=98?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.../main/java/cn/hutool/core/annotation/AnnotationProxy.java | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/hutool-core/src/main/java/cn/hutool/core/annotation/AnnotationProxy.java b/hutool-core/src/main/java/cn/hutool/core/annotation/AnnotationProxy.java
index a61ff66681..bcf2ba4b58 100644
--- a/hutool-core/src/main/java/cn/hutool/core/annotation/AnnotationProxy.java
+++ b/hutool-core/src/main/java/cn/hutool/core/annotation/AnnotationProxy.java
@@ -39,7 +39,7 @@ public class AnnotationProxy implements Annotation, Invoca
@Override
public Class extends Annotation> annotationType() {
- return null;
+ return type;
}
@Override
--
Gitee
From 580e2e9fbe1d3cc43bdc7f6ebaa0f28c96344a05 Mon Sep 17 00:00:00 2001
From: Looly
Date: Sat, 5 Mar 2022 23:49:27 +0800
Subject: [PATCH 15/57] add XXTEA
---
CHANGELOG.md | 3 +-
.../cn/hutool/crypto/symmetric/XXTEA.java | 157 ++++++++++++++++++
.../hutool/crypto/test/symmetric/TEATest.java | 19 ++-
3 files changed, 176 insertions(+), 3 deletions(-)
create mode 100644 hutool-crypto/src/main/java/cn/hutool/crypto/symmetric/XXTEA.java
diff --git a/CHANGELOG.md b/CHANGELOG.md
index fcd3d51bd8..74943728c0 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -2,12 +2,13 @@
# 🚀Changelog
-------------------------------------------------------------------------------------------------------------
-# 5.7.23 (2022-03-04)
+# 5.7.23 (2022-03-05)
### 🐣新特性
* 【http 】 HttpRequest.form采用TableMap方式(issue#I4W427@Gitee)
* 【core 】 AnnotationUtil增加getAnnotationAlias方法(pr#554@Gitee)
* 【core 】 FileUtil.extName增加对tar.gz特殊处理(issue#I4W5FS@Gitee)
+* 【crypto 】 增加XXTEA实现(issue#I4WH2X@Gitee)
### 🐞Bug修复
* 【core 】 修复ObjectUtil.hasNull传入null返回true的问题(pr#555@Gitee)
diff --git a/hutool-crypto/src/main/java/cn/hutool/crypto/symmetric/XXTEA.java b/hutool-crypto/src/main/java/cn/hutool/crypto/symmetric/XXTEA.java
new file mode 100644
index 0000000000..4f948cd4f9
--- /dev/null
+++ b/hutool-crypto/src/main/java/cn/hutool/crypto/symmetric/XXTEA.java
@@ -0,0 +1,157 @@
+package cn.hutool.crypto.symmetric;
+
+import cn.hutool.core.io.IoUtil;
+
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.io.Serializable;
+
+/**
+ * XXTEA(Corrected Block Tiny Encryption Algorithm)算法实现
+ * 来自:https://github.com/xxtea/xxtea-java
+ *
+ * @author Ma Bingyao
+ */
+public class XXTEA implements SymmetricEncryptor, SymmetricDecryptor, Serializable {
+ private static final long serialVersionUID = 1L;
+
+ private static final int DELTA = 0x9E3779B9;
+
+ private final byte[] key;
+
+ /**
+ * 构造
+ *
+ * @param key 密钥,16位
+ */
+ public XXTEA(byte[] key) {
+ this.key = key;
+ }
+
+ @Override
+ public byte[] encrypt(byte[] data) {
+ if (data.length == 0) {
+ return data;
+ }
+ return toByteArray(encrypt(
+ toIntArray(data, true),
+ toIntArray(fixKey(key), false)), false);
+ }
+
+ @Override
+ public void encrypt(InputStream data, OutputStream out, boolean isClose) {
+ IoUtil.write(out, isClose, encrypt(IoUtil.readBytes(data)));
+ }
+
+ @Override
+ public byte[] decrypt(byte[] data) {
+ if (data.length == 0) {
+ return data;
+ }
+ return toByteArray(decrypt(
+ toIntArray(data, false),
+ toIntArray(fixKey(key), false)), true);
+ }
+
+ @Override
+ public void decrypt(InputStream data, OutputStream out, boolean isClose) {
+ IoUtil.write(out, isClose, decrypt(IoUtil.readBytes(data)));
+ }
+
+ //region Private Method
+ private static int[] encrypt(int[] v, int[] k) {
+ int n = v.length - 1;
+
+ if (n < 1) {
+ return v;
+ }
+ int p, q = 6 + 52 / (n + 1);
+ int z = v[n], y, sum = 0, e;
+
+ while (q-- > 0) {
+ sum = sum + DELTA;
+ e = sum >>> 2 & 3;
+ for (p = 0; p < n; p++) {
+ y = v[p + 1];
+ z = v[p] += mx(sum, y, z, p, e, k);
+ }
+ y = v[0];
+ z = v[n] += mx(sum, y, z, p, e, k);
+ }
+ return v;
+ }
+
+ private static int[] decrypt(int[] v, int[] k) {
+ int n = v.length - 1;
+
+ if (n < 1) {
+ return v;
+ }
+ int p, q = 6 + 52 / (n + 1);
+ int z, y = v[0], sum = q * DELTA, e;
+
+ while (sum != 0) {
+ e = sum >>> 2 & 3;
+ for (p = n; p > 0; p--) {
+ z = v[p - 1];
+ y = v[p] -= mx(sum, y, z, p, e, k);
+ }
+ z = v[n];
+ y = v[0] -= mx(sum, y, z, p, e, k);
+ sum = sum - DELTA;
+ }
+ return v;
+ }
+
+ private static int mx(int sum, int y, int z, int p, int e, int[] k) {
+ return (z >>> 5 ^ y << 2) + (y >>> 3 ^ z << 4) ^ (sum ^ y) + (k[p & 3 ^ e] ^ z);
+ }
+
+ private static byte[] fixKey(byte[] key) {
+ if (key.length == 16) {
+ return key;
+ }
+ byte[] fixedkey = new byte[16];
+ System.arraycopy(key, 0, fixedkey, 0, Math.min(key.length, 16));
+ return fixedkey;
+ }
+
+ private static int[] toIntArray(byte[] data, boolean includeLength) {
+ int n = (((data.length & 3) == 0)
+ ? (data.length >>> 2)
+ : ((data.length >>> 2) + 1));
+ int[] result;
+
+ if (includeLength) {
+ result = new int[n + 1];
+ result[n] = data.length;
+ } else {
+ result = new int[n];
+ }
+ n = data.length;
+ for (int i = 0; i < n; ++i) {
+ result[i >>> 2] |= (0x000000ff & data[i]) << ((i & 3) << 3);
+ }
+ return result;
+ }
+
+ private static byte[] toByteArray(int[] data, boolean includeLength) {
+ int n = data.length << 2;
+
+ if (includeLength) {
+ int m = data[data.length - 1];
+ n -= 4;
+ if ((m < n - 3) || (m > n)) {
+ return null;
+ }
+ n = m;
+ }
+ byte[] result = new byte[n];
+
+ for (int i = 0; i < n; ++i) {
+ result[i] = (byte) (data[i >>> 2] >>> ((i & 3) << 3));
+ }
+ return result;
+ }
+ //endregion
+}
diff --git a/hutool-crypto/src/test/java/cn/hutool/crypto/test/symmetric/TEATest.java b/hutool-crypto/src/test/java/cn/hutool/crypto/test/symmetric/TEATest.java
index 8a9d008d6e..53916926a2 100644
--- a/hutool-crypto/src/test/java/cn/hutool/crypto/test/symmetric/TEATest.java
+++ b/hutool-crypto/src/test/java/cn/hutool/crypto/test/symmetric/TEATest.java
@@ -1,6 +1,7 @@
package cn.hutool.crypto.test.symmetric;
import cn.hutool.crypto.symmetric.SymmetricCrypto;
+import cn.hutool.crypto.symmetric.XXTEA;
import org.junit.Assert;
import org.junit.Test;
@@ -10,7 +11,7 @@ import org.junit.Test;
public class TEATest {
@Test
- public void teaTest(){
+ public void teaTest() {
String data = "测试的加密数据 by Hutool";
// 密钥必须为128bit
@@ -24,7 +25,7 @@ public class TEATest {
}
@Test
- public void xteaTest(){
+ public void xteaTest() {
String data = "测试的加密数据 by Hutool";
// 密钥必须为128bit
@@ -36,4 +37,18 @@ public class TEATest {
Assert.assertEquals(data, decryptStr);
}
+
+ @Test
+ public void xxteaTest() {
+ String data = "测试的加密数据 by Hutool";
+
+ // 密钥必须为128bit
+ final XXTEA tea = new XXTEA("MyPassword123456".getBytes());
+ final byte[] encrypt = tea.encrypt(data);
+
+ // 解密
+ final String decryptStr = tea.decryptStr(encrypt);
+
+ Assert.assertEquals(data, decryptStr);
+ }
}
--
Gitee
From 4b22f7cc74c1ba6155228e745bd79c9a46b1eb9f Mon Sep 17 00:00:00 2001
From: Looly
Date: Sun, 6 Mar 2022 19:22:29 +0800
Subject: [PATCH 16/57] fix code
---
.../java/cn/hutool/script/ScriptRuntimeException.java | 8 ++++----
1 file changed, 4 insertions(+), 4 deletions(-)
diff --git a/hutool-script/src/main/java/cn/hutool/script/ScriptRuntimeException.java b/hutool-script/src/main/java/cn/hutool/script/ScriptRuntimeException.java
index c609ebbf7b..48b62e0854 100644
--- a/hutool-script/src/main/java/cn/hutool/script/ScriptRuntimeException.java
+++ b/hutool-script/src/main/java/cn/hutool/script/ScriptRuntimeException.java
@@ -42,11 +42,11 @@ public class ScriptRuntimeException extends RuntimeException {
}
/**
- * Creates a ScriptException with message, filename and linenumber to be used in error messages.
+ * Creates a {@code ScriptException} with message, filename and linenumber to be used in error messages.
*
* @param message The string to use in the message
- * @param fileName The file or resource name describing the location of a script error causing the ScriptException to be thrown.
- * @param lineNumber A line number describing the location of a script error causing the ScriptException to be thrown.
+ * @param fileName The file or resource name describing the location of a script error causing the {@code ScriptException} to be thrown.
+ * @param lineNumber A line number describing the location of a script error causing the {@code ScriptException} to be thrown.
*/
public ScriptRuntimeException(String message, String fileName, int lineNumber) {
super(message);
@@ -55,7 +55,7 @@ public class ScriptRuntimeException extends RuntimeException {
}
/**
- * ScriptException constructor specifying message, filename, line number and column number.
+ * {@code ScriptException} constructor specifying message, filename, line number and column number.
*
* @param message The message.
* @param fileName The filename
--
Gitee
From 1e113ef83f071485ec24fa5c0bff920f566e29bb Mon Sep 17 00:00:00 2001
From: Looly
Date: Sun, 6 Mar 2022 21:01:08 +0800
Subject: [PATCH 17/57] fix code
---
.../java/cn/hutool/core/bean/BeanUtil.java | 4 +-
.../hutool/core/map/CamelCaseLinkedMap.java | 20 +---------
.../java/cn/hutool/core/map/CamelCaseMap.java | 38 ++++++++++---------
.../core/map/CaseInsensitiveLinkedMap.java | 16 +-------
.../hutool/core/map/CaseInsensitiveMap.java | 31 ++++++++-------
.../core/map/CaseInsensitiveTreeMap.java | 18 +--------
.../java/cn/hutool/core/map/CustomKeyMap.java | 6 +--
.../hutool/core/map/FixedLinkedHashMap.java | 3 +-
.../java/cn/hutool/core/map/FuncKeyMap.java | 9 +++--
.../java/cn/hutool/core/map/MapBuilder.java | 13 ++++++-
.../java/cn/hutool/core/map/MapWrapper.java | 5 ---
.../cn/hutool/core/bean/BeanUtilTest.java | 16 ++++----
12 files changed, 75 insertions(+), 104 deletions(-)
diff --git a/hutool-core/src/main/java/cn/hutool/core/bean/BeanUtil.java b/hutool-core/src/main/java/cn/hutool/core/bean/BeanUtil.java
index b66b15f0df..84e7c59178 100644
--- a/hutool-core/src/main/java/cn/hutool/core/bean/BeanUtil.java
+++ b/hutool-core/src/main/java/cn/hutool/core/bean/BeanUtil.java
@@ -225,8 +225,8 @@ public class BeanUtil {
*/
private static Map internalGetPropertyDescriptorMap(Class> clazz, boolean ignoreCase) throws BeanException {
final PropertyDescriptor[] propertyDescriptors = getPropertyDescriptors(clazz);
- final Map map = ignoreCase ? new CaseInsensitiveMap<>(propertyDescriptors.length, 1)
- : new HashMap<>((int) (propertyDescriptors.length), 1);
+ final Map map = ignoreCase ? new CaseInsensitiveMap<>(propertyDescriptors.length, 1f)
+ : new HashMap<>(propertyDescriptors.length, 1);
for (PropertyDescriptor propertyDescriptor : propertyDescriptors) {
map.put(propertyDescriptor.getName(), propertyDescriptor);
diff --git a/hutool-core/src/main/java/cn/hutool/core/map/CamelCaseLinkedMap.java b/hutool-core/src/main/java/cn/hutool/core/map/CamelCaseLinkedMap.java
index fbd2b776ce..639bfbfccd 100644
--- a/hutool-core/src/main/java/cn/hutool/core/map/CamelCaseLinkedMap.java
+++ b/hutool-core/src/main/java/cn/hutool/core/map/CamelCaseLinkedMap.java
@@ -1,7 +1,5 @@
package cn.hutool.core.map;
-import cn.hutool.core.util.StrUtil;
-
import java.util.LinkedHashMap;
import java.util.Map;
@@ -15,7 +13,7 @@ import java.util.Map;
* @param 值类型
* @since 4.0.7
*/
-public class CamelCaseLinkedMap extends CustomKeyMap {
+public class CamelCaseLinkedMap extends CamelCaseMap {
private static final long serialVersionUID = 4043263744224569870L;
// ------------------------------------------------------------------------- Constructor start
@@ -48,7 +46,7 @@ public class CamelCaseLinkedMap extends CustomKeyMap {
* 构造
*
* @param loadFactor 加载因子
- * @param m Map
+ * @param m Map,数据会被默认拷贝到一个新的LinkedHashMap中
*/
public CamelCaseLinkedMap(float loadFactor, Map extends K, ? extends V> m) {
this(m.size(), loadFactor);
@@ -65,18 +63,4 @@ public class CamelCaseLinkedMap extends CustomKeyMap {
super(new LinkedHashMap<>(initialCapacity, loadFactor));
}
// ------------------------------------------------------------------------- Constructor end
-
- /**
- * 将Key转为驼峰风格,如果key为字符串的话
- *
- * @param key KEY
- * @return 驼峰Key
- */
- @Override
- protected Object customKey(Object key) {
- if (key instanceof CharSequence) {
- key = StrUtil.toCamelCase(key.toString());
- }
- return key;
- }
}
diff --git a/hutool-core/src/main/java/cn/hutool/core/map/CamelCaseMap.java b/hutool-core/src/main/java/cn/hutool/core/map/CamelCaseMap.java
index ea1642d141..4759b72028 100644
--- a/hutool-core/src/main/java/cn/hutool/core/map/CamelCaseMap.java
+++ b/hutool-core/src/main/java/cn/hutool/core/map/CamelCaseMap.java
@@ -1,24 +1,24 @@
package cn.hutool.core.map;
+import cn.hutool.core.util.StrUtil;
+
import java.util.HashMap;
import java.util.Map;
-import cn.hutool.core.util.StrUtil;
-
/**
* 驼峰Key风格的Map
* 对KEY转换为驼峰,get("int_value")和get("intValue")获得的值相同,put进入的值也会被覆盖
*
- * @author Looly
- *
* @param 键类型
* @param 值类型
+ * @author Looly
* @since 4.0.7
*/
-public class CamelCaseMap extends CustomKeyMap {
+public class CamelCaseMap extends FuncKeyMap {
private static final long serialVersionUID = 4043263744224569870L;
// ------------------------------------------------------------------------- Constructor start
+
/**
* 构造
*/
@@ -48,7 +48,7 @@ public class CamelCaseMap extends CustomKeyMap {
* 构造
*
* @param loadFactor 加载因子
- * @param m Map
+ * @param m 初始Map,数据会被默认拷贝到一个新的HashMap中
*/
public CamelCaseMap(float loadFactor, Map extends K, ? extends V> m) {
this(m.size(), loadFactor);
@@ -59,24 +59,26 @@ public class CamelCaseMap extends CustomKeyMap {
* 构造
*
* @param initialCapacity 初始大小
- * @param loadFactor 加载因子
+ * @param loadFactor 加载因子
*/
public CamelCaseMap(int initialCapacity, float loadFactor) {
- super(new HashMap<>(initialCapacity, loadFactor));
+ this(MapBuilder.create(new HashMap<>(initialCapacity, loadFactor)));
}
- // ------------------------------------------------------------------------- Constructor end
/**
- * 将Key转为驼峰风格,如果key为字符串的话
+ * 构造
+ * 注意此构造将传入的Map作为被包装的Map,针对任何修改,传入的Map都会被同样修改。
*
- * @param key KEY
- * @return 驼峰Key
+ * @param emptyMapBuilder Map构造器,必须构造空的Map
*/
- @Override
- protected Object customKey(Object key) {
- if (key instanceof CharSequence) {
- key = StrUtil.toCamelCase(key.toString());
- }
- return key;
+ CamelCaseMap(MapBuilder emptyMapBuilder) {
+ super(emptyMapBuilder.build(), (key) -> {
+ if (key instanceof CharSequence) {
+ key = StrUtil.toCamelCase(key.toString());
+ }
+ //noinspection unchecked
+ return (K) key;
+ });
}
+ // ------------------------------------------------------------------------- Constructor end
}
diff --git a/hutool-core/src/main/java/cn/hutool/core/map/CaseInsensitiveLinkedMap.java b/hutool-core/src/main/java/cn/hutool/core/map/CaseInsensitiveLinkedMap.java
index 51cdd2561a..5be588ef37 100644
--- a/hutool-core/src/main/java/cn/hutool/core/map/CaseInsensitiveLinkedMap.java
+++ b/hutool-core/src/main/java/cn/hutool/core/map/CaseInsensitiveLinkedMap.java
@@ -13,7 +13,7 @@ import java.util.Map;
* @param 值类型
* @since 3.3.1
*/
-public class CaseInsensitiveLinkedMap extends CustomKeyMap {
+public class CaseInsensitiveLinkedMap extends CaseInsensitiveMap {
private static final long serialVersionUID = 4043263744224569870L;
// ------------------------------------------------------------------------- Constructor start
@@ -64,18 +64,4 @@ public class CaseInsensitiveLinkedMap extends CustomKeyMap {
super(new LinkedHashMap<>(initialCapacity, loadFactor));
}
// ------------------------------------------------------------------------- Constructor end
-
- /**
- * 将Key转为小写
- *
- * @param key KEY
- * @return 小写KEY
- */
- @Override
- protected Object customKey(Object key) {
- if (key instanceof CharSequence) {
- key = key.toString().toLowerCase();
- }
- return key;
- }
}
diff --git a/hutool-core/src/main/java/cn/hutool/core/map/CaseInsensitiveMap.java b/hutool-core/src/main/java/cn/hutool/core/map/CaseInsensitiveMap.java
index 0c892b2680..40f805ad0d 100644
--- a/hutool-core/src/main/java/cn/hutool/core/map/CaseInsensitiveMap.java
+++ b/hutool-core/src/main/java/cn/hutool/core/map/CaseInsensitiveMap.java
@@ -13,7 +13,7 @@ import java.util.Map;
* @param 值类型
* @since 3.0.2
*/
-public class CaseInsensitiveMap extends CustomKeyMap {
+public class CaseInsensitiveMap extends FuncKeyMap {
private static final long serialVersionUID = 4043263744224569870L;
//------------------------------------------------------------------------- Constructor start
@@ -34,9 +34,10 @@ public class CaseInsensitiveMap extends CustomKeyMap {
}
/**
- * 构造
+ * 构造
+ * 注意此构造将传入的Map作为被包装的Map,针对任何修改,传入的Map都会被同样修改。
*
- * @param m Map
+ * @param m 被包装的自定义Map创建器
*/
public CaseInsensitiveMap(Map extends K, ? extends V> m) {
this(DEFAULT_LOAD_FACTOR, m);
@@ -61,21 +62,23 @@ public class CaseInsensitiveMap extends CustomKeyMap {
* @param loadFactor 加载因子
*/
public CaseInsensitiveMap(int initialCapacity, float loadFactor) {
- super(new HashMap<>(initialCapacity, loadFactor));
+ this(MapBuilder.create(new HashMap<>(initialCapacity, loadFactor)));
}
- //------------------------------------------------------------------------- Constructor end
/**
- * 将Key转为小写
+ * 构造
+ * 注意此构造将传入的Map作为被包装的Map,针对任何修改,传入的Map都会被同样修改。
*
- * @param key KEY
- * @return 小写KEY
+ * @param emptyMapBuilder 被包装的自定义Map创建器
*/
- @Override
- protected Object customKey(Object key) {
- if (key instanceof CharSequence) {
- key = key.toString().toLowerCase();
- }
- return key;
+ CaseInsensitiveMap(MapBuilder emptyMapBuilder) {
+ super(emptyMapBuilder.build(), (key)->{
+ if (key instanceof CharSequence) {
+ key = key.toString().toLowerCase();
+ }
+ //noinspection unchecked
+ return (K) key;
+ });
}
+ //------------------------------------------------------------------------- Constructor end
}
diff --git a/hutool-core/src/main/java/cn/hutool/core/map/CaseInsensitiveTreeMap.java b/hutool-core/src/main/java/cn/hutool/core/map/CaseInsensitiveTreeMap.java
index 7f8b065f63..69f3ec538f 100755
--- a/hutool-core/src/main/java/cn/hutool/core/map/CaseInsensitiveTreeMap.java
+++ b/hutool-core/src/main/java/cn/hutool/core/map/CaseInsensitiveTreeMap.java
@@ -15,7 +15,7 @@ import java.util.TreeMap;
* @param 值类型
* @since 3.3.1
*/
-public class CaseInsensitiveTreeMap extends CustomKeyMap {
+public class CaseInsensitiveTreeMap extends CaseInsensitiveMap {
private static final long serialVersionUID = 4043263744224569870L;
// ------------------------------------------------------------------------- Constructor start
@@ -40,7 +40,7 @@ public class CaseInsensitiveTreeMap extends CustomKeyMap {
/**
* 构造
*
- * @param m Map
+ * @param m Map,初始Map,键值对会被复制到新的TreeMap中
* @since 3.1.2
*/
public CaseInsensitiveTreeMap(SortedMap extends K, ? extends V> m) {
@@ -56,18 +56,4 @@ public class CaseInsensitiveTreeMap extends CustomKeyMap {
super(new TreeMap<>(comparator));
}
// ------------------------------------------------------------------------- Constructor end
-
- /**
- * 将Key转为小写
- *
- * @param key KEY
- * @return 小写KEY
- */
- @Override
- protected Object customKey(Object key) {
- if (key instanceof CharSequence) {
- key = key.toString().toLowerCase();
- }
- return key;
- }
}
diff --git a/hutool-core/src/main/java/cn/hutool/core/map/CustomKeyMap.java b/hutool-core/src/main/java/cn/hutool/core/map/CustomKeyMap.java
index 4584943a5e..04ed9d371e 100644
--- a/hutool-core/src/main/java/cn/hutool/core/map/CustomKeyMap.java
+++ b/hutool-core/src/main/java/cn/hutool/core/map/CustomKeyMap.java
@@ -18,11 +18,11 @@ public abstract class CustomKeyMap extends MapWrapper {
* 构造
* 通过传入一个Map从而确定Map的类型,子类需创建一个空的Map,而非传入一个已有Map,否则值可能会被修改
*
- * @param m Map 被包装的Map
+ * @param emptyMap Map 被包装的Map,必须为空Map,否则自定义key会无效
* @since 3.1.2
*/
- public CustomKeyMap(Map m) {
- super(m);
+ public CustomKeyMap(Map emptyMap) {
+ super(emptyMap);
}
@Override
diff --git a/hutool-core/src/main/java/cn/hutool/core/map/FixedLinkedHashMap.java b/hutool-core/src/main/java/cn/hutool/core/map/FixedLinkedHashMap.java
index 1559fde7a9..5ddaaa6f95 100644
--- a/hutool-core/src/main/java/cn/hutool/core/map/FixedLinkedHashMap.java
+++ b/hutool-core/src/main/java/cn/hutool/core/map/FixedLinkedHashMap.java
@@ -3,7 +3,8 @@ package cn.hutool.core.map;
import java.util.LinkedHashMap;
/**
- * 固定大小的{@link LinkedHashMap} 实现
+ * 固定大小的{@link LinkedHashMap} 实现
+ * 注意此类非线程安全,由于{@link #get(Object)}操作会修改链表的顺序结构,因此也不可以使用读写锁。
*
* @author looly
*
diff --git a/hutool-core/src/main/java/cn/hutool/core/map/FuncKeyMap.java b/hutool-core/src/main/java/cn/hutool/core/map/FuncKeyMap.java
index e5ca6a719a..0cc468d617 100644
--- a/hutool-core/src/main/java/cn/hutool/core/map/FuncKeyMap.java
+++ b/hutool-core/src/main/java/cn/hutool/core/map/FuncKeyMap.java
@@ -19,13 +19,14 @@ public class FuncKeyMap extends CustomKeyMap {
// ------------------------------------------------------------------------- Constructor start
/**
- * 构造
+ * 构造
+ * 注意提供的Map中不能有键值对,否则可能导致自定义key失效
*
- * @param m Map
+ * @param emptyMap Map,提供的空map
* @param keyFunc 自定义KEY的函数
*/
- public FuncKeyMap(Map m, Function keyFunc) {
- super(m);
+ public FuncKeyMap(Map emptyMap, Function keyFunc) {
+ super(emptyMap);
this.keyFunc = keyFunc;
}
// ------------------------------------------------------------------------- Constructor end
diff --git a/hutool-core/src/main/java/cn/hutool/core/map/MapBuilder.java b/hutool-core/src/main/java/cn/hutool/core/map/MapBuilder.java
index 5840d92042..2f605d71e0 100644
--- a/hutool-core/src/main/java/cn/hutool/core/map/MapBuilder.java
+++ b/hutool-core/src/main/java/cn/hutool/core/map/MapBuilder.java
@@ -13,7 +13,7 @@ import java.util.function.Supplier;
* @param Value类型
* @since 3.1.1
*/
-public class MapBuilder implements Builder> {
+public class MapBuilder implements Builder> {
private static final long serialVersionUID = 1L;
private final Map map;
@@ -120,6 +120,17 @@ public class MapBuilder implements Builder> {
return this;
}
+ /**
+ * 清空Map
+ *
+ * @return this
+ * @since 5.7.23
+ */
+ public MapBuilder clear() {
+ this.map.clear();
+ return this;
+ }
+
/**
* 创建后的map
*
diff --git a/hutool-core/src/main/java/cn/hutool/core/map/MapWrapper.java b/hutool-core/src/main/java/cn/hutool/core/map/MapWrapper.java
index bfd81dd86d..3c1ac59363 100644
--- a/hutool-core/src/main/java/cn/hutool/core/map/MapWrapper.java
+++ b/hutool-core/src/main/java/cn/hutool/core/map/MapWrapper.java
@@ -88,7 +88,6 @@ public class MapWrapper implements Map, Iterable>, S
}
@Override
- @SuppressWarnings("NullableProblems")
public void putAll(Map extends K, ? extends V> m) {
raw.putAll(m);
}
@@ -99,25 +98,21 @@ public class MapWrapper implements Map, Iterable>, S
}
@Override
- @SuppressWarnings("NullableProblems")
public Collection values() {
return raw.values();
}
@Override
- @SuppressWarnings("NullableProblems")
public Set keySet() {
return raw.keySet();
}
@Override
- @SuppressWarnings("NullableProblems")
public Set> entrySet() {
return raw.entrySet();
}
@Override
- @SuppressWarnings("NullableProblems")
public Iterator> iterator() {
return this.entrySet().iterator();
}
diff --git a/hutool-core/src/test/java/cn/hutool/core/bean/BeanUtilTest.java b/hutool-core/src/test/java/cn/hutool/core/bean/BeanUtilTest.java
index a01dc608be..f311903132 100644
--- a/hutool-core/src/test/java/cn/hutool/core/bean/BeanUtilTest.java
+++ b/hutool-core/src/test/java/cn/hutool/core/bean/BeanUtilTest.java
@@ -5,6 +5,7 @@ import cn.hutool.core.bean.copier.CopyOptions;
import cn.hutool.core.bean.copier.ValueProvider;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.collection.ListUtil;
+import cn.hutool.core.map.MapBuilder;
import cn.hutool.core.map.MapUtil;
import cn.hutool.core.thread.ThreadUtil;
import cn.hutool.core.util.ArrayUtil;
@@ -78,14 +79,15 @@ public class BeanUtilTest {
@Test
public void fillBeanWithMapIgnoreCaseTest() {
- HashMap map = MapUtil.newHashMap();
- map.put("Name", "Joe");
- map.put("aGe", 12);
- map.put("openId", "DFDFSDFWERWER");
+ Map map = MapBuilder.create()
+ .put("Name", "Joe")
+ .put("aGe", 12)
+ .put("openId", "DFDFSDFWERWER")
+ .build();
SubPerson person = BeanUtil.fillBeanWithMapIgnoreCase(map, new SubPerson(), false);
- Assert.assertEquals(person.getName(), "Joe");
- Assert.assertEquals(person.getAge(), 12);
- Assert.assertEquals(person.getOpenid(), "DFDFSDFWERWER");
+ Assert.assertEquals("Joe", person.getName());
+ Assert.assertEquals(12, person.getAge());
+ Assert.assertEquals("DFDFSDFWERWER", person.getOpenid());
}
@Test
--
Gitee
From 04dc6d2b730170d5b8ed68f830febea36eec4945 Mon Sep 17 00:00:00 2001
From: Looly
Date: Sun, 6 Mar 2022 21:08:15 +0800
Subject: [PATCH 18/57] fix code
---
hutool-core/src/main/java/cn/hutool/core/text/StrBuilder.java | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/hutool-core/src/main/java/cn/hutool/core/text/StrBuilder.java b/hutool-core/src/main/java/cn/hutool/core/text/StrBuilder.java
index a09dc06810..18a18bd92a 100644
--- a/hutool-core/src/main/java/cn/hutool/core/text/StrBuilder.java
+++ b/hutool-core/src/main/java/cn/hutool/core/text/StrBuilder.java
@@ -8,7 +8,8 @@ import java.io.Serializable;
import java.util.Arrays;
/**
- * 可复用的字符串生成器,非线程安全
+ * 可复用的字符串生成器,非线程安全
+ * TODO 6.x移除此类,java8的StringBuilder非常完善了,无需重写。
*
* @author Looly
* @since 4.0.0
--
Gitee
From 820db7fa3226683fc91311520aab87674fe82302 Mon Sep 17 00:00:00 2001
From: Looly
Date: Mon, 7 Mar 2022 02:39:05 +0800
Subject: [PATCH 19/57] addTable
---
.../cn/hutool/core/builder/EqualsBuilder.java | 122 ++++----
.../cn/hutool/core/map/multi/RowKeyTable.java | 272 ++++++++++++++++++
.../java/cn/hutool/core/map/multi/Table.java | 253 ++++++++++++++++
.../hutool/core/map/multi/package-info.java | 4 +-
4 files changed, 588 insertions(+), 63 deletions(-)
create mode 100644 hutool-core/src/main/java/cn/hutool/core/map/multi/RowKeyTable.java
create mode 100644 hutool-core/src/main/java/cn/hutool/core/map/multi/Table.java
diff --git a/hutool-core/src/main/java/cn/hutool/core/builder/EqualsBuilder.java b/hutool-core/src/main/java/cn/hutool/core/builder/EqualsBuilder.java
index bb7c7e46cd..e68ccdf387 100644
--- a/hutool-core/src/main/java/cn/hutool/core/builder/EqualsBuilder.java
+++ b/hutool-core/src/main/java/cn/hutool/core/builder/EqualsBuilder.java
@@ -70,7 +70,7 @@ public class EqualsBuilder implements Builder {
* Converters value pair into a register pair.
*
*
- * @param lhs this object
+ * @param lhs {@code this} object
* @param rhs the other object
* @return the pair
*/
@@ -82,15 +82,15 @@ public class EqualsBuilder implements Builder {
/**
*
- * Returns true if the registry contains the given object pair.
+ * Returns {@code true} if the registry contains the given object pair.
* Used by the reflection methods to avoid infinite loops.
* Objects might be swapped therefore a check is needed if the object pair
* is registered in given or swapped order.
*
*
- * @param lhs this object to lookup in registry
+ * @param lhs {@code this} object to lookup in registry
* @param rhs the other object to lookup on registry
- * @return boolean true if the registry contains the given object.
+ * @return boolean {@code true} if the registry contains the given object.
* @since 3.0
*/
static boolean isRegistered(final Object lhs, final Object rhs) {
@@ -108,7 +108,7 @@ public class EqualsBuilder implements Builder {
* Used by the reflection methods to avoid infinite loops.
*
*
- * @param lhs this object to register
+ * @param lhs {@code this} object to register
* @param rhs the other object to register
*/
static void register(final Object lhs, final Object rhs) {
@@ -131,7 +131,7 @@ public class EqualsBuilder implements Builder {
*
* Used by the reflection methods to avoid infinite loops.
*
- * @param lhs this object to unregister
+ * @param lhs {@code this} object to unregister
* @param rhs the other object to unregister
* @since 3.0
*/
@@ -170,7 +170,7 @@ public class EqualsBuilder implements Builder {
* @param lhs 此对象
* @param rhs 另一个对象
* @param excludeFields 排除的字段集合,如果有不参与计算equals的字段加入此集合即可
- * @return 两个对象是否equals,是返回true
+ * @return 两个对象是否equals,是返回{@code true}
*/
public static boolean reflectionEquals(final Object lhs, final Object rhs, final Collection excludeFields) {
return reflectionEquals(lhs, rhs, ArrayUtil.toArray(excludeFields, String.class));
@@ -182,62 +182,62 @@ public class EqualsBuilder implements Builder {
* @param lhs 此对象
* @param rhs 另一个对象
* @param excludeFields 排除的字段集合,如果有不参与计算equals的字段加入此集合即可
- * @return 两个对象是否equals,是返回true
+ * @return 两个对象是否equals,是返回{@code true}
*/
public static boolean reflectionEquals(final Object lhs, final Object rhs, final String... excludeFields) {
return reflectionEquals(lhs, rhs, false, null, excludeFields);
}
/**
- * This method uses reflection to determine if the two Objects
+ *
This method uses reflection to determine if the two {@code Object}s
* are equal.
*
- * It uses AccessibleObject.setAccessible to gain access to private
+ *
It uses {@code AccessibleObject.setAccessible} to gain access to private
* fields. This means that it will throw a security exception if run under
* a security manager, if the permissions are not set up correctly. It is also
* not as efficient as testing explicitly. Non-primitive fields are compared using
- * equals().
+ * {@code equals()}.
*
- * If the TestTransients parameter is set to true, transient
+ *
If the TestTransients parameter is set to {@code true}, transient
* members will be tested, otherwise they are ignored, as they are likely
- * derived fields, and not part of the value of the Object.
+ * derived fields, and not part of the value of the {@code Object}.
*
* Static fields will not be tested. Superclass fields will be included.
*
- * @param lhs this object
+ * @param lhs {@code this} object
* @param rhs the other object
* @param testTransients whether to include transient fields
- * @return true if the two Objects have tested equals.
+ * @return {@code true} if the two Objects have tested equals.
*/
public static boolean reflectionEquals(final Object lhs, final Object rhs, final boolean testTransients) {
return reflectionEquals(lhs, rhs, testTransients, null);
}
/**
- * This method uses reflection to determine if the two Objects
+ *
This method uses reflection to determine if the two {@code Object}s
* are equal.
*
- * It uses AccessibleObject.setAccessible to gain access to private
+ *
It uses {@code AccessibleObject.setAccessible} to gain access to private
* fields. This means that it will throw a security exception if run under
* a security manager, if the permissions are not set up correctly. It is also
* not as efficient as testing explicitly. Non-primitive fields are compared using
- * equals().
+ * {@code equals()}.
*
- * If the testTransients parameter is set to true, transient
+ *
If the testTransients parameter is set to {@code true}, transient
* members will be tested, otherwise they are ignored, as they are likely
- * derived fields, and not part of the value of the Object.
+ * derived fields, and not part of the value of the {@code Object}.
*
* Static fields will not be included. Superclass fields will be appended
* up to and including the specified superclass. A null superclass is treated
* as java.lang.Object.
*
- * @param lhs this object
+ * @param lhs {@code this} object
* @param rhs the other object
* @param testTransients whether to include transient fields
* @param reflectUpToClass the superclass to reflect up to (inclusive),
- * may be null
+ * may be {@code null}
* @param excludeFields array of field names to exclude from testing
- * @return true if the two Objects have tested equals.
+ * @return {@code true} if the two Objects have tested equals.
* @since 2.0
*/
public static boolean reflectionEquals(final Object lhs, final Object rhs, final boolean testTransients, final Class> reflectUpToClass,
@@ -343,9 +343,9 @@ public class EqualsBuilder implements Builder {
//-------------------------------------------------------------------------
/**
- * Adds the result of super.equals() to this builder.
+ * Adds the result of {@code super.equals()} to this builder.
*
- * @param superEquals the result of calling super.equals()
+ * @param superEquals the result of calling {@code super.equals()}
* @return EqualsBuilder - used to chain calls.
* @since 2.0
*/
@@ -360,8 +360,8 @@ public class EqualsBuilder implements Builder {
//-------------------------------------------------------------------------
/**
- * Test if two Objects are equal using their
- * equals method.
+ * Test if two {@code Object}s are equal using their
+ * {@code equals} method.
*
* @param lhs the left hand object
* @param rhs the right hand object
@@ -388,11 +388,11 @@ public class EqualsBuilder implements Builder {
/**
*
- * Test if two long s are equal.
+ * Test if two {@code long} s are equal.
*
*
- * @param lhs the left hand long
- * @param rhs the right hand long
+ * @param lhs the left hand {@code long}
+ * @param rhs the right hand {@code long}
* @return EqualsBuilder - used to chain calls.
*/
public EqualsBuilder append(final long lhs, final long rhs) {
@@ -404,10 +404,10 @@ public class EqualsBuilder implements Builder {
}
/**
- * Test if two ints are equal.
+ * Test if two {@code int}s are equal.
*
- * @param lhs the left hand int
- * @param rhs the right hand int
+ * @param lhs the left hand {@code int}
+ * @param rhs the right hand {@code int}
* @return EqualsBuilder - used to chain calls.
*/
public EqualsBuilder append(final int lhs, final int rhs) {
@@ -419,10 +419,10 @@ public class EqualsBuilder implements Builder {
}
/**
- * Test if two shorts are equal.
+ * Test if two {@code short}s are equal.
*
- * @param lhs the left hand short
- * @param rhs the right hand short
+ * @param lhs the left hand {@code short}
+ * @param rhs the right hand {@code short}
* @return EqualsBuilder - used to chain calls.
*/
public EqualsBuilder append(final short lhs, final short rhs) {
@@ -434,10 +434,10 @@ public class EqualsBuilder implements Builder {
}
/**
- * Test if two chars are equal.
+ * Test if two {@code char}s are equal.
*
- * @param lhs the left hand char
- * @param rhs the right hand char
+ * @param lhs the left hand {@code char}
+ * @param rhs the right hand {@code char}
* @return EqualsBuilder - used to chain calls.
*/
public EqualsBuilder append(final char lhs, final char rhs) {
@@ -449,10 +449,10 @@ public class EqualsBuilder implements Builder {
}
/**
- * Test if two bytes are equal.
+ * Test if two {@code byte}s are equal.
*
- * @param lhs the left hand byte
- * @param rhs the right hand byte
+ * @param lhs the left hand {@code byte}
+ * @param rhs the right hand {@code byte}
* @return EqualsBuilder - used to chain calls.
*/
public EqualsBuilder append(final byte lhs, final byte rhs) {
@@ -464,16 +464,16 @@ public class EqualsBuilder implements Builder {
}
/**
- * Test if two doubles are equal by testing that the
- * pattern of bits returned by doubleToLong are equal.
+ * Test if two {@code double}s are equal by testing that the
+ * pattern of bits returned by {@code doubleToLong} are equal.
*
- * This handles NaNs, Infinities, and -0.0.
+ * This handles NaNs, Infinities, and {@code -0.0}.
*
* It is compatible with the hash code generated by
- * HashCodeBuilder.
+ * {@code HashCodeBuilder}.
*
- * @param lhs the left hand double
- * @param rhs the right hand double
+ * @param lhs the left hand {@code double}
+ * @param rhs the right hand {@code double}
* @return EqualsBuilder - used to chain calls.
*/
public EqualsBuilder append(final double lhs, final double rhs) {
@@ -484,16 +484,16 @@ public class EqualsBuilder implements Builder {
}
/**
- * Test if two floats are equal byt testing that the
+ *
Test if two {@code float}s are equal byt testing that the
* pattern of bits returned by doubleToLong are equal.
*
- * This handles NaNs, Infinities, and -0.0.
+ * This handles NaNs, Infinities, and {@code -0.0}.
*
* It is compatible with the hash code generated by
- * HashCodeBuilder.
+ * {@code HashCodeBuilder}.
*
- * @param lhs the left hand float
- * @param rhs the right hand float
+ * @param lhs the left hand {@code float}
+ * @param rhs the right hand {@code float}
* @return EqualsBuilder - used to chain calls.
*/
public EqualsBuilder append(final float lhs, final float rhs) {
@@ -504,10 +504,10 @@ public class EqualsBuilder implements Builder {
}
/**
- * Test if two booleanss are equal.
+ * Test if two {@code booleans}s are equal.
*
- * @param lhs the left hand boolean
- * @param rhs the right hand boolean
+ * @param lhs the left hand {@code boolean}
+ * @param rhs the right hand {@code boolean}
* @return EqualsBuilder - used to chain calls.
*/
public EqualsBuilder append(final boolean lhs, final boolean rhs) {
@@ -519,7 +519,7 @@ public class EqualsBuilder implements Builder {
}
/**
- * Returns true if the fields that have been checked
+ *
Returns {@code true} if the fields that have been checked
* are all equal.
*
* @return boolean
@@ -529,11 +529,11 @@ public class EqualsBuilder implements Builder {
}
/**
- * Returns true if the fields that have been checked
+ *
Returns {@code true} if the fields that have been checked
* are all equal.
*
- * @return true if all of the fields that have been checked
- * are equal, false otherwise.
+ * @return {@code true} if all of the fields that have been checked
+ * are equal, {@code false} otherwise.
* @since 3.0
*/
@Override
@@ -542,7 +542,7 @@ public class EqualsBuilder implements Builder {
}
/**
- * Sets the isEquals value.
+ * Sets the {@code isEquals} value.
*
* @param isEquals The value to set.
* @return this
diff --git a/hutool-core/src/main/java/cn/hutool/core/map/multi/RowKeyTable.java b/hutool-core/src/main/java/cn/hutool/core/map/multi/RowKeyTable.java
new file mode 100644
index 0000000000..0e88548968
--- /dev/null
+++ b/hutool-core/src/main/java/cn/hutool/core/map/multi/RowKeyTable.java
@@ -0,0 +1,272 @@
+package cn.hutool.core.map.multi;
+
+import cn.hutool.core.collection.IterUtil;
+import cn.hutool.core.collection.TransIter;
+import cn.hutool.core.util.ObjectUtil;
+import com.sun.istack.internal.Nullable;
+
+import java.io.Serializable;
+import java.util.AbstractCollection;
+import java.util.AbstractSet;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.Objects;
+import java.util.Set;
+import java.util.function.Supplier;
+
+public class RowKeyTable implements Table {
+
+ final Map> raw;
+ final Supplier extends Map> supplier;
+
+ public RowKeyTable(Map> raw) {
+ this(raw, HashMap::new);
+ }
+
+ public RowKeyTable(Map> raw, Supplier extends Map> columnMapSupplier) {
+ this.raw = raw;
+ this.supplier = null == columnMapSupplier ? HashMap::new : columnMapSupplier;
+ }
+
+ @Override
+ public Map> rowMap() {
+ return raw;
+ }
+
+ @Override
+ public Map> columnMap() {
+ // TODO 实现columnMap
+ throw new UnsupportedOperationException("TODO implement this method");
+ }
+
+ @Override
+ public Collection values() {
+ return this.values;
+ }
+
+ @Override
+ public Set> cellSet() {
+ return this.cellSet;
+ }
+
+ @Override
+ public V put(R rowKey, C columnKey, V value) {
+ return raw.computeIfAbsent(rowKey, (key) -> supplier.get()).put(columnKey, value);
+ }
+
+ @Override
+ public void putAll(Table extends R, ? extends C, ? extends V> table) {
+ if (null != table) {
+ for (Table.Cell extends R, ? extends C, ? extends V> cell : table.cellSet()) {
+ put(cell.getRowKey(), cell.getColumnKey(), cell.getValue());
+ }
+ }
+ }
+
+ @Override
+ public V remove(R rowKey, C columnKey) {
+ final Map map = getRow(rowKey);
+ if (null == map) {
+ return null;
+ }
+ final V value = map.remove(columnKey);
+ if (map.isEmpty()) {
+ raw.remove(rowKey);
+ }
+ return value;
+ }
+
+ @Override
+ public boolean isEmpty() {
+ return raw.isEmpty();
+ }
+
+ @Override
+ public void clear() {
+ this.raw.clear();
+ }
+
+ @Override
+ public Iterator> iterator() {
+ return new CellIterator();
+ }
+
+ @Override
+ public boolean equals(@Nullable Object obj) {
+ if (obj == this) {
+ return true;
+ } else if (obj instanceof Table) {
+ final Table, ?, ?> that = (Table, ?, ?>) obj;
+ return this.cellSet().equals(that.cellSet());
+ } else {
+ return false;
+ }
+ }
+
+ @Override
+ public int hashCode() {
+ return cellSet().hashCode();
+ }
+
+ @Override
+ public String toString() {
+ return rowMap().toString();
+ }
+
+ /**
+ * 基于{@link Cell}的{@link Iterator}实现
+ */
+ private class CellIterator implements Iterator> {
+ final Iterator>> rowIterator = raw.entrySet().iterator();
+ Map.Entry> rowEntry;
+ Iterator> columnIterator = IterUtil.empty();
+
+ @Override
+ public boolean hasNext() {
+ return rowIterator.hasNext() || columnIterator.hasNext();
+ }
+
+ @Override
+ public Cell next() {
+ if (false == columnIterator.hasNext()) {
+ rowEntry = rowIterator.next();
+ columnIterator = rowEntry.getValue().entrySet().iterator();
+ }
+ final Map.Entry columnEntry = columnIterator.next();
+ return new SimpleCell<>(rowEntry.getKey(), columnEntry.getKey(), columnEntry.getValue());
+ }
+
+ @Override
+ public void remove() {
+ columnIterator.remove();
+ if (rowEntry.getValue().isEmpty()) {
+ rowIterator.remove();
+ }
+ }
+ }
+
+ /**
+ * 简单{@link Cell} 实现
+ *
+ * @param 行类型
+ * @param 列类型
+ * @param 值类型
+ */
+ private static class SimpleCell implements Cell, Serializable {
+ private static final long serialVersionUID = 1L;
+
+ private final R rowKey;
+ private final C columnKey;
+ private final V value;
+
+ SimpleCell(@Nullable R rowKey, @Nullable C columnKey, @Nullable V value) {
+ this.rowKey = rowKey;
+ this.columnKey = columnKey;
+ this.value = value;
+ }
+
+ @Override
+ public R getRowKey() {
+ return rowKey;
+ }
+
+ @Override
+ public C getColumnKey() {
+ return columnKey;
+ }
+
+ @Override
+ public V getValue() {
+ return value;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (obj == this) {
+ return true;
+ }
+ if (obj instanceof Cell) {
+ Cell, ?, ?> other = (Cell, ?, ?>) obj;
+ return ObjectUtil.equal(rowKey, other.getRowKey())
+ && ObjectUtil.equal(columnKey, other.getColumnKey())
+ && ObjectUtil.equal(value, other.getValue());
+ }
+ return false;
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(rowKey, columnKey, value);
+ }
+
+ @Override
+ public String toString() {
+ return "(" + rowKey + "," + columnKey + ")=" + value;
+ }
+ }
+
+ private final Collection values = new AbstractCollection() {
+ @Override
+ public Iterator iterator() {
+ return new TransIter<>(cellSet().iterator(), Cell::getValue);
+ }
+
+ @Override
+ public boolean contains(Object o) {
+ //noinspection unchecked
+ return containsValue((V) o);
+ }
+
+ @Override
+ public void clear() {
+ RowKeyTable.this.clear();
+ }
+
+ @Override
+ public int size() {
+ return RowKeyTable.this.size();
+ }
+ };
+
+ private final Set> cellSet = new AbstractSet>() {
+ @Override
+ public boolean contains(Object o) {
+ if (o instanceof Cell) {
+ @SuppressWarnings("unchecked") final
+ Cell cell = (Cell) o;
+ Map row = getRow(cell.getRowKey());
+ if (null != row) {
+ return ObjectUtil.equals(row.get(cell.getColumnKey()), cell.getValue());
+ }
+ }
+ return false;
+ }
+
+ @Override
+ public boolean remove(Object o) {
+ if (contains(o)) {
+ @SuppressWarnings("unchecked")
+ final Cell cell = (Cell) o;
+ RowKeyTable.this.remove(cell.getRowKey(), cell.getColumnKey());
+ }
+ return false;
+ }
+
+ @Override
+ public void clear() {
+ RowKeyTable.this.clear();
+ }
+
+ @Override
+ public Iterator> iterator() {
+ return new CellIterator();
+ }
+
+ @Override
+ public int size() {
+ return RowKeyTable.this.size();
+ }
+ };
+}
diff --git a/hutool-core/src/main/java/cn/hutool/core/map/multi/Table.java b/hutool-core/src/main/java/cn/hutool/core/map/multi/Table.java
new file mode 100644
index 0000000000..cd14029a5b
--- /dev/null
+++ b/hutool-core/src/main/java/cn/hutool/core/map/multi/Table.java
@@ -0,0 +1,253 @@
+package cn.hutool.core.map.multi;
+
+import cn.hutool.core.lang.Opt;
+import cn.hutool.core.lang.func.Consumer3;
+import cn.hutool.core.map.MapUtil;
+
+import java.util.Collection;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * 表格数据结构定义
+ * 此结构类似于Guava的Table接口,使用两个键映射到一个值,类似于表格结构。
+ *
+ * @param 行键类型
+ * @param 列键类型
+ * @param 值类型
+ * @since 5.7.23
+ */
+public interface Table extends Iterable> {
+
+ /**
+ * 是否包含指定行列的映射
+ * 行和列任意一个不存在都会返回{@code false},如果行和列都存在,值为{@code null},也会返回{@code true}
+ *
+ * @param rowKey 行键
+ * @param columnKey 列键
+ * @return 是否包含映射
+ */
+ default boolean contains(R rowKey, C columnKey) {
+ return Opt.ofNullable(getRow(rowKey)).map((map) -> map.containsKey(columnKey)).get();
+ }
+
+ //region Row
+
+ /**
+ * 行是否存在
+ *
+ * @param rowKey 行键
+ * @return 行是否存在
+ */
+ default boolean containsRow(R rowKey) {
+ return Opt.ofNullable(rowMap()).map((map) -> map.containsKey(rowKey)).get();
+ }
+
+ /**
+ * 获取行
+ *
+ * @param rowKey 行键
+ * @return 行映射,返回的键为列键,值为表格的值
+ */
+ default Map getRow(R rowKey) {
+ return Opt.ofNullable(rowMap()).map((map) -> map.get(rowKey)).get();
+ }
+
+ /**
+ * 返回所有行的key,行的key不可重复
+ *
+ * @return 行键
+ */
+ default Set rowKeySet() {
+ return Opt.ofNullable(rowMap()).map(Map::keySet).get();
+ }
+
+ /**
+ * 返回行列对应的Map
+ *
+ * @return map,键为行键,值为列和值的对应map
+ */
+ Map> rowMap();
+ //endregion
+
+ //region Column
+
+ /**
+ * 列是否存在
+ *
+ * @param columnKey 列键
+ * @return 列是否存在
+ */
+ default boolean containsColumn(C columnKey) {
+ return Opt.ofNullable(columnMap()).map((map) -> map.containsKey(columnKey)).get();
+ }
+
+ /**
+ * 获取列
+ *
+ * @param columnKey 列键
+ * @return 列映射,返回的键为行键,值为表格的值
+ */
+ default Map getColumn(C columnKey) {
+ return Opt.ofNullable(columnMap()).map((map) -> map.get(columnKey)).get();
+ }
+
+ /**
+ * 返回所有列的key,列的key不可重复
+ *
+ * @return 列set
+ */
+ default Set columnKeySet() {
+ return Opt.ofNullable(columnMap()).map(Map::keySet).get();
+ }
+
+ /**
+ * 返回列-行对应的map
+ *
+ * @return map,键为列键,值为行和值的对应map
+ */
+ Map> columnMap();
+ //endregion
+
+ //region value
+
+ /**
+ * 指定值是否存在
+ *
+ * @param value 值
+ * @return 值
+ */
+ default boolean containsValue(V value){
+ final Collection> rows = Opt.ofNullable(rowMap()).map(Map::values).get();
+ if(null != rows){
+ for (Map row : rows) {
+ if (row.containsValue(value)) {
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+
+ /**
+ * 获取指定值
+ *
+ * @param rowKey 行键
+ * @param columnKey 列键
+ * @return 值,如果值不存在,返回{@code null}
+ */
+ default V get(R rowKey, C columnKey) {
+ return Opt.ofNullable(getRow(rowKey)).map((map) -> map.get(columnKey)).get();
+ }
+
+ /**
+ * 所有行列值的集合
+ *
+ * @return 值的集合
+ */
+ Collection values();
+ //endregion
+
+ /**
+ * 所有单元格集合
+ *
+ * @return 单元格集合
+ */
+ Set> cellSet();
+
+ /**
+ * 为表格指定行列赋值,如果不存在,创建之,存在则替换之,返回原值
+ *
+ * @param rowKey 行键
+ * @param columnKey 列键
+ * @param value 值
+ * @return 原值,不存在返回{@code null}
+ */
+ V put(R rowKey, C columnKey, V value);
+
+ /**
+ * 批量加入
+ *
+ * @param table 其他table
+ */
+ void putAll(Table extends R, ? extends C, ? extends V> table);
+
+ /**
+ * 移除指定值
+ *
+ * @param rowKey 行键
+ * @param columnKey 列键
+ * @return 移除的值,如果值不存在,返回{@code null}
+ */
+ V remove(R rowKey, C columnKey);
+
+ /**
+ * 表格是否为空
+ *
+ * @return 是否为空
+ */
+ boolean isEmpty();
+
+ /**
+ * 表格大小,一般为单元格的个数
+ *
+ * @return 表格大小
+ */
+ default int size(){
+ final Map> rowMap = rowMap();
+ if(MapUtil.isEmpty(rowMap)){
+ return 0;
+ }
+ int size = 0;
+ for (Map map : rowMap.values()) {
+ size += map.size();
+ }
+ return size;
+ }
+
+ /**
+ * 清空表格
+ */
+ void clear();
+
+ /**
+ * 遍历表格的单元格,处理值
+ *
+ * @param consumer 单元格值处理器
+ */
+ default void forEach(Consumer3 super R, ? super C, ? super V> consumer) {
+ for (Cell cell : this) {
+ consumer.accept(cell.getRowKey(), cell.getColumnKey(), cell.getValue());
+ }
+ }
+
+ /**
+ * 单元格,用于表示一个单元格的行、列和值
+ *
+ * @param 行键类型
+ * @param 列键类型
+ * @param 值类型
+ */
+ interface Cell {
+ /**
+ * 获取行键
+ *
+ * @return 行键
+ */
+ R getRowKey();
+
+ /**
+ * 获取列键
+ *
+ * @return 列键
+ */
+ C getColumnKey();
+
+ /**
+ * 获取值
+ *
+ * @return 值
+ */
+ V getValue();
+ }
+}
diff --git a/hutool-core/src/main/java/cn/hutool/core/map/multi/package-info.java b/hutool-core/src/main/java/cn/hutool/core/map/multi/package-info.java
index b0a2bcd20c..2bf5b685f3 100644
--- a/hutool-core/src/main/java/cn/hutool/core/map/multi/package-info.java
+++ b/hutool-core/src/main/java/cn/hutool/core/map/multi/package-info.java
@@ -1,7 +1,7 @@
/**
- * 列表类型值的Map实现
+ * 多参数类型的Map实现,包括集合类型值的Map和Table
*
* @author looly
*
*/
-package cn.hutool.core.map.multi;
\ No newline at end of file
+package cn.hutool.core.map.multi;
--
Gitee
From 01af68cd0d68561a1f46f856d4ff5dc5a95c20c9 Mon Sep 17 00:00:00 2001
From: Looly
Date: Tue, 8 Mar 2022 01:15:13 +0800
Subject: [PATCH 20/57] add Table
---
CHANGELOG.md | 4 +-
.../cn/hutool/core/collection/IterUtil.java | 15 +
.../java/cn/hutool/core/map/AbsEntry.java | 46 +++
.../java/cn/hutool/core/map/SimpleEntry.java | 31 ++
.../java/cn/hutool/core/map/TableMap.java | 49 +--
.../cn/hutool/core/map/multi/AbsTable.java | 236 +++++++++++++
.../cn/hutool/core/map/multi/RowKeyTable.java | 325 +++++++++---------
.../java/cn/hutool/core/map/multi/Table.java | 8 +-
.../cn/hutool/core/map/RowKeyTableTest.java | 39 +++
9 files changed, 535 insertions(+), 218 deletions(-)
create mode 100644 hutool-core/src/main/java/cn/hutool/core/map/AbsEntry.java
create mode 100644 hutool-core/src/main/java/cn/hutool/core/map/SimpleEntry.java
create mode 100644 hutool-core/src/main/java/cn/hutool/core/map/multi/AbsTable.java
create mode 100644 hutool-core/src/test/java/cn/hutool/core/map/RowKeyTableTest.java
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 74943728c0..4c9993b9fa 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -2,13 +2,15 @@
# 🚀Changelog
-------------------------------------------------------------------------------------------------------------
-# 5.7.23 (2022-03-05)
+# 5.7.23 (2022-03-08)
### 🐣新特性
* 【http 】 HttpRequest.form采用TableMap方式(issue#I4W427@Gitee)
* 【core 】 AnnotationUtil增加getAnnotationAlias方法(pr#554@Gitee)
* 【core 】 FileUtil.extName增加对tar.gz特殊处理(issue#I4W5FS@Gitee)
* 【crypto 】 增加XXTEA实现(issue#I4WH2X@Gitee)
+* 【core 】 增加Table实现(issue#2179@Github)
+*
### 🐞Bug修复
* 【core 】 修复ObjectUtil.hasNull传入null返回true的问题(pr#555@Gitee)
diff --git a/hutool-core/src/main/java/cn/hutool/core/collection/IterUtil.java b/hutool-core/src/main/java/cn/hutool/core/collection/IterUtil.java
index f03451bc32..2634eefae2 100644
--- a/hutool-core/src/main/java/cn/hutool/core/collection/IterUtil.java
+++ b/hutool-core/src/main/java/cn/hutool/core/collection/IterUtil.java
@@ -906,4 +906,19 @@ public class IterUtil {
// 当两个Iterable长度不一致时返回false
return false == (it1.hasNext() || it2.hasNext());
}
+
+ /**
+ * 清空指定{@link Iterator},此方法遍历后调用{@link Iterator#remove()}移除每个元素
+ *
+ * @param iterator {@link Iterator}
+ * @since 5.7.23
+ */
+ public static void clear(Iterator> iterator) {
+ if (null != iterator) {
+ while (iterator.hasNext()) {
+ iterator.next();
+ iterator.remove();
+ }
+ }
+ }
}
diff --git a/hutool-core/src/main/java/cn/hutool/core/map/AbsEntry.java b/hutool-core/src/main/java/cn/hutool/core/map/AbsEntry.java
new file mode 100644
index 0000000000..a2ab5885fe
--- /dev/null
+++ b/hutool-core/src/main/java/cn/hutool/core/map/AbsEntry.java
@@ -0,0 +1,46 @@
+package cn.hutool.core.map;
+
+import cn.hutool.core.util.ObjectUtil;
+
+import java.util.Map;
+
+/**
+ * 抽象的{@link Map.Entry}实现,来自Guava
+ * 实现了默认的{@link #equals(Object)}、{@link #hashCode()}、{@link #toString()}方法。
+ * 默认{@link #setValue(Object)}抛出异常。
+ *
+ * @param 键类型
+ * @param 值类型
+ * @author Guava
+ * @since 5.7.23
+ */
+public abstract class AbsEntry implements Map.Entry {
+
+ @Override
+ public V setValue(V value) {
+ throw new UnsupportedOperationException("Entry is read only.");
+ }
+
+ @Override
+ public boolean equals(Object object) {
+ if (object instanceof Map.Entry) {
+ final Map.Entry, ?> that = (Map.Entry, ?>) object;
+ return ObjectUtil.equals(this.getKey(), that.getKey())
+ && ObjectUtil.equals(this.getValue(), that.getValue());
+ }
+ return false;
+ }
+
+ @Override
+ public int hashCode() {
+ //copy from 1.8 HashMap.Node
+ K k = getKey();
+ V v = getValue();
+ return ((k == null) ? 0 : k.hashCode()) ^ ((v == null) ? 0 : v.hashCode());
+ }
+
+ @Override
+ public String toString() {
+ return getKey() + "=" + getValue();
+ }
+}
diff --git a/hutool-core/src/main/java/cn/hutool/core/map/SimpleEntry.java b/hutool-core/src/main/java/cn/hutool/core/map/SimpleEntry.java
new file mode 100644
index 0000000000..e414636c0f
--- /dev/null
+++ b/hutool-core/src/main/java/cn/hutool/core/map/SimpleEntry.java
@@ -0,0 +1,31 @@
+package cn.hutool.core.map;
+
+/**
+ * {@link java.util.Map.Entry}简单实现。
+ * 键值对使用不可变字段表示。
+ *
+ * @param 键类型
+ * @param 值类型
+ * @author looly
+ * @since 5.7.23
+ */
+public class SimpleEntry extends AbsEntry {
+
+ private final K key;
+ private final V value;
+
+ public SimpleEntry(K key, V value) {
+ this.key = key;
+ this.value = value;
+ }
+
+ @Override
+ public K getKey() {
+ return key;
+ }
+
+ @Override
+ public V getValue() {
+ return value;
+ }
+}
diff --git a/hutool-core/src/main/java/cn/hutool/core/map/TableMap.java b/hutool-core/src/main/java/cn/hutool/core/map/TableMap.java
index 596ec51605..783f1e98a3 100644
--- a/hutool-core/src/main/java/cn/hutool/core/map/TableMap.java
+++ b/hutool-core/src/main/java/cn/hutool/core/map/TableMap.java
@@ -13,7 +13,6 @@ import java.util.Iterator;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
-import java.util.Objects;
import java.util.Set;
/**
@@ -173,7 +172,7 @@ public class TableMap implements Map, Iterable>, Ser
public Set> entrySet() {
final Set> hashSet = new LinkedHashSet<>();
for (int i = 0; i < size(); i++) {
- hashSet.add(new Entry<>(keys.get(i), values.get(i)));
+ hashSet.add(new SimpleEntry<>(keys.get(i), values.get(i)));
}
return hashSet;
}
@@ -191,7 +190,7 @@ public class TableMap implements Map, Iterable>, Ser
@Override
public Map.Entry next() {
- return new Entry<>(keysIter.next(), valuesIter.next());
+ return new SimpleEntry<>(keysIter.next(), valuesIter.next());
}
@Override
@@ -209,48 +208,4 @@ public class TableMap implements Map, Iterable>, Ser
", values=" + values +
'}';
}
-
- private static class Entry implements Map.Entry {
-
- private final K key;
- private final V value;
-
- public Entry(K key, V value) {
- this.key = key;
- this.value = value;
- }
-
- @Override
- public K getKey() {
- return key;
- }
-
- @Override
- public V getValue() {
- return value;
- }
-
- @Override
- public V setValue(V value) {
- throw new UnsupportedOperationException("setValue not supported.");
- }
-
- @Override
- public final boolean equals(Object o) {
- if (o == this)
- return true;
- if (o instanceof Map.Entry) {
- Map.Entry, ?> e = (Map.Entry, ?>) o;
- return Objects.equals(key, e.getKey()) &&
- Objects.equals(value, e.getValue());
- }
- return false;
- }
-
- @Override
- public int hashCode() {
- //copy from 1.8 HashMap.Node
- return Objects.hashCode(key) ^ Objects.hashCode(value);
- }
- }
}
diff --git a/hutool-core/src/main/java/cn/hutool/core/map/multi/AbsTable.java b/hutool-core/src/main/java/cn/hutool/core/map/multi/AbsTable.java
new file mode 100644
index 0000000000..a98e402867
--- /dev/null
+++ b/hutool-core/src/main/java/cn/hutool/core/map/multi/AbsTable.java
@@ -0,0 +1,236 @@
+package cn.hutool.core.map.multi;
+
+import cn.hutool.core.collection.IterUtil;
+import cn.hutool.core.collection.TransIter;
+import cn.hutool.core.util.ObjectUtil;
+
+import java.io.Serializable;
+import java.util.AbstractCollection;
+import java.util.AbstractSet;
+import java.util.Collection;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.Objects;
+import java.util.Set;
+
+/**
+ * 抽象{@link Table}接口实现
+ * 默认实现了:
+ *
+ * {@link #equals(Object)}
+ * {@link #hashCode()}
+ * {@link #toString()}
+ * {@link #values()}
+ * {@link #cellSet()}
+ * {@link #iterator()}
+ *
+ *
+ * @param 行类型
+ * @param 列类型
+ * @param 值类型
+ * @author Guava, Looly
+ * @since 5.7.23
+ */
+public abstract class AbsTable implements Table {
+
+ @Override
+ public boolean equals(Object obj) {
+ if (obj == this) {
+ return true;
+ } else if (obj instanceof Table) {
+ final Table, ?, ?> that = (Table, ?, ?>) obj;
+ return this.cellSet().equals(that.cellSet());
+ } else {
+ return false;
+ }
+ }
+
+ @Override
+ public int hashCode() {
+ return cellSet().hashCode();
+ }
+
+ @Override
+ public String toString() {
+ return rowMap().toString();
+ }
+
+ //region values
+ @Override
+ public Collection values() {
+ Collection result = values;
+ return (result == null) ? values = new Values() : result;
+ }
+
+ private Collection values;
+ private class Values extends AbstractCollection {
+ @Override
+ public Iterator iterator() {
+ return new TransIter<>(cellSet().iterator(), Cell::getValue);
+ }
+
+ @Override
+ public boolean contains(Object o) {
+ //noinspection unchecked
+ return containsValue((V) o);
+ }
+
+ @Override
+ public void clear() {
+ AbsTable.this.clear();
+ }
+
+ @Override
+ public int size() {
+ return AbsTable.this.size();
+ }
+ }
+ //endregion
+
+ //region cellSet
+ @Override
+ public Set> cellSet() {
+ Set> result = cellSet;
+ return (result == null) ? cellSet = new CellSet() : result;
+ }
+
+ private Set> cellSet;
+
+ private class CellSet extends AbstractSet> {
+ @Override
+ public boolean contains(Object o) {
+ if (o instanceof Cell) {
+ @SuppressWarnings("unchecked") final Cell cell = (Cell) o;
+ Map row = getRow(cell.getRowKey());
+ if (null != row) {
+ return ObjectUtil.equals(row.get(cell.getColumnKey()), cell.getValue());
+ }
+ }
+ return false;
+ }
+
+ @Override
+ public boolean remove(Object o) {
+ if (contains(o)) {
+ @SuppressWarnings("unchecked") final Cell cell = (Cell) o;
+ AbsTable.this.remove(cell.getRowKey(), cell.getColumnKey());
+ }
+ return false;
+ }
+
+ @Override
+ public void clear() {
+ AbsTable.this.clear();
+ }
+
+ @Override
+ public Iterator> iterator() {
+ return new AbsTable.CellIterator();
+ }
+
+ @Override
+ public int size() {
+ return AbsTable.this.size();
+ }
+ }
+ //endregion
+
+ //region iterator
+ @Override
+ public Iterator> iterator() {
+ return new CellIterator();
+ }
+
+ /**
+ * 基于{@link Cell}的{@link Iterator}实现
+ */
+ private class CellIterator implements Iterator> {
+ final Iterator>> rowIterator = rowMap().entrySet().iterator();
+ Map.Entry> rowEntry;
+ Iterator> columnIterator = IterUtil.empty();
+
+ @Override
+ public boolean hasNext() {
+ return rowIterator.hasNext() || columnIterator.hasNext();
+ }
+
+ @Override
+ public Cell next() {
+ if (false == columnIterator.hasNext()) {
+ rowEntry = rowIterator.next();
+ columnIterator = rowEntry.getValue().entrySet().iterator();
+ }
+ final Map.Entry columnEntry = columnIterator.next();
+ return new SimpleCell<>(rowEntry.getKey(), columnEntry.getKey(), columnEntry.getValue());
+ }
+
+ @Override
+ public void remove() {
+ columnIterator.remove();
+ if (rowEntry.getValue().isEmpty()) {
+ rowIterator.remove();
+ }
+ }
+ }
+ //endregion
+
+ /**
+ * 简单{@link Cell} 实现
+ *
+ * @param 行类型
+ * @param 列类型
+ * @param 值类型
+ */
+ private static class SimpleCell implements Cell, Serializable {
+ private static final long serialVersionUID = 1L;
+
+ private final R rowKey;
+ private final C columnKey;
+ private final V value;
+
+ SimpleCell(R rowKey, C columnKey, V value) {
+ this.rowKey = rowKey;
+ this.columnKey = columnKey;
+ this.value = value;
+ }
+
+ @Override
+ public R getRowKey() {
+ return rowKey;
+ }
+
+ @Override
+ public C getColumnKey() {
+ return columnKey;
+ }
+
+ @Override
+ public V getValue() {
+ return value;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (obj == this) {
+ return true;
+ }
+ if (obj instanceof Cell) {
+ Cell, ?, ?> other = (Cell, ?, ?>) obj;
+ return ObjectUtil.equal(rowKey, other.getRowKey())
+ && ObjectUtil.equal(columnKey, other.getColumnKey())
+ && ObjectUtil.equal(value, other.getValue());
+ }
+ return false;
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(rowKey, columnKey, value);
+ }
+
+ @Override
+ public String toString() {
+ return "(" + rowKey + "," + columnKey + ")=" + value;
+ }
+ }
+}
diff --git a/hutool-core/src/main/java/cn/hutool/core/map/multi/RowKeyTable.java b/hutool-core/src/main/java/cn/hutool/core/map/multi/RowKeyTable.java
index 0e88548968..6094300ac2 100644
--- a/hutool-core/src/main/java/cn/hutool/core/map/multi/RowKeyTable.java
+++ b/hutool-core/src/main/java/cn/hutool/core/map/multi/RowKeyTable.java
@@ -1,68 +1,75 @@
package cn.hutool.core.map.multi;
+import cn.hutool.core.builder.Builder;
+import cn.hutool.core.collection.ComputeIter;
import cn.hutool.core.collection.IterUtil;
import cn.hutool.core.collection.TransIter;
-import cn.hutool.core.util.ObjectUtil;
-import com.sun.istack.internal.Nullable;
+import cn.hutool.core.map.AbsEntry;
+import cn.hutool.core.map.SimpleEntry;
-import java.io.Serializable;
-import java.util.AbstractCollection;
+import java.util.AbstractMap;
import java.util.AbstractSet;
-import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
-import java.util.Objects;
import java.util.Set;
-import java.util.function.Supplier;
-public class RowKeyTable implements Table {
+/**
+ * 将行的键作为主键的{@link Table}实现
+ * 此结构为: 行=(列=值)
+ *
+ * @param 行类型
+ * @param 列类型
+ * @param 值类型
+ * @author Guava, Looly
+ * @since 5.7.23
+ */
+public class RowKeyTable extends AbsTable {
final Map> raw;
- final Supplier extends Map> supplier;
+ /**
+ * 列的Map创建器,用于定义Table中Value对应Map类型
+ */
+ final Builder extends Map> columnBuilder;
+
+ //region 构造
+
+ /**
+ * 构造
+ */
+ public RowKeyTable() {
+ this(new HashMap<>());
+ }
+ /**
+ * 构造
+ *
+ * @param raw 原始Map
+ */
public RowKeyTable(Map> raw) {
this(raw, HashMap::new);
}
- public RowKeyTable(Map> raw, Supplier extends Map> columnMapSupplier) {
+ /**
+ * 构造
+ *
+ * @param raw 原始Map
+ * @param columnMapBuilder 列的map创建器
+ */
+ public RowKeyTable(Map> raw, Builder extends Map> columnMapBuilder) {
this.raw = raw;
- this.supplier = null == columnMapSupplier ? HashMap::new : columnMapSupplier;
+ this.columnBuilder = null == columnMapBuilder ? HashMap::new : columnMapBuilder;
}
+ //endregion
@Override
public Map> rowMap() {
return raw;
}
- @Override
- public Map> columnMap() {
- // TODO 实现columnMap
- throw new UnsupportedOperationException("TODO implement this method");
- }
-
- @Override
- public Collection values() {
- return this.values;
- }
-
- @Override
- public Set> cellSet() {
- return this.cellSet;
- }
-
@Override
public V put(R rowKey, C columnKey, V value) {
- return raw.computeIfAbsent(rowKey, (key) -> supplier.get()).put(columnKey, value);
- }
-
- @Override
- public void putAll(Table extends R, ? extends C, ? extends V> table) {
- if (null != table) {
- for (Table.Cell extends R, ? extends C, ? extends V> cell : table.cellSet()) {
- put(cell.getRowKey(), cell.getColumnKey(), cell.getValue());
- }
- }
+ return raw.computeIfAbsent(rowKey, (key) -> columnBuilder.build()).put(columnKey, value);
}
@Override
@@ -89,184 +96,164 @@ public class RowKeyTable implements Table {
}
@Override
- public Iterator> iterator() {
- return new CellIterator();
- }
-
- @Override
- public boolean equals(@Nullable Object obj) {
- if (obj == this) {
- return true;
- } else if (obj instanceof Table) {
- final Table, ?, ?> that = (Table, ?, ?>) obj;
- return this.cellSet().equals(that.cellSet());
- } else {
+ public boolean containsColumn(C columnKey) {
+ if (columnKey == null) {
return false;
}
+ for (Map map : raw.values()) {
+ if (null != map && map.containsKey(columnKey)) {
+ return true;
+ }
+ }
+ return false;
}
+ //region columnMap
@Override
- public int hashCode() {
- return cellSet().hashCode();
- }
-
- @Override
- public String toString() {
- return rowMap().toString();
+ public Map> columnMap() {
+ Map> result = columnMap;
+ return (result == null) ? columnMap = new ColumnMap() : result;
}
- /**
- * 基于{@link Cell}的{@link Iterator}实现
- */
- private class CellIterator implements Iterator> {
- final Iterator>> rowIterator = raw.entrySet().iterator();
- Map.Entry> rowEntry;
- Iterator> columnIterator = IterUtil.empty();
+ private Map> columnMap;
+ private class ColumnMap extends AbstractMap> {
@Override
- public boolean hasNext() {
- return rowIterator.hasNext() || columnIterator.hasNext();
+ public Set>> entrySet() {
+ return new ColumnMapEntrySet();
}
+ }
+
+ private class ColumnMapEntrySet extends AbstractSet>> {
+ private final Set columnKeySet = columnKeySet();
@Override
- public Cell next() {
- if (false == columnIterator.hasNext()) {
- rowEntry = rowIterator.next();
- columnIterator = rowEntry.getValue().entrySet().iterator();
- }
- final Map.Entry columnEntry = columnIterator.next();
- return new SimpleCell<>(rowEntry.getKey(), columnEntry.getKey(), columnEntry.getValue());
+ public Iterator>> iterator() {
+ return new TransIter<>(columnKeySet.iterator(),
+ c -> new SimpleEntry<>(c, getColumn(c)));
}
@Override
- public void remove() {
- columnIterator.remove();
- if (rowEntry.getValue().isEmpty()) {
- rowIterator.remove();
- }
+ public int size() {
+ return columnKeySet.size();
}
}
+ //endregion
- /**
- * 简单{@link Cell} 实现
- *
- * @param 行类型
- * @param 列类型
- * @param 值类型
- */
- private static class SimpleCell implements Cell, Serializable {
- private static final long serialVersionUID = 1L;
- private final R rowKey;
- private final C columnKey;
- private final V value;
+ //region columnKeySet
+ @Override
+ public Set columnKeySet() {
+ Set result = columnKeySet;
+ return (result == null) ? columnKeySet = new ColumnKeySet() : result;
+ }
- SimpleCell(@Nullable R rowKey, @Nullable C columnKey, @Nullable V value) {
- this.rowKey = rowKey;
- this.columnKey = columnKey;
- this.value = value;
- }
+ private Set columnKeySet;
- @Override
- public R getRowKey() {
- return rowKey;
- }
+ private class ColumnKeySet extends AbstractSet {
@Override
- public C getColumnKey() {
- return columnKey;
+ public Iterator iterator() {
+ return new ColumnKeyIterator();
}
@Override
- public V getValue() {
- return value;
+ public int size() {
+ return IterUtil.size(iterator());
}
+ }
+
+ private class ColumnKeyIterator extends ComputeIter {
+ final Map seen = columnBuilder.build();
+ final Iterator> mapIterator = raw.values().iterator();
+ Iterator> entryIterator = IterUtil.empty();
@Override
- public boolean equals(Object obj) {
- if (obj == this) {
- return true;
- }
- if (obj instanceof Cell) {
- Cell, ?, ?> other = (Cell, ?, ?>) obj;
- return ObjectUtil.equal(rowKey, other.getRowKey())
- && ObjectUtil.equal(columnKey, other.getColumnKey())
- && ObjectUtil.equal(value, other.getValue());
+ protected C computeNext() {
+ while (true) {
+ if (entryIterator.hasNext()) {
+ Map.Entry entry = entryIterator.next();
+ if (false == seen.containsKey(entry.getKey())) {
+ seen.put(entry.getKey(), entry.getValue());
+ return entry.getKey();
+ }
+ } else if (mapIterator.hasNext()) {
+ entryIterator = mapIterator.next().entrySet().iterator();
+ } else {
+ return null;
+ }
}
- return false;
}
+ }
+ //endregion
- @Override
- public int hashCode() {
- return Objects.hash(rowKey, columnKey, value);
- }
+ //region getColumn
- @Override
- public String toString() {
- return "(" + rowKey + "," + columnKey + ")=" + value;
- }
+ @Override
+ public Map getColumn(C columnKey) {
+ return new Column(columnKey);
}
- private final Collection values = new AbstractCollection() {
- @Override
- public Iterator iterator() {
- return new TransIter<>(cellSet().iterator(), Cell::getValue);
- }
+ private class Column extends AbstractMap {
+ final C columnKey;
- @Override
- public boolean contains(Object o) {
- //noinspection unchecked
- return containsValue((V) o);
+ Column(C columnKey) {
+ this.columnKey = columnKey;
}
@Override
- public void clear() {
- RowKeyTable.this.clear();
+ public Set> entrySet() {
+ return new EntrySet();
}
- @Override
- public int size() {
- return RowKeyTable.this.size();
- }
- };
+ private class EntrySet extends AbstractSet> {
- private final Set> cellSet = new AbstractSet>() {
- @Override
- public boolean contains(Object o) {
- if (o instanceof Cell) {
- @SuppressWarnings("unchecked") final
- Cell cell = (Cell) o;
- Map row = getRow(cell.getRowKey());
- if (null != row) {
- return ObjectUtil.equals(row.get(cell.getColumnKey()), cell.getValue());
- }
+ @Override
+ public Iterator> iterator() {
+ return new EntrySetIterator();
}
- return false;
- }
- @Override
- public boolean remove(Object o) {
- if (contains(o)) {
- @SuppressWarnings("unchecked")
- final Cell cell = (Cell) o;
- RowKeyTable.this.remove(cell.getRowKey(), cell.getColumnKey());
+ @Override
+ public int size() {
+ int size = 0;
+ for (Map map : raw.values()) {
+ if (map.containsKey(columnKey)) {
+ size++;
+ }
+ }
+ return size;
}
- return false;
}
- @Override
- public void clear() {
- RowKeyTable.this.clear();
- }
-
- @Override
- public Iterator> iterator() {
- return new CellIterator();
- }
-
- @Override
- public int size() {
- return RowKeyTable.this.size();
+ private class EntrySetIterator extends ComputeIter> {
+ final Iterator>> iterator = raw.entrySet().iterator();
+
+ @Override
+ protected Entry computeNext() {
+ while (iterator.hasNext()) {
+ final Entry> entry = iterator.next();
+ if (entry.getValue().containsKey(columnKey)) {
+ return new AbsEntry() {
+ @Override
+ public R getKey() {
+ return entry.getKey();
+ }
+
+ @Override
+ public V getValue() {
+ return entry.getValue().get(columnKey);
+ }
+
+ @Override
+ public V setValue(V value) {
+ return entry.getValue().put(columnKey, value);
+ }
+ };
+ }
+ }
+ return null;
+ }
}
- };
+ }
+ //endregion
}
diff --git a/hutool-core/src/main/java/cn/hutool/core/map/multi/Table.java b/hutool-core/src/main/java/cn/hutool/core/map/multi/Table.java
index cd14029a5b..e0fb1c5f4f 100644
--- a/hutool-core/src/main/java/cn/hutool/core/map/multi/Table.java
+++ b/hutool-core/src/main/java/cn/hutool/core/map/multi/Table.java
@@ -170,7 +170,13 @@ public interface Table extends Iterable> {
*
* @param table 其他table
*/
- void putAll(Table extends R, ? extends C, ? extends V> table);
+ default void putAll(Table extends R, ? extends C, ? extends V> table){
+ if (null != table) {
+ for (Table.Cell extends R, ? extends C, ? extends V> cell : table.cellSet()) {
+ put(cell.getRowKey(), cell.getColumnKey(), cell.getValue());
+ }
+ }
+ }
/**
* 移除指定值
diff --git a/hutool-core/src/test/java/cn/hutool/core/map/RowKeyTableTest.java b/hutool-core/src/test/java/cn/hutool/core/map/RowKeyTableTest.java
new file mode 100644
index 0000000000..fdbaca3986
--- /dev/null
+++ b/hutool-core/src/test/java/cn/hutool/core/map/RowKeyTableTest.java
@@ -0,0 +1,39 @@
+package cn.hutool.core.map;
+
+import cn.hutool.core.map.multi.RowKeyTable;
+import cn.hutool.core.map.multi.Table;
+import org.junit.Assert;
+import org.junit.Test;
+
+import java.util.Map;
+
+public class RowKeyTableTest {
+
+ @Test
+ public void putGetTest(){
+ final Table table = new RowKeyTable<>();
+ table.put(1, 2, 3);
+ table.put(1, 6, 4);
+
+ Assert.assertEquals(new Integer(3), table.get(1, 2));
+ Assert.assertNull(table.get(1, 3));
+
+ //判断row和column确定的二维点是否存在
+ Assert.assertTrue(table.contains(1, 2));
+ Assert.assertFalse(table.contains(1, 3));
+
+ //判断列
+ Assert.assertTrue(table.containsColumn(2));
+ Assert.assertFalse(table.containsColumn(3));
+
+ // 判断行
+ Assert.assertTrue(table.containsRow(1));
+ Assert.assertFalse(table.containsRow(2));
+
+
+ // 获取列
+ Map column = table.getColumn(6);
+ Assert.assertEquals(1, column.size());
+ Assert.assertEquals(new Integer(4), column.get(1));
+ }
+}
--
Gitee
From 2cfb33af9eeac7869e180c496ac1c427f77fa903 Mon Sep 17 00:00:00 2001
From: jiazhengquan <2466896229@qq.com>
Date: Tue, 8 Mar 2022 18:01:07 +0800
Subject: [PATCH 21/57] =?UTF-8?q?=E9=92=88=E5=AF=B9issue#I4WUWR=E5=88=9D?=
=?UTF-8?q?=E7=89=88=E4=BF=AE=E5=A4=8D?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.../java/cn/hutool/core/util/ReflectUtil.java | 33 ++++++++++++++-----
1 file changed, 25 insertions(+), 8 deletions(-)
diff --git a/hutool-core/src/main/java/cn/hutool/core/util/ReflectUtil.java b/hutool-core/src/main/java/cn/hutool/core/util/ReflectUtil.java
index de406bfbfd..4f9ded9d2c 100644
--- a/hutool-core/src/main/java/cn/hutool/core/util/ReflectUtil.java
+++ b/hutool-core/src/main/java/cn/hutool/core/util/ReflectUtil.java
@@ -663,18 +663,35 @@ public class ReflectUtil {
Assert.notNull(beanClass);
Method[] allMethods = null;
+ Class>[] searchInterfaces = null, parentInterfaces = null;
Class> searchType = beanClass;
Method[] declaredMethods;
- while (searchType != null) {
- declaredMethods = searchType.getDeclaredMethods();
- if (null == allMethods) {
- allMethods = declaredMethods;
- } else {
- allMethods = ArrayUtil.append(allMethods, declaredMethods);
+ while (searchType != null || searchInterfaces != null) {
+ if (searchType != null) {
+ declaredMethods = searchType.getDeclaredMethods();
+ if (null == allMethods) {
+ allMethods = declaredMethods;
+ } else {
+ allMethods = ArrayUtil.append(allMethods, declaredMethods);
+ }
+ Class>[] interfaces = searchType.getInterfaces();
+ for (Class> element : interfaces) {
+ allMethods = ArrayUtil.append(allMethods, element.getDeclaredMethods());
+ parentInterfaces = ArrayUtil.addAll(element.getInterfaces());
+ }
+ searchInterfaces = parentInterfaces;
+ Class>[] classes = searchInterfaces.length == 0 ? null : searchInterfaces;
+ searchInterfaces = withSuperClassMethods ? classes : null;
+ searchType = withSuperClassMethods ? searchType.getSuperclass() : null;
+ }
+ if (searchInterfaces != null) {
+ for (Class> searchInterface : searchInterfaces) {
+ allMethods = ArrayUtil.append(allMethods, searchInterface.getDeclaredMethods());
+ parentInterfaces = ArrayUtil.addAll(searchInterface.getInterfaces());
+ }
+ searchInterfaces = parentInterfaces.length == 0 ? null : parentInterfaces;
}
- searchType = withSuperClassMethods ? searchType.getSuperclass() : null;
}
-
return allMethods;
}
--
Gitee
From 5eac491cd4427a15b39efed4eaa3f00a5a9c5cfc Mon Sep 17 00:00:00 2001
From: Looly
Date: Tue, 8 Mar 2022 19:00:27 +0800
Subject: [PATCH 22/57] fix code
---
.../java/cn/hutool/core/convert/NumberChineseFormatter.java | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/hutool-core/src/main/java/cn/hutool/core/convert/NumberChineseFormatter.java b/hutool-core/src/main/java/cn/hutool/core/convert/NumberChineseFormatter.java
index d6da1d22b5..2a315d38dc 100644
--- a/hutool-core/src/main/java/cn/hutool/core/convert/NumberChineseFormatter.java
+++ b/hutool-core/src/main/java/cn/hutool/core/convert/NumberChineseFormatter.java
@@ -188,7 +188,7 @@ public class NumberChineseFormatter {
Assert.checkBetween(amount, -999, 999, "Number support only: (-999 ~ 999)!");
final String chinese = thousandToChinese(amount, isUseTraditional);
- if(amount < 20 && amount > 10){
+ if(amount < 20 && amount >= 10){
// "十一"而非"一十一"
return chinese.substring(1);
}
--
Gitee
From 8a8242409748029b52c250e1c0951c0ff30ff423 Mon Sep 17 00:00:00 2001
From: Looly
Date: Tue, 8 Mar 2022 19:21:07 +0800
Subject: [PATCH 23/57] fix convert bug
---
CHANGELOG.md | 3 ++-
.../core/convert/ConverterRegistry.java | 4 ++++
.../core/convert/impl/NumberConverter.java | 4 ++--
.../cn/hutool/core/convert/ConvertTest.java | 22 +++++++++++++++++++
4 files changed, 30 insertions(+), 3 deletions(-)
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 74943728c0..b7b36fb559 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -2,7 +2,7 @@
# 🚀Changelog
-------------------------------------------------------------------------------------------------------------
-# 5.7.23 (2022-03-05)
+# 5.7.23 (2022-03-08)
### 🐣新特性
* 【http 】 HttpRequest.form采用TableMap方式(issue#I4W427@Gitee)
@@ -11,6 +11,7 @@
* 【crypto 】 增加XXTEA实现(issue#I4WH2X@Gitee)
### 🐞Bug修复
* 【core 】 修复ObjectUtil.hasNull传入null返回true的问题(pr#555@Gitee)
+* 【core 】 修复NumberConverter对数字转换的问题(issue#I4WPF4@Gitee)
-------------------------------------------------------------------------------------------------------------
# 5.7.22 (2022-03-01)
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 96b19c52f0..bbc1fd3470 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
@@ -77,6 +77,8 @@ import java.util.concurrent.atomic.AtomicIntegerArray;
import java.util.concurrent.atomic.AtomicLong;
import java.util.concurrent.atomic.AtomicLongArray;
import java.util.concurrent.atomic.AtomicReference;
+import java.util.concurrent.atomic.DoubleAdder;
+import java.util.concurrent.atomic.LongAdder;
/**
* 转换器登记中心
@@ -389,11 +391,13 @@ public class ConverterRegistry implements Serializable {
defaultConverterMap.put(Integer.class, new NumberConverter(Integer.class));
defaultConverterMap.put(AtomicInteger.class, new NumberConverter(AtomicInteger.class));// since 3.0.8
defaultConverterMap.put(Long.class, new NumberConverter(Long.class));
+ defaultConverterMap.put(LongAdder.class, new NumberConverter(LongAdder.class));
defaultConverterMap.put(AtomicLong.class, new NumberConverter(AtomicLong.class));// since 3.0.8
defaultConverterMap.put(Byte.class, new NumberConverter(Byte.class));
defaultConverterMap.put(Short.class, new NumberConverter(Short.class));
defaultConverterMap.put(Float.class, new NumberConverter(Float.class));
defaultConverterMap.put(Double.class, new NumberConverter(Double.class));
+ defaultConverterMap.put(DoubleAdder.class, new NumberConverter(DoubleAdder.class));
defaultConverterMap.put(Character.class, new CharacterConverter());
defaultConverterMap.put(Boolean.class, new BooleanConverter());
defaultConverterMap.put(AtomicBoolean.class, new AtomicBooleanConverter());// since 3.0.8
diff --git a/hutool-core/src/main/java/cn/hutool/core/convert/impl/NumberConverter.java b/hutool-core/src/main/java/cn/hutool/core/convert/impl/NumberConverter.java
index 36242cab9e..421628c91e 100644
--- a/hutool-core/src/main/java/cn/hutool/core/convert/impl/NumberConverter.java
+++ b/hutool-core/src/main/java/cn/hutool/core/convert/impl/NumberConverter.java
@@ -186,7 +186,7 @@ public class NumberConverter extends AbstractConverter {
return StrUtil.isBlank(valueStr) ? null : NumberUtil.parseFloat(valueStr);
} else if (Double.class == targetType) {
if (value instanceof Number) {
- return ((Number) value).doubleValue();
+ return NumberUtil.toDouble((Number) value);
} else if (value instanceof Boolean) {
return BooleanUtil.toDoubleObj((Boolean) value);
}
@@ -194,7 +194,7 @@ public class NumberConverter extends AbstractConverter {
return StrUtil.isBlank(valueStr) ? null : NumberUtil.parseDouble(valueStr);
} else if (DoubleAdder.class == targetType) {
//jdk8 新增
- final Number number = convert(value, Long.class, toStrFunc);
+ final Number number = convert(value, Double.class, toStrFunc);
if (null != number) {
final DoubleAdder doubleAdder = new DoubleAdder();
doubleAdder.add(number.doubleValue());
diff --git a/hutool-core/src/test/java/cn/hutool/core/convert/ConvertTest.java b/hutool-core/src/test/java/cn/hutool/core/convert/ConvertTest.java
index 402140e680..18cd78b144 100644
--- a/hutool-core/src/test/java/cn/hutool/core/convert/ConvertTest.java
+++ b/hutool-core/src/test/java/cn/hutool/core/convert/ConvertTest.java
@@ -24,6 +24,7 @@ import java.util.Map;
import java.util.Set;
import java.util.concurrent.atomic.AtomicIntegerArray;
import java.util.concurrent.atomic.AtomicLongArray;
+import java.util.concurrent.atomic.DoubleAdder;
/**
* 类型转换工具单元测试
@@ -361,4 +362,25 @@ public class ConvertTest {
final float f = Convert.toFloat(value);
Assert.assertEquals(406.1F, f, 2);
}
+
+ @Test
+ public void floatToDoubleTest(){
+ float a = 0.45f;
+ double b = Convert.toDouble(a);
+ Assert.assertEquals(a, b, 5);
+ }
+
+ @Test
+ public void floatToDoubleAddrTest(){
+ float a = 0.45f;
+ final DoubleAdder adder = Convert.convert(DoubleAdder.class, a);
+ Assert.assertEquals(a, adder.doubleValue(), 5);
+ }
+
+ @Test
+ public void doubleToFloatTest(){
+ double a = 0.45f;
+ float b = Convert.toFloat(a);
+ Assert.assertEquals(a, b, 5);
+ }
}
--
Gitee
From 9229eea85f77432dca67c8703efc48fd2e4466d5 Mon Sep 17 00:00:00 2001
From: Looly
Date: Tue, 8 Mar 2022 20:52:43 +0800
Subject: [PATCH 24/57] fix bug
---
CHANGELOG.md | 1 +
.../cn/hutool/core/util/ModifierUtil.java | 72 ++++++++++++++-----
.../java/cn/hutool/core/util/ReflectUtil.java | 20 ++++--
.../core/lang/test/bean/ExamInfoDict.java | 50 ++-----------
.../cn/hutool/core/util/ReflectUtilTest.java | 63 +++++++++++++++-
5 files changed, 134 insertions(+), 72 deletions(-)
diff --git a/CHANGELOG.md b/CHANGELOG.md
index dd037ca02d..3fd0cf5e63 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -14,6 +14,7 @@
### 🐞Bug修复
* 【core 】 修复ObjectUtil.hasNull传入null返回true的问题(pr#555@Gitee)
* 【core 】 修复NumberConverter对数字转换的问题(issue#I4WPF4@Gitee)
+* 【core 】 修复ReflectUtil.getMethods获取接口方法问题(issue#I4WUWR@Gitee)
-------------------------------------------------------------------------------------------------------------
# 5.7.22 (2022-03-01)
diff --git a/hutool-core/src/main/java/cn/hutool/core/util/ModifierUtil.java b/hutool-core/src/main/java/cn/hutool/core/util/ModifierUtil.java
index f2b3f389ff..dec42bed06 100644
--- a/hutool-core/src/main/java/cn/hutool/core/util/ModifierUtil.java
+++ b/hutool-core/src/main/java/cn/hutool/core/util/ModifierUtil.java
@@ -20,35 +20,60 @@ public class ModifierUtil {
* @since 4.0.5
*/
public enum ModifierType {
- /** public修饰符,所有类都能访问 */
+ /**
+ * public修饰符,所有类都能访问
+ */
PUBLIC(Modifier.PUBLIC),
- /** private修饰符,只能被自己访问和修改 */
+ /**
+ * private修饰符,只能被自己访问和修改
+ */
PRIVATE(Modifier.PRIVATE),
- /** protected修饰符,自身、子类及同一个包中类可以访问 */
+ /**
+ * protected修饰符,自身、子类及同一个包中类可以访问
+ */
PROTECTED(Modifier.PROTECTED),
- /** static修饰符,(静态修饰符)指定变量被所有对象共享,即所有实例都可以使用该变量。变量属于这个类 */
+ /**
+ * static修饰符,(静态修饰符)指定变量被所有对象共享,即所有实例都可以使用该变量。变量属于这个类
+ */
STATIC(Modifier.STATIC),
- /** final修饰符,最终修饰符,指定此变量的值不能变,使用在方法上表示不能被重载 */
+ /**
+ * final修饰符,最终修饰符,指定此变量的值不能变,使用在方法上表示不能被重载
+ */
FINAL(Modifier.FINAL),
- /** synchronized,同步修饰符,在多个线程中,该修饰符用于在运行前,对他所属的方法加锁,以防止其他线程的访问,运行结束后解锁。 */
+ /**
+ * synchronized,同步修饰符,在多个线程中,该修饰符用于在运行前,对他所属的方法加锁,以防止其他线程的访问,运行结束后解锁。
+ */
SYNCHRONIZED(Modifier.SYNCHRONIZED),
- /** (易失修饰符)指定该变量可以同时被几个线程控制和修改 */
+ /**
+ * (易失修饰符)指定该变量可以同时被几个线程控制和修改
+ */
VOLATILE(Modifier.VOLATILE),
- /** (过度修饰符)指定该变量是系统保留,暂无特别作用的临时性变量,序列化时忽略 */
+ /**
+ * (过度修饰符)指定该变量是系统保留,暂无特别作用的临时性变量,序列化时忽略
+ */
TRANSIENT(Modifier.TRANSIENT),
- /** native,本地修饰符。指定此方法的方法体是用其他语言在程序外部编写的。 */
+ /**
+ * native,本地修饰符。指定此方法的方法体是用其他语言在程序外部编写的。
+ */
NATIVE(Modifier.NATIVE),
- /** abstract,将一个类声明为抽象类,没有实现的方法,需要子类提供方法实现。 */
+ /**
+ * abstract,将一个类声明为抽象类,没有实现的方法,需要子类提供方法实现。
+ */
ABSTRACT(Modifier.ABSTRACT),
- /** strictfp,一旦使用了关键字strictfp来声明某个类、接口或者方法时,那么在这个关键字所声明的范围内所有浮点运算都是精确的,符合IEEE-754规范的。 */
+ /**
+ * strictfp,一旦使用了关键字strictfp来声明某个类、接口或者方法时,那么在这个关键字所声明的范围内所有浮点运算都是精确的,符合IEEE-754规范的。
+ */
STRICT(Modifier.STRICT);
- /** 修饰符枚举对应的int修饰符值 */
+ /**
+ * 修饰符枚举对应的int修饰符值
+ */
private final int value;
/**
* 构造
+ *
* @param modifier 修饰符int表示,见{@link Modifier}
*/
ModifierType(int modifier) {
@@ -57,6 +82,7 @@ public class ModifierUtil {
/**
* 获取修饰符枚举对应的int修饰符值,值见{@link Modifier}
+ *
* @return 修饰符枚举对应的int修饰符值
*/
public int getValue() {
@@ -67,7 +93,7 @@ public class ModifierUtil {
/**
* 是否同时存在一个或多个修饰符(可能有多个修饰符,如果有指定的修饰符则返回true)
*
- * @param clazz 类
+ * @param clazz 类
* @param modifierTypes 修饰符枚举
* @return 是否有指定修饰符,如果有返回true,否则false,如果提供参数为null返回false
*/
@@ -81,7 +107,7 @@ public class ModifierUtil {
/**
* 是否同时存在一个或多个修饰符(可能有多个修饰符,如果有指定的修饰符则返回true)
*
- * @param constructor 构造方法
+ * @param constructor 构造方法
* @param modifierTypes 修饰符枚举
* @return 是否有指定修饰符,如果有返回true,否则false,如果提供参数为null返回false
*/
@@ -95,7 +121,7 @@ public class ModifierUtil {
/**
* 是否同时存在一个或多个修饰符(可能有多个修饰符,如果有指定的修饰符则返回true)
*
- * @param method 方法
+ * @param method 方法
* @param modifierTypes 修饰符枚举
* @return 是否有指定修饰符,如果有返回true,否则false,如果提供参数为null返回false
*/
@@ -109,7 +135,7 @@ public class ModifierUtil {
/**
* 是否同时存在一个或多个修饰符(可能有多个修饰符,如果有指定的修饰符则返回true)
*
- * @param field 字段
+ * @param field 字段
* @param modifierTypes 修饰符枚举
* @return 是否有指定修饰符,如果有返回true,否则false,如果提供参数为null返回false
*/
@@ -226,15 +252,27 @@ public class ModifierUtil {
return clazz.isSynthetic();
}
+ /**
+ * 是否抽象方法
+ *
+ * @param method 方法
+ * @return 是否抽象方法
+ * @since 5.7.23
+ */
+ public static boolean isAbstract(Method method) {
+ return hasModifier(method, ModifierType.ABSTRACT);
+ }
//-------------------------------------------------------------------------------------------------------- Private method start
+
/**
* 多个修饰符做“与”操作,表示同时存在多个修饰符
+ *
* @param modifierTypes 修饰符列表,元素不能为空
* @return “与”之后的修饰符
*/
private static int modifiersToInt(ModifierType... modifierTypes) {
int modifier = modifierTypes[0].getValue();
- for(int i = 1; i < modifierTypes.length; i++) {
+ for (int i = 1; i < modifierTypes.length; i++) {
modifier |= modifierTypes[i].getValue();
}
return modifier;
diff --git a/hutool-core/src/main/java/cn/hutool/core/util/ReflectUtil.java b/hutool-core/src/main/java/cn/hutool/core/util/ReflectUtil.java
index de406bfbfd..4e4a78faf8 100644
--- a/hutool-core/src/main/java/cn/hutool/core/util/ReflectUtil.java
+++ b/hutool-core/src/main/java/cn/hutool/core/util/ReflectUtil.java
@@ -652,30 +652,36 @@ public class ReflectUtil {
}
/**
- * 获得一个类中所有方法列表,直接反射获取,无缓存
+ * 获得一个类中所有方法列表,直接反射获取,无缓存
+ * 接口获取方法和默认方法
*
- * @param beanClass 类
- * @param withSuperClassMethods 是否包括父类的方法列表
+ * @param beanClass 类或接口
+ * @param withSupers 是否包括父类或接口的方法列表
* @return 方法列表
* @throws SecurityException 安全检查异常
*/
- public static Method[] getMethodsDirectly(Class> beanClass, boolean withSuperClassMethods) throws SecurityException {
+ public static Method[] getMethodsDirectly(Class> beanClass, boolean withSupers) throws SecurityException {
Assert.notNull(beanClass);
+ if(beanClass.isInterface()){
+ // 对于接口,直接调用Class.getMethods方法获取所有方法,因为接口都是public方法
+ return withSupers ? beanClass.getMethods() : beanClass.getDeclaredMethods();
+ }
+
Method[] allMethods = null;
Class> searchType = beanClass;
Method[] declaredMethods;
- while (searchType != null) {
+ while (searchType != null && searchType != Object.class) {
declaredMethods = searchType.getDeclaredMethods();
if (null == allMethods) {
allMethods = declaredMethods;
} else {
allMethods = ArrayUtil.append(allMethods, declaredMethods);
}
- searchType = withSuperClassMethods ? searchType.getSuperclass() : null;
+ searchType = (withSupers && false == searchType.isInterface()) ? searchType.getSuperclass() : null;
}
- return allMethods;
+ return ArrayUtil.append(allMethods);
}
/**
diff --git a/hutool-core/src/test/java/cn/hutool/core/lang/test/bean/ExamInfoDict.java b/hutool-core/src/test/java/cn/hutool/core/lang/test/bean/ExamInfoDict.java
index a39e91238b..b5068f2d8a 100644
--- a/hutool-core/src/test/java/cn/hutool/core/lang/test/bean/ExamInfoDict.java
+++ b/hutool-core/src/test/java/cn/hutool/core/lang/test/bean/ExamInfoDict.java
@@ -1,16 +1,18 @@
package cn.hutool.core.lang.test.bean;
+import lombok.Data;
+
import java.io.Serializable;
-import java.util.Objects;
/**
- *
+ *
* @author 质量过关
*
*/
+@Data
public class ExamInfoDict implements Serializable {
private static final long serialVersionUID = 3640936499125004525L;
-
+
// 主键
private Integer id; // 可当作题号
// 试题类型 客观题 0主观题 1
@@ -18,49 +20,7 @@ public class ExamInfoDict implements Serializable {
// 试题是否作答
private Integer answerIs;
- public Integer getId() {
- return id;
- }
public Integer getId(Integer defaultValue) {
return this.id == null ? defaultValue : this.id;
}
- public void setId(Integer id) {
- this.id = id;
- }
-
- public Integer getExamType() {
- return examType;
- }
- public void setExamType(Integer examType) {
- this.examType = examType;
- }
-
- public Integer getAnswerIs() {
- return answerIs;
- }
- public void setAnswerIs(Integer answerIs) {
- this.answerIs = answerIs;
- }
-
- @Override
- public boolean equals(Object o) {
- if (this == o) {
- return true;
- }
- if (o == null || getClass() != o.getClass()) {
- return false;
- }
- ExamInfoDict that = (ExamInfoDict) o;
- return Objects.equals(id, that.id) && Objects.equals(examType, that.examType) && Objects.equals(answerIs, that.answerIs);
- }
-
- @Override
- public int hashCode() {
- return Objects.hash(id, examType, answerIs);
- }
-
- @Override
- public String toString() {
- return "ExamInfoDict{" + "id=" + id + ", examType=" + examType + ", answerIs=" + answerIs + '}';
- }
}
diff --git a/hutool-core/src/test/java/cn/hutool/core/util/ReflectUtilTest.java b/hutool-core/src/test/java/cn/hutool/core/util/ReflectUtilTest.java
index 9a90c8fe38..bb9dca5f3b 100644
--- a/hutool-core/src/test/java/cn/hutool/core/util/ReflectUtilTest.java
+++ b/hutool-core/src/test/java/cn/hutool/core/util/ReflectUtilTest.java
@@ -116,7 +116,7 @@ public class ReflectUtilTest {
@Ignore
public void getMethodBenchTest(){
// 预热
- getMethod(TestBenchClass.class, false, "getH");
+ getMethodWithReturnTypeCheck(TestBenchClass.class, false, "getH");
final TimeInterval timer = DateUtil.timer();
timer.start();
@@ -127,7 +127,7 @@ public class ReflectUtilTest {
timer.restart();
for (int i = 0; i < 100000000; i++) {
- getMethod(TestBenchClass.class, false, "getH");
+ getMethodWithReturnTypeCheck(TestBenchClass.class, false, "getH");
}
Console.log(timer.interval());
}
@@ -150,7 +150,7 @@ public class ReflectUtilTest {
private String n;
}
- public static Method getMethod(Class> clazz, boolean ignoreCase, String methodName, Class>... paramTypes) throws SecurityException {
+ public static Method getMethodWithReturnTypeCheck(Class> clazz, boolean ignoreCase, String methodName, Class>... paramTypes) throws SecurityException {
if (null == clazz || StrUtil.isBlank(methodName)) {
return null;
}
@@ -169,4 +169,61 @@ public class ReflectUtilTest {
}
return res;
}
+
+ @Test
+ public void getMethodsFromClassExtends(){
+ // 继承情况下,需解决方法去重问题
+ final Method[] methods = ReflectUtil.getMethods(C2.class);
+ Assert.assertEquals(2, methods.length);
+ }
+
+ @Test
+ public void getMethodsFromInterfaceTest(){
+ // 对于接口,直接调用Class.getMethods方法获取所有方法,因为接口都是public方法
+ // 因此此处得到包括TestInterface1、TestInterface2、TestInterface3中一共4个方法
+ final Method[] methods = ReflectUtil.getMethods(TestInterface3.class);
+ Assert.assertEquals(4, methods.length);
+
+ // 接口里,调用getMethods和getPublicMethods效果相同
+ final Method[] publicMethods = ReflectUtil.getPublicMethods(TestInterface3.class);
+ Assert.assertArrayEquals(methods, publicMethods);
+ }
+
+ interface TestInterface1{
+ void getA();
+ void getB();
+
+ default void getC(){
+
+ }
+ }
+
+ interface TestInterface2 extends TestInterface1{
+ @Override
+ void getB();
+ }
+
+ interface TestInterface3 extends TestInterface2{
+ void get3();
+ }
+
+ class C1 implements TestInterface2{
+
+ @Override
+ public void getA() {
+
+ }
+
+ @Override
+ public void getB() {
+
+ }
+ }
+
+ class C2 extends C1{
+ @Override
+ public void getA() {
+
+ }
+ }
}
--
Gitee
From 56d490e2ac5ef147acfacafde9a63df4e997669d Mon Sep 17 00:00:00 2001
From: Looly
Date: Tue, 8 Mar 2022 22:50:58 +0800
Subject: [PATCH 25/57] fix doc
---
.../main/java/cn/hutool/core/lang/ObjectId.java | 15 +++++++++++++++
1 file changed, 15 insertions(+)
diff --git a/hutool-core/src/main/java/cn/hutool/core/lang/ObjectId.java b/hutool-core/src/main/java/cn/hutool/core/lang/ObjectId.java
index e4a8c06fa5..d8c7c69551 100644
--- a/hutool-core/src/main/java/cn/hutool/core/lang/ObjectId.java
+++ b/hutool-core/src/main/java/cn/hutool/core/lang/ObjectId.java
@@ -22,6 +22,21 @@ import java.util.concurrent.atomic.AtomicInteger;
* 4. INC 自增计数器。确保同一秒内产生objectId的唯一性。
*
*
+ *
+ *
+ * 时间戳
+ * 机器ID
+ * 进程ID
+ * 自增计数器
+ *
+ *
+ * 4
+ * 3
+ * 2
+ * 3
+ *
+ *
+ *
* 参考:http://blog.csdn.net/qxc1281/article/details/54021882
*
* @author looly
--
Gitee
From 8f0f3354e3f78fea471f72d24dd1b28d0ff77245 Mon Sep 17 00:00:00 2001
From: Looly
Date: Wed, 9 Mar 2022 00:58:10 +0800
Subject: [PATCH 26/57] add UniqueKeySet
---
CHANGELOG.md | 3 +-
.../hutool/core/collection/UniqueKeySet.java | 152 ++++++++++++++++++
.../java/cn/hutool/core/util/ReflectUtil.java | 89 +++++++---
.../core/collection/UniqueKeySetTest.java | 34 ++++
.../cn/hutool/core/util/ReflectUtilTest.java | 39 +++--
5 files changed, 285 insertions(+), 32 deletions(-)
create mode 100644 hutool-core/src/main/java/cn/hutool/core/collection/UniqueKeySet.java
create mode 100644 hutool-core/src/test/java/cn/hutool/core/collection/UniqueKeySetTest.java
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 3fd0cf5e63..dfefa113bb 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -2,7 +2,7 @@
# 🚀Changelog
-------------------------------------------------------------------------------------------------------------
-# 5.7.23 (2022-03-08)
+# 5.7.23 (2022-03-09)
### 🐣新特性
* 【http 】 HttpRequest.form采用TableMap方式(issue#I4W427@Gitee)
@@ -10,6 +10,7 @@
* 【core 】 FileUtil.extName增加对tar.gz特殊处理(issue#I4W5FS@Gitee)
* 【crypto 】 增加XXTEA实现(issue#I4WH2X@Gitee)
* 【core 】 增加Table实现(issue#2179@Github)
+* 【core 】 增加UniqueKeySet(issue#I4WUWR@Gitee)
*
### 🐞Bug修复
* 【core 】 修复ObjectUtil.hasNull传入null返回true的问题(pr#555@Gitee)
diff --git a/hutool-core/src/main/java/cn/hutool/core/collection/UniqueKeySet.java b/hutool-core/src/main/java/cn/hutool/core/collection/UniqueKeySet.java
new file mode 100644
index 0000000000..f4fbcb15c8
--- /dev/null
+++ b/hutool-core/src/main/java/cn/hutool/core/collection/UniqueKeySet.java
@@ -0,0 +1,152 @@
+package cn.hutool.core.collection;
+
+import cn.hutool.core.map.MapBuilder;
+import cn.hutool.core.util.ObjectUtil;
+
+import java.io.Serializable;
+import java.util.AbstractSet;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.function.Function;
+
+/**
+ * 唯一键的Set
+ * 通过自定义唯一键,通过{@link #uniqueGenerator}生成节点对象对应的键作为Map的key,确定唯一
+ * 此Set与HashSet不同的是,HashSet依赖于{@link Object#equals(Object)}确定唯一
+ * 但是很多时候我们无法对对象进行修改,此时在外部定义一个唯一规则,即可完成去重。
+ *
+ * {@code Set set = new UniqueKeySet<>(UniqueTestBean::getId);}
+ *
+ *
+ * @param 唯一键类型
+ * @param 值对象
+ * @author looly
+ * @since 5.7.23
+ */
+public class UniqueKeySet extends AbstractSet implements Serializable {
+ private static final long serialVersionUID = 1L;
+
+ private Map map;
+ private final Function uniqueGenerator;
+
+ //region 构造
+
+ /**
+ * 构造
+ *
+ * @param uniqueGenerator 唯一键生成规则函数,用于生成对象对应的唯一键
+ */
+ public UniqueKeySet(Function uniqueGenerator) {
+ this(false, uniqueGenerator);
+ }
+
+ /**
+ * 构造
+ *
+ * @param isLinked 是否保持加入顺序
+ * @param uniqueGenerator 唯一键生成规则函数,用于生成对象对应的唯一键
+ */
+ public UniqueKeySet(boolean isLinked, Function uniqueGenerator) {
+ this(MapBuilder.create(isLinked), uniqueGenerator);
+ }
+
+ /**
+ * 构造
+ *
+ * @param initialCapacity 初始容量
+ * @param loadFactor 增长因子
+ * @param uniqueGenerator 唯一键生成规则函数,用于生成对象对应的唯一键
+ */
+ public UniqueKeySet(int initialCapacity, float loadFactor, Function uniqueGenerator) {
+ this(MapBuilder.create(new HashMap<>(initialCapacity, loadFactor)), uniqueGenerator);
+ }
+
+ /**
+ * 构造
+ *
+ * @param builder 初始Map,定义了Map类型
+ * @param uniqueGenerator 唯一键生成规则函数,用于生成对象对应的唯一键
+ */
+ public UniqueKeySet(MapBuilder builder, Function uniqueGenerator) {
+ this.map = builder.build();
+ this.uniqueGenerator = uniqueGenerator;
+ }
+ //endregion
+
+ @Override
+ public Iterator iterator() {
+ return map.values().iterator();
+ }
+
+ @Override
+ public int size() {
+ return map.size();
+ }
+
+ @Override
+ public boolean isEmpty() {
+ return map.isEmpty();
+ }
+
+ @Override
+ public boolean contains(Object o) {
+ //noinspection unchecked
+ return map.containsKey(this.uniqueGenerator.apply((V) o));
+ }
+
+ @Override
+ public boolean add(V v) {
+ return null == map.put(this.uniqueGenerator.apply(v), v);
+ }
+
+ /**
+ * 加入值,如果值已经存在,则忽略之
+ *
+ * @param v 值
+ * @return 是否成功加入
+ */
+ public boolean addIfAbsent(V v) {
+ return null == map.putIfAbsent(this.uniqueGenerator.apply(v), v);
+ }
+
+ /**
+ * 加入集合中所有的值,如果值已经存在,则忽略之
+ *
+ * @param c 集合
+ * @return 是否有一个或多个被加入成功
+ */
+ public boolean addAllIfAbsent(Collection extends V> c) {
+ boolean modified = false;
+ for (V v : c)
+ if (addIfAbsent(v)) {
+ modified = true;
+ }
+ return modified;
+ }
+
+ @Override
+ public boolean remove(Object o) {
+ //noinspection unchecked
+ return null != map.remove(this.uniqueGenerator.apply((V) o));
+ }
+
+ @Override
+ public void clear() {
+ map.clear();
+ }
+
+ @Override
+ @SuppressWarnings("unchecked")
+ public UniqueKeySet clone() {
+ try {
+ UniqueKeySet newSet = (UniqueKeySet) super.clone();
+ newSet.map = ObjectUtil.clone(this.map);
+ return newSet;
+ } catch (CloneNotSupportedException e) {
+ throw new InternalError(e);
+ }
+ }
+
+}
diff --git a/hutool-core/src/main/java/cn/hutool/core/util/ReflectUtil.java b/hutool-core/src/main/java/cn/hutool/core/util/ReflectUtil.java
index 4e4a78faf8..7b746bc6ec 100644
--- a/hutool-core/src/main/java/cn/hutool/core/util/ReflectUtil.java
+++ b/hutool-core/src/main/java/cn/hutool/core/util/ReflectUtil.java
@@ -3,6 +3,7 @@ package cn.hutool.core.util;
import cn.hutool.core.annotation.Alias;
import cn.hutool.core.bean.NullWrapperBean;
import cn.hutool.core.collection.CollUtil;
+import cn.hutool.core.collection.UniqueKeySet;
import cn.hutool.core.convert.Convert;
import cn.hutool.core.exceptions.UtilException;
import cn.hutool.core.lang.Assert;
@@ -17,6 +18,7 @@ import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.AbstractMap;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
@@ -347,11 +349,12 @@ public class ReflectUtil {
/**
* 是否为父类引用字段
* 当字段所在类是对象子类时(对象中定义的非static的class),会自动生成一个以"this$0"为名称的字段,指向父类对象
+ *
* @param field 字段
* @return 是否为父类引用字段
* @since 5.7.20
*/
- public static boolean isOuterClassField(Field field){
+ public static boolean isOuterClassField(Field field) {
return "this$0".equals(field.getName());
}
@@ -648,40 +651,47 @@ public class ReflectUtil {
*/
public static Method[] getMethods(Class> beanClass) throws SecurityException {
Assert.notNull(beanClass);
- return METHODS_CACHE.get(beanClass, () -> getMethodsDirectly(beanClass, true));
+ return METHODS_CACHE.get(beanClass,
+ () -> getMethodsDirectly(beanClass, true, true));
}
/**
* 获得一个类中所有方法列表,直接反射获取,无缓存
- * 接口获取方法和默认方法
+ * 接口获取方法和默认方法,获取的方法包括:
+ *
+ * 本类中的所有方法(包括static方法)
+ * 父类中的所有方法(包括static方法)
+ * Object中(包括static方法)
+ *
*
- * @param beanClass 类或接口
- * @param withSupers 是否包括父类或接口的方法列表
+ * @param beanClass 类或接口
+ * @param withSupers 是否包括父类或接口的方法列表
+ * @param withMethodFromObject 是否包括Object中的方法
* @return 方法列表
* @throws SecurityException 安全检查异常
*/
- public static Method[] getMethodsDirectly(Class> beanClass, boolean withSupers) throws SecurityException {
+ public static Method[] getMethodsDirectly(Class> beanClass, boolean withSupers, boolean withMethodFromObject) throws SecurityException {
Assert.notNull(beanClass);
- if(beanClass.isInterface()){
+ if (beanClass.isInterface()) {
// 对于接口,直接调用Class.getMethods方法获取所有方法,因为接口都是public方法
return withSupers ? beanClass.getMethods() : beanClass.getDeclaredMethods();
}
- Method[] allMethods = null;
+ final UniqueKeySet result = new UniqueKeySet<>(true, ReflectUtil::getUniqueKey);
Class> searchType = beanClass;
- Method[] declaredMethods;
- while (searchType != null && searchType != Object.class) {
- declaredMethods = searchType.getDeclaredMethods();
- if (null == allMethods) {
- allMethods = declaredMethods;
- } else {
- allMethods = ArrayUtil.append(allMethods, declaredMethods);
+ while (searchType != null) {
+ if (false == withMethodFromObject && Object.class == searchType) {
+ break;
}
+ result.addAllIfAbsent(Arrays.asList(searchType.getDeclaredMethods()));
+ result.addAllIfAbsent(getDefaultMethodsFromInterface(searchType));
+
+
searchType = (withSupers && false == searchType.isInterface()) ? searchType.getSuperclass() : null;
}
- return ArrayUtil.append(allMethods);
+ return result.toArray(new Method[0]);
}
/**
@@ -777,10 +787,10 @@ public class ReflectUtil {
String name = method.getName();
// 跳过getClass这个特殊方法
- if("getClass".equals(name)){
+ if ("getClass".equals(name)) {
return false;
}
- if(ignoreCase){
+ if (ignoreCase) {
name = name.toLowerCase();
}
switch (parameterCount) {
@@ -1044,4 +1054,47 @@ public class ReflectUtil {
}
return accessibleObject;
}
+
+ /**
+ * 获取方法的唯一键,结构为:
+ *
+ * 返回类型#方法名:参数1类型,参数2类型...
+ *
+ *
+ * @param method 方法
+ * @return 方法唯一键
+ */
+ private static String getUniqueKey(Method method) {
+ final StringBuilder sb = new StringBuilder();
+ sb.append(method.getReturnType().getName()).append('#');
+ sb.append(method.getName());
+ Class>[] parameters = method.getParameterTypes();
+ for (int i = 0; i < parameters.length; i++) {
+ if (i == 0) {
+ sb.append(':');
+ } else {
+ sb.append(',');
+ }
+ sb.append(parameters[i].getName());
+ }
+ return sb.toString();
+ }
+
+ /**
+ * 获取类对应接口中的非抽象方法(default方法)
+ *
+ * @param clazz 类
+ * @return 方法列表
+ */
+ private static List getDefaultMethodsFromInterface(Class> clazz) {
+ List result = new ArrayList<>();
+ for (Class> ifc : clazz.getInterfaces()) {
+ for (Method m : ifc.getMethods()) {
+ if (false == ModifierUtil.isAbstract(m)) {
+ result.add(m);
+ }
+ }
+ }
+ return result;
+ }
}
diff --git a/hutool-core/src/test/java/cn/hutool/core/collection/UniqueKeySetTest.java b/hutool-core/src/test/java/cn/hutool/core/collection/UniqueKeySetTest.java
new file mode 100644
index 0000000000..9dd45058fc
--- /dev/null
+++ b/hutool-core/src/test/java/cn/hutool/core/collection/UniqueKeySetTest.java
@@ -0,0 +1,34 @@
+package cn.hutool.core.collection;
+
+import cn.hutool.core.lang.Console;
+import lombok.AllArgsConstructor;
+import lombok.Data;
+import org.junit.Assert;
+import org.junit.Test;
+
+import java.util.Set;
+
+public class UniqueKeySetTest {
+
+ @Test
+ public void addTest(){
+ Set set = new UniqueKeySet<>(UniqueTestBean::getId);
+ set.add(new UniqueTestBean("id1", "张三", "地球"));
+ set.add(new UniqueTestBean("id2", "李四", "火星"));
+ // id重复,替换之前的元素
+ set.add(new UniqueTestBean("id2", "王五", "木星"));
+
+ // 后两个ID重复
+ Assert.assertEquals(2, set.size());
+
+ set.forEach(Console::log);
+ }
+
+ @Data
+ @AllArgsConstructor
+ static class UniqueTestBean{
+ private String id;
+ private String name;
+ private String address;
+ }
+}
diff --git a/hutool-core/src/test/java/cn/hutool/core/util/ReflectUtilTest.java b/hutool-core/src/test/java/cn/hutool/core/util/ReflectUtilTest.java
index bb9dca5f3b..6affbabb61 100644
--- a/hutool-core/src/test/java/cn/hutool/core/util/ReflectUtilTest.java
+++ b/hutool-core/src/test/java/cn/hutool/core/util/ReflectUtilTest.java
@@ -23,7 +23,7 @@ public class ReflectUtilTest {
@Test
public void getMethodsTest() {
Method[] methods = ReflectUtil.getMethods(ExamInfoDict.class);
- Assert.assertEquals(22, methods.length);
+ Assert.assertEquals(20, methods.length);
//过滤器测试
methods = ReflectUtil.getMethods(ExamInfoDict.class, t -> Integer.class.equals(t.getReturnType()));
@@ -35,7 +35,7 @@ public class ReflectUtilTest {
//null过滤器测试
methods = ReflectUtil.getMethods(ExamInfoDict.class, null);
- Assert.assertEquals(22, methods.length);
+ Assert.assertEquals(20, methods.length);
final Method method2 = methods[0];
Assert.assertNotNull(method2);
}
@@ -114,7 +114,7 @@ public class ReflectUtilTest {
@Test
@Ignore
- public void getMethodBenchTest(){
+ public void getMethodBenchTest() {
// 预热
getMethodWithReturnTypeCheck(TestBenchClass.class, false, "getH");
@@ -171,14 +171,26 @@ public class ReflectUtilTest {
}
@Test
- public void getMethodsFromClassExtends(){
+ public void getMethodsFromClassExtends() {
// 继承情况下,需解决方法去重问题
- final Method[] methods = ReflectUtil.getMethods(C2.class);
- Assert.assertEquals(2, methods.length);
+ Method[] methods = ReflectUtil.getMethods(C2.class);
+ Assert.assertEquals(15, methods.length);
+
+ // 排除Object中的方法
+ // 3个方法包括类
+ methods = ReflectUtil.getMethodsDirectly(C2.class, true, false);
+ Assert.assertEquals(3, methods.length);
+
+ // getA属于本类
+ Assert.assertEquals("public void cn.hutool.core.util.ReflectUtilTest$C2.getA()", methods[0].toString());
+ // getB属于父类
+ Assert.assertEquals("public void cn.hutool.core.util.ReflectUtilTest$C1.getB()", methods[1].toString());
+ // getC属于接口中的默认方法
+ Assert.assertEquals("public default void cn.hutool.core.util.ReflectUtilTest$TestInterface1.getC()", methods[2].toString());
}
@Test
- public void getMethodsFromInterfaceTest(){
+ public void getMethodsFromInterfaceTest() {
// 对于接口,直接调用Class.getMethods方法获取所有方法,因为接口都是public方法
// 因此此处得到包括TestInterface1、TestInterface2、TestInterface3中一共4个方法
final Method[] methods = ReflectUtil.getMethods(TestInterface3.class);
@@ -189,25 +201,26 @@ public class ReflectUtilTest {
Assert.assertArrayEquals(methods, publicMethods);
}
- interface TestInterface1{
+ interface TestInterface1 {
void getA();
+
void getB();
- default void getC(){
+ default void getC() {
}
}
- interface TestInterface2 extends TestInterface1{
+ interface TestInterface2 extends TestInterface1 {
@Override
void getB();
}
- interface TestInterface3 extends TestInterface2{
+ interface TestInterface3 extends TestInterface2 {
void get3();
}
- class C1 implements TestInterface2{
+ class C1 implements TestInterface2 {
@Override
public void getA() {
@@ -220,7 +233,7 @@ public class ReflectUtilTest {
}
}
- class C2 extends C1{
+ class C2 extends C1 {
@Override
public void getA() {
--
Gitee
From 1aeb56564f38826d575e63e4e52298b9784c484e Mon Sep 17 00:00:00 2001
From: jiazhengquan <2466896229@qq.com>
Date: Wed, 9 Mar 2022 11:25:52 +0800
Subject: [PATCH 27/57] =?UTF-8?q?=E9=92=88=E5=AF=B9issue#I4WUWR=E4=BF=AE?=
=?UTF-8?q?=E5=A4=8D?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.../java/cn/hutool/core/util/ReflectUtil.java | 36 +++++++++----------
1 file changed, 18 insertions(+), 18 deletions(-)
diff --git a/hutool-core/src/main/java/cn/hutool/core/util/ReflectUtil.java b/hutool-core/src/main/java/cn/hutool/core/util/ReflectUtil.java
index 4f9ded9d2c..dbad1e91de 100644
--- a/hutool-core/src/main/java/cn/hutool/core/util/ReflectUtil.java
+++ b/hutool-core/src/main/java/cn/hutool/core/util/ReflectUtil.java
@@ -663,34 +663,34 @@ public class ReflectUtil {
Assert.notNull(beanClass);
Method[] allMethods = null;
- Class>[] searchInterfaces = null, parentInterfaces = null;
Class> searchType = beanClass;
+ Class>[] tempInterfaceType, interfaceType;
Method[] declaredMethods;
- while (searchType != null || searchInterfaces != null) {
- if (searchType != null) {
+ if (searchType.isInterface()) {
+ allMethods = searchType.getDeclaredMethods();
+ interfaceType = searchType.getInterfaces();
+ while (ArrayUtil.isNotEmpty(interfaceType)) {
+ tempInterfaceType = interfaceType;
+ for (int i = 0; i < tempInterfaceType.length; i++) {
+ Class> temp = tempInterfaceType[i];
+ allMethods = ArrayUtil.append(allMethods, temp.getDeclaredMethods());
+ if (i == 0 || ArrayUtil.isEmpty(interfaceType)) {
+ interfaceType = temp.getInterfaces();
+ } else {
+ interfaceType = ArrayUtil.append(interfaceType, temp.getInterfaces());
+ }
+ }
+ }
+ } else {
+ while (searchType != null) {
declaredMethods = searchType.getDeclaredMethods();
if (null == allMethods) {
allMethods = declaredMethods;
} else {
allMethods = ArrayUtil.append(allMethods, declaredMethods);
}
- Class>[] interfaces = searchType.getInterfaces();
- for (Class> element : interfaces) {
- allMethods = ArrayUtil.append(allMethods, element.getDeclaredMethods());
- parentInterfaces = ArrayUtil.addAll(element.getInterfaces());
- }
- searchInterfaces = parentInterfaces;
- Class>[] classes = searchInterfaces.length == 0 ? null : searchInterfaces;
- searchInterfaces = withSuperClassMethods ? classes : null;
searchType = withSuperClassMethods ? searchType.getSuperclass() : null;
}
- if (searchInterfaces != null) {
- for (Class> searchInterface : searchInterfaces) {
- allMethods = ArrayUtil.append(allMethods, searchInterface.getDeclaredMethods());
- parentInterfaces = ArrayUtil.addAll(searchInterface.getInterfaces());
- }
- searchInterfaces = parentInterfaces.length == 0 ? null : parentInterfaces;
- }
}
return allMethods;
}
--
Gitee
From b8bfce490baabb963d5f4ebc076593e4fef26321 Mon Sep 17 00:00:00 2001
From: Looly
Date: Wed, 9 Mar 2022 12:05:09 +0800
Subject: [PATCH 28/57] fix comment
---
.../src/main/java/cn/hutool/crypto/symmetric/XXTEA.java | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/hutool-crypto/src/main/java/cn/hutool/crypto/symmetric/XXTEA.java b/hutool-crypto/src/main/java/cn/hutool/crypto/symmetric/XXTEA.java
index 4f948cd4f9..d413dce9d7 100644
--- a/hutool-crypto/src/main/java/cn/hutool/crypto/symmetric/XXTEA.java
+++ b/hutool-crypto/src/main/java/cn/hutool/crypto/symmetric/XXTEA.java
@@ -10,7 +10,7 @@ import java.io.Serializable;
* XXTEA(Corrected Block Tiny Encryption Algorithm)算法实现
* 来自:https://github.com/xxtea/xxtea-java
*
- * @author Ma Bingyao
+ * @author Ma Bingyao
*/
public class XXTEA implements SymmetricEncryptor, SymmetricDecryptor, Serializable {
private static final long serialVersionUID = 1L;
--
Gitee
From 10d5cabf976bba2c4eba3b66d6dbda080baffd6d Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E9=9F=A9=E5=B8=85?=
Date: Wed, 9 Mar 2022 15:48:31 +0800
Subject: [PATCH 29/57] =?UTF-8?q?=E6=B7=BB=E5=8A=A0=E5=A4=87=E7=94=A8?=
=?UTF-8?q?=E6=96=87=E6=A1=A3=E5=9C=B0=E5=9D=80?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
README-EN.md | 2 ++
README.md | 2 ++
2 files changed, 4 insertions(+)
diff --git a/README-EN.md b/README-EN.md
index 05475e644e..7456a08f77 100644
--- a/README-EN.md
+++ b/README-EN.md
@@ -111,6 +111,8 @@ Each module can be introduced individually, or all modules can be introduced by
[📘Chinese documentation](https://www.hutool.cn/docs/)
+[📘Chinese back-up documentation](https://plus.hutool.cn/docs/#/)
+
[📙API](https://apidoc.gitee.com/dromara/hutool/)
[🎬Video](https://www.bilibili.com/video/BV1bQ4y1M7d9?p=2)
diff --git a/README.md b/README.md
index f8834f52a6..409e4e22d9 100644
--- a/README.md
+++ b/README.md
@@ -107,6 +107,8 @@ Hutool的存在就是为了减少代码搜索成本,避免网络上参差不
[📘中文文档](https://www.hutool.cn/docs/)
+[📘中文备用文档](https://plus.hutool.cn/docs/#/)
+
[📙参考API](https://apidoc.gitee.com/dromara/hutool/)
[🎬视频介绍](https://www.bilibili.com/video/BV1bQ4y1M7d9?p=2)
--
Gitee
From aec10a7714a72ee038a9fa497e4e3726cfff1fc4 Mon Sep 17 00:00:00 2001
From: gxz <514190950@qq.com>
Date: Wed, 9 Mar 2022 17:03:17 +0800
Subject: [PATCH 30/57] =?UTF-8?q?=E4=BF=AE=E6=94=B9Bug:=E5=BD=93FileCopier?=
=?UTF-8?q?=E7=9A=84=E7=9B=AE=E6=A0=87=E6=96=87=E4=BB=B6=E6=98=AF=E7=9B=B8?=
=?UTF-8?q?=E5=AF=B9=E8=B7=AF=E5=BE=84=E7=9A=84=E6=97=B6=E5=80=99=EF=BC=8C?=
=?UTF-8?q?=E5=A4=8D=E5=88=B6=E4=BC=9A=E5=87=BA=E7=8E=B0=E5=BC=82=E5=B8=B8?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.../cn/hutool/core/io/file/FileCopier.java | 2 +-
.../cn/hutool/core/io/FileCopierTest.java | 20 ++++++++++++++-----
2 files changed, 16 insertions(+), 6 deletions(-)
diff --git a/hutool-core/src/main/java/cn/hutool/core/io/file/FileCopier.java b/hutool-core/src/main/java/cn/hutool/core/io/file/FileCopier.java
index fbdc4938f8..6f133f2e37 100644
--- a/hutool-core/src/main/java/cn/hutool/core/io/file/FileCopier.java
+++ b/hutool-core/src/main/java/cn/hutool/core/io/file/FileCopier.java
@@ -268,7 +268,7 @@ public class FileCopier extends SrcToDestCopier{
}else {
//路径不存在则创建父目录
//noinspection ResultOfMethodCallIgnored
- dest.getParentFile().mkdirs();
+ dest.getAbsoluteFile().getParentFile().mkdirs();
}
final ArrayList optionList = new ArrayList<>(2);
diff --git a/hutool-core/src/test/java/cn/hutool/core/io/FileCopierTest.java b/hutool-core/src/test/java/cn/hutool/core/io/FileCopierTest.java
index 80c50288de..97f7026388 100644
--- a/hutool-core/src/test/java/cn/hutool/core/io/FileCopierTest.java
+++ b/hutool-core/src/test/java/cn/hutool/core/io/FileCopierTest.java
@@ -5,41 +5,51 @@ import org.junit.Test;
import cn.hutool.core.io.file.FileCopier;
+import java.io.File;
+
/**
* 文件拷贝单元测试
* @author Looly
*
*/
public class FileCopierTest {
-
+
@Test
@Ignore
public void dirCopyTest() {
FileCopier copier = FileCopier.create("D:\\Java", "e:/eclipse/eclipse2.zip");
copier.copy();
}
-
+
@Test
@Ignore
public void dirCopyTest2() {
//测试带.的文件夹复制
FileCopier copier = FileCopier.create("D:\\workspace\\java\\.metadata", "D:\\workspace\\java\\.metadata\\temp");
copier.copy();
-
+
FileUtil.copy("D:\\workspace\\java\\looly\\hutool\\.git", "D:\\workspace\\java\\temp", true);
}
-
+
@Test(expected = IORuntimeException.class)
public void dirCopySubTest() {
//测试父目录复制到子目录报错
FileCopier copier = FileCopier.create("D:\\workspace\\java\\.metadata", "D:\\workspace\\java\\.metadata\\temp");
copier.copy();
}
-
+
@Test
@Ignore
public void copyFileToDirTest() {
FileCopier copier = FileCopier.create("d:/GReen_Soft/XshellXftpPortable.zip", "c:/hp/");
copier.copy();
}
+
+ @Test
+ @Ignore
+ public void copyFileByRelativePath(){
+ // 当复制的目标文件位置是相对路径的时候可以通过
+ FileCopier copier = FileCopier.create(new File("pom.xml"),new File("aaa.txt"));
+ copier.copy();
+ }
}
--
Gitee
From 11bf574aaed4aaaca7a448729dfdfac4dfdfb3fc Mon Sep 17 00:00:00 2001
From: VampireAchao
Date: Wed, 9 Mar 2022 22:36:23 +0800
Subject: [PATCH 31/57] =?UTF-8?q?=E5=8D=87=E7=BA=A7MongoDB?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
hutool-db/pom.xml | 12 +----
.../cn/hutool/db/nosql/mongo/MongoDS.java | 44 +++++++++++--------
2 files changed, 28 insertions(+), 28 deletions(-)
diff --git a/hutool-db/pom.xml b/hutool-db/pom.xml
index 2d586bf0bc..c80ea176d8 100644
--- a/hutool-db/pom.xml
+++ b/hutool-db/pom.xml
@@ -23,7 +23,7 @@
10.0.14
1.2.8
2.4.13
- 3.12.10
+ 4.5.0
3.36.0.3
2.5.2
@@ -97,20 +97,12 @@
true
-
- org.mongodb
- mongo-java-driver
- ${mongo.version}
- true
-
-
redis.clients
diff --git a/hutool-db/src/main/java/cn/hutool/db/nosql/mongo/MongoDS.java b/hutool-db/src/main/java/cn/hutool/db/nosql/mongo/MongoDS.java
index 8158f0d33a..1c7a4c70f7 100644
--- a/hutool-db/src/main/java/cn/hutool/db/nosql/mongo/MongoDS.java
+++ b/hutool-db/src/main/java/cn/hutool/db/nosql/mongo/MongoDS.java
@@ -6,24 +6,28 @@ import cn.hutool.core.util.StrUtil;
import cn.hutool.db.DbRuntimeException;
import cn.hutool.log.Log;
import cn.hutool.setting.Setting;
-import com.mongodb.MongoClient;
-import com.mongodb.MongoClientOptions;
-import com.mongodb.MongoClientOptions.Builder;
+import com.mongodb.MongoClientSettings;
import com.mongodb.MongoCredential;
+import com.mongodb.MongoDriverInformation;
import com.mongodb.ServerAddress;
+import com.mongodb.client.MongoClient;
import com.mongodb.client.MongoCollection;
import com.mongodb.client.MongoDatabase;
+import com.mongodb.client.internal.MongoClientImpl;
+import com.mongodb.connection.ConnectionPoolSettings;
+import com.mongodb.connection.SocketSettings;
import org.bson.Document;
import java.io.Closeable;
import java.util.ArrayList;
+import java.util.Collections;
import java.util.List;
+import java.util.concurrent.TimeUnit;
/**
* MongoDB工具类
*
* @author xiaoleilu
- *
*/
public class MongoDS implements Closeable {
private final static Log log = Log.get();
@@ -146,11 +150,11 @@ public class MongoDS implements Closeable {
final MongoCredential credentail = createCredentail(group);
try {
- if (null == credentail) {
- mongo = new MongoClient(serverAddress, buildMongoClientOptions(group));
- } else {
- mongo = new MongoClient(serverAddress, credentail, buildMongoClientOptions(group));
+ MongoClientSettings.Builder clusterSettingsBuilder = MongoClientSettings.builder().applyToClusterSettings(b -> b.hosts(Collections.singletonList(serverAddress)));
+ if (null != credentail) {
+ clusterSettingsBuilder.credential(credentail);
}
+ mongo = new MongoClientImpl(clusterSettingsBuilder.build(), MongoDriverInformation.builder().build());
} catch (Exception e) {
throw new DbRuntimeException(StrUtil.format("Init MongoDB pool with connection to [{}] error!", serverAddress), e);
}
@@ -192,11 +196,11 @@ public class MongoDS implements Closeable {
final MongoCredential credentail = createCredentail(StrUtil.EMPTY);
try {
- if (null == credentail) {
- mongo = new MongoClient(addrList, buildMongoClientOptions(StrUtil.EMPTY));
- } else {
- mongo = new MongoClient(addrList, credentail, buildMongoClientOptions(StrUtil.EMPTY));
+ MongoClientSettings.Builder clusterSettingsBuilder = MongoClientSettings.builder().applyToClusterSettings(b -> b.hosts(addrList));
+ if (null != credentail) {
+ clusterSettingsBuilder.credential(credentail);
}
+ mongo = new MongoClientImpl(clusterSettingsBuilder.build(), MongoDriverInformation.builder().build());
} catch (Exception e) {
log.error(e, "Init MongoDB connection error!");
return;
@@ -248,6 +252,7 @@ public class MongoDS implements Closeable {
}
// --------------------------------------------------------------------------- Private method start
+
/**
* 创建ServerAddress对象,会读取配置文件中的相关信息
*
@@ -322,8 +327,8 @@ public class MongoDS implements Closeable {
* @param group 分组,当分组对应的选项不存在时会读取根选项,如果也不存在使用默认值
* @return MongoClientOptions
*/
- private MongoClientOptions buildMongoClientOptions(String group) {
- return buildMongoClientOptions(MongoClientOptions.builder(), group).build();
+ private MongoClientSettings buildMongoClientOptions(String group) {
+ return buildMongoClientOptions(MongoClientSettings.builder(), group).build();
}
/**
@@ -332,7 +337,7 @@ public class MongoDS implements Closeable {
* @param group 分组,当分组对应的选项不存在时会读取根选项,如果也不存在使用默认值
* @return Builder
*/
- private Builder buildMongoClientOptions(Builder builder, String group) {
+ private MongoClientSettings.Builder buildMongoClientOptions(MongoClientSettings.Builder builder, String group) {
if (setting == null) {
return builder;
}
@@ -348,8 +353,9 @@ public class MongoDS implements Closeable {
if (StrUtil.isBlank(group) == false && connectionsPerHost == null) {
connectionsPerHost = setting.getInt("connectionsPerHost");
}
+ ConnectionPoolSettings.Builder connectionPoolSettingsBuilder = ConnectionPoolSettings.builder();
if (connectionsPerHost != null) {
- builder.connectionsPerHost(connectionsPerHost);
+ connectionPoolSettingsBuilder.maxConnecting(connectionsPerHost);
log.debug("MongoDB connectionsPerHost: {}", connectionsPerHost);
}
@@ -359,9 +365,10 @@ public class MongoDS implements Closeable {
setting.getInt("connectTimeout");
}
if (connectTimeout != null) {
- builder.connectTimeout(connectTimeout);
+ connectionPoolSettingsBuilder.maxWaitTime(connectTimeout, TimeUnit.MILLISECONDS);
log.debug("MongoDB connectTimeout: {}", connectTimeout);
}
+ builder.applyToConnectionPoolSettings(b -> b.applySettings(connectionPoolSettingsBuilder.build()));
// 套接字超时时间;该值会被传递给Socket.setSoTimeout(int)。默以为0(无穷) --int
Integer socketTimeout = setting.getInt(group + "socketTimeout");
@@ -369,7 +376,8 @@ public class MongoDS implements Closeable {
setting.getInt("socketTimeout");
}
if (socketTimeout != null) {
- builder.socketTimeout(socketTimeout);
+ SocketSettings socketSettings = SocketSettings.builder().connectTimeout(socketTimeout, TimeUnit.MILLISECONDS).build();
+ builder.applyToSocketSettings(b -> b.applySettings(socketSettings));
log.debug("MongoDB socketTimeout: {}", socketTimeout);
}
--
Gitee
From 206de3bad0493dbe699065aa6ce2855bfa107368 Mon Sep 17 00:00:00 2001
From: MCP
Date: Thu, 10 Mar 2022 15:45:43 +0800
Subject: [PATCH 32/57] =?UTF-8?q?=E9=98=BF=E6=8B=89=E4=BC=AF=E6=95=B0?=
=?UTF-8?q?=E5=AD=97=E8=BD=AC=E6=8D=A2=E6=88=90=E4=B8=AD=E6=96=87=E5=AF=B9?=
=?UTF-8?q?=E5=8F=91=E7=A5=A8=E7=A5=A8=E9=9D=A2=E9=87=91=E9=A2=9D=E8=BD=AC?=
=?UTF-8?q?=E6=8D=A2=E7=9A=84=E6=89=A9=E5=B1=95?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.../core/convert/NumberChineseFormatter.java | 107 +++++++++++-------
1 file changed, 64 insertions(+), 43 deletions(-)
diff --git a/hutool-core/src/main/java/cn/hutool/core/convert/NumberChineseFormatter.java b/hutool-core/src/main/java/cn/hutool/core/convert/NumberChineseFormatter.java
index 2a315d38dc..5f8a7b52fc 100644
--- a/hutool-core/src/main/java/cn/hutool/core/convert/NumberChineseFormatter.java
+++ b/hutool-core/src/main/java/cn/hutool/core/convert/NumberChineseFormatter.java
@@ -40,7 +40,6 @@ public class NumberChineseFormatter {
new ChineseUnit('亿', 1_0000_0000, true),
};
-
/**
* 阿拉伯数字转换成中文,小数点后四舍五入保留两位. 使用于整数、小数的转换.
*
@@ -53,15 +52,25 @@ public class NumberChineseFormatter {
}
/**
- * 阿拉伯数字转换成中文,小数点后四舍五入保留两位. 使用于整数、小数的转换.
+ * 阿拉伯数字转换成中文.
+ *
+ * 主要是对发票票面金额转换的扩展
+ * 如:-12.32
+ * 发票票面转换为:(负数)壹拾贰圆叁角贰分
+ * 而非:负壹拾贰元叁角贰分
+ * 共两点不同:1、(负数) 而非 负;2、圆 而非 元
+ * 2022/3/9
*
* @param amount 数字
* @param isUseTraditional 是否使用繁体
- * @param isMoneyMode 是否为金额模式
- * @return 中文
+ * @param isMoneyMode 是否金额模式
+ * @param negativeName 负号转换名称 如:负、(负数)
+ * @param unitName 单位名称 如:元、圆
+ * @return java.lang.String
+ * @author machuanpeng
*/
- public static String format(double amount, boolean isUseTraditional, boolean isMoneyMode) {
- if(0 == amount){
+ public static String format(double amount, boolean isUseTraditional, boolean isMoneyMode, String negativeName, String unitName) {
+ if (0 == amount) {
return "零";
}
Assert.checkBetween(amount, -99_9999_9999_9999.99, 99_9999_9999_9999.99,
@@ -71,7 +80,7 @@ public class NumberChineseFormatter {
// 负数
if (amount < 0) {
- chineseStr.append("负");
+ chineseStr.append(StrUtil.isNullOrUndefined(negativeName) ? "负" : negativeName);
amount = -amount;
}
@@ -82,44 +91,44 @@ public class NumberChineseFormatter {
yuan = yuan / 10;
// 元
- if(false == isMoneyMode || 0 != yuan){
+ if (false == isMoneyMode || 0 != yuan) {
// 金额模式下,无需“零元”
chineseStr.append(longToChinese(yuan, isUseTraditional));
- if(isMoneyMode){
- chineseStr.append("元");
+ if (isMoneyMode) {
+ chineseStr.append(StrUtil.isNullOrUndefined(unitName) ? "元" : unitName);
}
}
- if(0 == jiao && 0 == fen){
+ if (0 == jiao && 0 == fen) {
//无小数部分的金额结尾
- if(isMoneyMode){
+ if (isMoneyMode) {
chineseStr.append("整");
}
return chineseStr.toString();
}
// 小数部分
- if(false == isMoneyMode){
+ if (false == isMoneyMode) {
chineseStr.append("点");
}
// 角
- if(0 == yuan && 0 == jiao){
+ if (0 == yuan && 0 == jiao) {
// 元和角都为0时,只有非金额模式下补“零”
- if(false == isMoneyMode){
+ if (false == isMoneyMode) {
chineseStr.append("零");
}
- }else{
+ } else {
chineseStr.append(numberToChinese(jiao, isUseTraditional));
- if(isMoneyMode && 0 != jiao){
+ if (isMoneyMode && 0 != jiao) {
chineseStr.append("角");
}
}
// 分
- if(0 != fen){
+ if (0 != fen) {
chineseStr.append(numberToChinese(fen, isUseTraditional));
- if(isMoneyMode){
+ if (isMoneyMode) {
chineseStr.append("分");
}
}
@@ -127,16 +136,28 @@ public class NumberChineseFormatter {
return chineseStr.toString();
}
+ /**
+ * 阿拉伯数字转换成中文,小数点后四舍五入保留两位. 使用于整数、小数的转换.
+ *
+ * @param amount 数字
+ * @param isUseTraditional 是否使用繁体
+ * @param isMoneyMode 是否为金额模式
+ * @return 中文
+ */
+ public static String format(double amount, boolean isUseTraditional, boolean isMoneyMode) {
+ return format(amount, isUseTraditional, isMoneyMode, "负", "元");
+ }
+
/**
* 阿拉伯数字(支持正负整数)转换成中文
*
- * @param amount 数字
+ * @param amount 数字
* @param isUseTraditional 是否使用繁体
* @return 中文
* @since 5.7.17
*/
- public static String format(long amount, boolean isUseTraditional){
- if(0 == amount){
+ public static String format(long amount, boolean isUseTraditional) {
+ if (0 == amount) {
return "零";
}
Assert.checkBetween(amount, -99_9999_9999_9999.99, 99_9999_9999_9999.99,
@@ -179,16 +200,16 @@ public class NumberChineseFormatter {
* 格式化-999~999之间的数字
* 这个方法显示10~19以下的数字时使用"十一"而非"一十一"。
*
- * @param amount 数字
+ * @param amount 数字
* @param isUseTraditional 是否使用繁体
* @return 中文
* @since 5.7.17
*/
- public static String formatThousand(int amount, boolean isUseTraditional){
+ public static String formatThousand(int amount, boolean isUseTraditional) {
Assert.checkBetween(amount, -999, 999, "Number support only: (-999 ~ 999)!");
final String chinese = thousandToChinese(amount, isUseTraditional);
- if(amount < 20 && amount >= 10){
+ if (amount < 20 && amount >= 10) {
// "十一"而非"一十一"
return chinese.substring(1);
}
@@ -218,7 +239,7 @@ public class NumberChineseFormatter {
* @return 中文
*/
private static String longToChinese(long amount, boolean isUseTraditional) {
- if(0 == amount){
+ if (0 == amount) {
return "零";
}
@@ -235,11 +256,11 @@ public class NumberChineseFormatter {
// 千
partValue = parts[0];
- if(partValue > 0){
+ if (partValue > 0) {
partChinese = thousandToChinese(partValue, isUseTraditional);
chineseStr.insert(0, partChinese);
- if(partValue < 1000){
+ if (partValue < 1000) {
// 和万位之间空0,则补零,如一万零三百
addPreZero(chineseStr);
}
@@ -247,26 +268,26 @@ public class NumberChineseFormatter {
// 万
partValue = parts[1];
- if(partValue > 0){
- if((partValue % 10 == 0 && parts[0] > 0)){
+ if (partValue > 0) {
+ if ((partValue % 10 == 0 && parts[0] > 0)) {
// 如果"万"的个位是0,则补零,如十万零八千
addPreZero(chineseStr);
}
partChinese = thousandToChinese(partValue, isUseTraditional);
chineseStr.insert(0, partChinese + "万");
- if(partValue < 1000){
+ if (partValue < 1000) {
// 和亿位之间空0,则补零,如一亿零三百万
addPreZero(chineseStr);
}
- } else{
+ } else {
addPreZero(chineseStr);
}
// 亿
partValue = parts[2];
- if(partValue > 0){
- if((partValue % 10 == 0 && parts[1] > 0)){
+ if (partValue > 0) {
+ if ((partValue % 10 == 0 && parts[1] > 0)) {
// 如果"万"的个位是0,则补零,如十万零八千
addPreZero(chineseStr);
}
@@ -274,25 +295,25 @@ public class NumberChineseFormatter {
partChinese = thousandToChinese(partValue, isUseTraditional);
chineseStr.insert(0, partChinese + "亿");
- if(partValue < 1000){
+ if (partValue < 1000) {
// 和万亿位之间空0,则补零,如一万亿零三百亿
addPreZero(chineseStr);
}
- } else{
+ } else {
addPreZero(chineseStr);
}
// 万亿
partValue = parts[3];
- if(partValue > 0){
- if(parts[2] == 0){
+ if (partValue > 0) {
+ if (parts[2] == 0) {
chineseStr.insert(0, "亿");
}
partChinese = thousandToChinese(partValue, isUseTraditional);
chineseStr.insert(0, partChinese + "万");
}
- if(StrUtil.isNotEmpty(chineseStr) && '零' == chineseStr.charAt(0)){
+ if (StrUtil.isNotEmpty(chineseStr) && '零' == chineseStr.charAt(0)) {
return chineseStr.substring(1);
}
@@ -386,7 +407,7 @@ public class NumberChineseFormatter {
} else {
// 非节单位,和单位前的单数字组合为值
int unitNumber = number;
- if(0 == number && 0 == i){
+ if (0 == number && 0 == i) {
// issue#1726,对于单位开头的数组,默认赋予1
// 十二 -> 一十二
// 百二 -> 一百二
@@ -502,12 +523,12 @@ public class NumberChineseFormatter {
}
}
- private static void addPreZero(StringBuilder chineseStr){
- if(StrUtil.isEmpty(chineseStr)){
+ private static void addPreZero(StringBuilder chineseStr) {
+ if (StrUtil.isEmpty(chineseStr)) {
return;
}
final char c = chineseStr.charAt(0);
- if('零' != c){
+ if ('零' != c) {
chineseStr.insert(0, '零');
}
}
--
Gitee
From 04ae795ce651589223a85e89246bc9bd08c82504 Mon Sep 17 00:00:00 2001
From: jiazhengquan <2466896229@qq.com>
Date: Fri, 11 Mar 2022 09:50:37 +0800
Subject: [PATCH 33/57] =?UTF-8?q?=E9=92=88=E5=AF=B9issue#I4X9TT=E4=BF=AE?=
=?UTF-8?q?=E5=A4=8D?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
hutool-core/src/main/java/cn/hutool/core/text/NamingCase.java | 2 +-
hutool-core/src/test/java/cn/hutool/core/util/StrUtilTest.java | 1 +
2 files changed, 2 insertions(+), 1 deletion(-)
diff --git a/hutool-core/src/main/java/cn/hutool/core/text/NamingCase.java b/hutool-core/src/main/java/cn/hutool/core/text/NamingCase.java
index a35d78f1ee..a5c7a5d521 100644
--- a/hutool-core/src/main/java/cn/hutool/core/text/NamingCase.java
+++ b/hutool-core/src/main/java/cn/hutool/core/text/NamingCase.java
@@ -97,7 +97,7 @@ public class NamingCase {
// 后一个为大写,按照专有名词对待,如aBC -> a_BC
} else {
//前一个为大写
- if (null == nextChar || Character.isLowerCase(nextChar)) {
+ if (null != nextChar && Character.isLowerCase(nextChar)) {
// 普通首字母大写,如ABcc -> A_bcc
sb.append(symbol);
c = Character.toLowerCase(c);
diff --git a/hutool-core/src/test/java/cn/hutool/core/util/StrUtilTest.java b/hutool-core/src/test/java/cn/hutool/core/util/StrUtilTest.java
index ee1e1da509..2dcef2850a 100644
--- a/hutool-core/src/test/java/cn/hutool/core/util/StrUtilTest.java
+++ b/hutool-core/src/test/java/cn/hutool/core/util/StrUtilTest.java
@@ -422,6 +422,7 @@ public class StrUtilTest {
.set("H2", "H2")
.set("H#case", "H#case")
.set("PNLabel", "PN_label")
+ .set("DEPT_NAME","DEPT_NAME")
.forEach((key, value) -> Assert.assertEquals(value, StrUtil.toUnderlineCase(key)));
}
--
Gitee
From 78f886075eb45b1fdeca88cc0ace0f9aefcc4ff5 Mon Sep 17 00:00:00 2001
From: jiazhengquan <2466896229@qq.com>
Date: Fri, 11 Mar 2022 17:51:09 +0800
Subject: [PATCH 34/57] =?UTF-8?q?issue=20#I4XG4L=20=E5=88=9D=E7=89=88?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.../java/cn/hutool/core/util/ArrayUtil.java | 37 +++++++++++++++++++
1 file changed, 37 insertions(+)
diff --git a/hutool-core/src/main/java/cn/hutool/core/util/ArrayUtil.java b/hutool-core/src/main/java/cn/hutool/core/util/ArrayUtil.java
index 8c948e8a92..01e4ea721b 100644
--- a/hutool-core/src/main/java/cn/hutool/core/util/ArrayUtil.java
+++ b/hutool-core/src/main/java/cn/hutool/core/util/ArrayUtil.java
@@ -368,6 +368,43 @@ public class ArrayUtil extends PrimitiveArrayUtil {
}
}
+ /**
+ * 将元素值设置为数组的某个位置,根据元素顺序添加
+ * 当给定的index大于数组长度,则追加
+ *
+ * @param 数组元素类型
+ * @param buffer 已有数组
+ * @param index 位置,大于长度追加,否则替换
+ * @param values 新值
+ * @return 新数组或原有数组
+ */
+ public static T[] replace(T[] buffer, int index, T... values) {
+ return index == 0 ? values : replaceBy(buffer, index, values);
+ }
+
+ /**
+ * 将元素值设置为数组的某个位置,根据元素顺序添加
+ * 当给定的index大于数组长度,则追加
+ *
+ * @param 数组元素类型
+ * @param buffer 已有数组
+ * @param index 位置,大于长度追加,否则替换
+ * @param values 新值
+ * @return 新数组或原有数组
+ */
+ public static T[] replaceBy(T[] buffer, int index, T... values) {
+ if (index < buffer.length && buffer.length - index - 1 >= values.length) {
+ for (int i = index; i < values.length; i++) {
+ Array.set(buffer, index, values[i]);
+ }
+ return buffer;
+ } else {
+ final T[] result = (T[]) Array.newInstance(buffer.getClass().getComponentType(), buffer.length - index - 1);
+ System.arraycopy(buffer, 0, result, 0, buffer.length - index - 1);
+ return append(result, values);
+ }
+ }
+
/**
* 将新元素插入到到已有数组中的某个位置
* 添加新元素会生成一个新的数组,不影响原数组
--
Gitee
From 55c9b0c37a6f124f3bd97e15f357de84cc2d7a36 Mon Sep 17 00:00:00 2001
From: jcsun <80867809@qq.com>
Date: Sun, 13 Mar 2022 18:14:36 +0800
Subject: [PATCH 35/57] =?UTF-8?q?=E6=B7=BB=E5=8A=A0=20Spring=20Task?=
=?UTF-8?q?=E9=85=8D=E7=BD=AE?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.../extra/spring/config/SpringCronConfig.java | 31 +++++++++++++++++++
.../main/resources/META-INF/spring.factories | 3 +-
2 files changed, 33 insertions(+), 1 deletion(-)
create mode 100644 hutool-extra/src/main/java/cn/hutool/extra/spring/config/SpringCronConfig.java
diff --git a/hutool-extra/src/main/java/cn/hutool/extra/spring/config/SpringCronConfig.java b/hutool-extra/src/main/java/cn/hutool/extra/spring/config/SpringCronConfig.java
new file mode 100644
index 0000000000..1f31d08320
--- /dev/null
+++ b/hutool-extra/src/main/java/cn/hutool/extra/spring/config/SpringCronConfig.java
@@ -0,0 +1,31 @@
+package cn.hutool.extra.spring.config;
+
+import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.scheduling.TaskScheduler;
+import org.springframework.scheduling.annotation.EnableScheduling;
+import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler;
+
+/**
+ * 可自行配置任务线程池, 修改默认参数
+ *
+ * @author JC
+ * @date 03/13
+ */
+@Configuration
+@EnableScheduling
+public class SpringCronConfig {
+ @Bean
+ @ConditionalOnMissingBean(value = TaskScheduler.class)
+ public TaskScheduler taskScheduler() {
+ ThreadPoolTaskScheduler scheduler = new ThreadPoolTaskScheduler();
+ // 任务线程池初始化
+ scheduler.setThreadNamePrefix("TaskScheduler-");
+ scheduler.setPoolSize(Runtime.getRuntime().availableProcessors() / 3 + 1);
+
+ // 保证能立刻丢弃运行中的任务
+ scheduler.setRemoveOnCancelPolicy(true);
+ return scheduler;
+ }
+}
diff --git a/hutool-extra/src/main/resources/META-INF/spring.factories b/hutool-extra/src/main/resources/META-INF/spring.factories
index 7b08ebdf82..06d39a3ecb 100644
--- a/hutool-extra/src/main/resources/META-INF/spring.factories
+++ b/hutool-extra/src/main/resources/META-INF/spring.factories
@@ -1,3 +1,4 @@
# Auto Configure
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
-cn.hutool.extra.spring.SpringUtil
\ No newline at end of file
+cn.hutool.extra.spring.SpringUtil,\
+cn.hutool.extra.spring.config.SpringCronConfig
--
Gitee
From c73ea53f8f504f0d6e46f7d5c308040158bb090d Mon Sep 17 00:00:00 2001
From: jcsun <80867809@qq.com>
Date: Sun, 13 Mar 2022 18:30:34 +0800
Subject: [PATCH 36/57] =?UTF-8?q?=E6=B7=BB=E5=8A=A0=20=E5=8A=A8=E6=80=81?=
=?UTF-8?q?=E5=AE=9A=E6=97=B6=E4=BB=BB=E5=8A=A1=20=E5=B7=A5=E5=85=B7?=
=?UTF-8?q?=E7=B1=BB?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.../hutool/extra/spring/SpringCronUtil.java | 157 ++++++++++++++++++
.../main/resources/META-INF/spring.factories | 3 +-
2 files changed, 159 insertions(+), 1 deletion(-)
create mode 100644 hutool-extra/src/main/java/cn/hutool/extra/spring/SpringCronUtil.java
diff --git a/hutool-extra/src/main/java/cn/hutool/extra/spring/SpringCronUtil.java b/hutool-extra/src/main/java/cn/hutool/extra/spring/SpringCronUtil.java
new file mode 100644
index 0000000000..01947029a5
--- /dev/null
+++ b/hutool-extra/src/main/java/cn/hutool/extra/spring/SpringCronUtil.java
@@ -0,0 +1,157 @@
+package cn.hutool.extra.spring;
+
+import cn.hutool.core.collection.CollUtil;
+import cn.hutool.core.map.MapUtil;
+import cn.hutool.core.util.IdUtil;
+import org.springframework.scheduling.TaskScheduler;
+import org.springframework.scheduling.support.CronTrigger;
+import org.springframework.stereotype.Component;
+
+import javax.annotation.Resource;
+import java.io.Serializable;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.ScheduledFuture;
+
+/**
+ * Spring 动态定时任务封装
+ *
+ * 创建定时任务
+ * 修改定时任务
+ * 取消定时任务
+ * 高级操作
+ *
+ *
+ * @author JC
+ * @date 03/13
+ */
+@Component
+public class SpringCronUtil {
+ /**
+ * 任务调度器
+ */
+ private static TaskScheduler taskScheduler;
+
+ /**
+ * ID 与 Future 绑定
+ */
+ private static final Map> TASK_FUTURE = MapUtil.newConcurrentHashMap();
+
+ /**
+ * ID 与 Runnable 绑定
+ */
+ private static final Map TASK_RUNNABLE = MapUtil.newConcurrentHashMap();
+
+ /**
+ * 加入定时任务
+ *
+ * @param task 任务
+ * @param expression 定时任务执行时间的cron表达式
+ * @return 定时任务ID
+ */
+ public static String schedule(Runnable task, String expression) {
+ String id = IdUtil.fastUUID();
+ return schedule(id, task, expression);
+ }
+
+ /**
+ * 加入定时任务
+ *
+ * @param id 定时任务ID
+ * @param expression 定时任务执行时间的cron表达式
+ * @param task 任务
+ * @return 定时任务ID
+ */
+ public static String schedule(Serializable id, Runnable task, String expression) {
+ ScheduledFuture> schedule = taskScheduler.schedule(task, new CronTrigger(expression));
+ TASK_FUTURE.put(id, schedule);
+ TASK_RUNNABLE.put(id, task);
+ return id.toString();
+ }
+
+ /**
+ * 修改定时任务
+ *
+ * @param id 定时任务ID
+ * @param expression 定时任务执行时间的cron表达式
+ * @return 是否修改成功,{@code false}表示未找到对应ID的任务
+ */
+ public static boolean update(Serializable id, String expression) {
+ if (!TASK_FUTURE.containsKey(id)) {
+ return false;
+ }
+ ScheduledFuture> future = TASK_FUTURE.get(id);
+ if (future == null) {
+ return false;
+ }
+ future.cancel(true);
+ schedule(id, TASK_RUNNABLE.get(id), expression);
+ return true;
+ }
+
+ /**
+ * 移除任务
+ *
+ * @param schedulerId 任务ID
+ * @return 是否移除成功,{@code false}表示未找到对应ID的任务
+ */
+ public static boolean cancel(Serializable schedulerId) {
+ ScheduledFuture> future = getScheduledFuture(schedulerId);
+ if (future == null) {
+ return false;
+ }
+ boolean cancel = future.cancel(false);
+ if (cancel) {
+ TASK_FUTURE.remove(schedulerId);
+ TASK_RUNNABLE.remove(schedulerId);
+ }
+ return cancel;
+ }
+
+ @Resource
+ public void setTaskScheduler(TaskScheduler taskScheduler) {
+ SpringCronUtil.taskScheduler = taskScheduler;
+ }
+
+ /**
+ * @return 获得Scheduler对象
+ */
+ public static TaskScheduler getScheduler() {
+ return taskScheduler;
+ }
+
+ /**
+ * 可在项目中 进行细粒度控制
+ *
+ * @return 获得ScheduledFuture对象
+ */
+ private static ScheduledFuture> getScheduledFuture(Serializable id) {
+ return TASK_FUTURE.get(id);
+ }
+
+ /**
+ * 获得当前运行的所有任务
+ *
+ * @return 所有任务
+ */
+ public static List getAllTask() {
+ if (CollUtil.isNotEmpty(TASK_FUTURE.keySet())) {
+ return new ArrayList<>(TASK_FUTURE.keySet());
+ }
+ return new ArrayList<>();
+ }
+
+ /**
+ * 取消所有的任务
+ */
+ public static void destroy() {
+ for (ScheduledFuture> future : TASK_FUTURE.values()) {
+ if (future != null) {
+ future.cancel(true);
+ }
+ }
+ TASK_FUTURE.clear();
+ TASK_RUNNABLE.clear();
+ }
+}
diff --git a/hutool-extra/src/main/resources/META-INF/spring.factories b/hutool-extra/src/main/resources/META-INF/spring.factories
index 06d39a3ecb..0f0032ef8a 100644
--- a/hutool-extra/src/main/resources/META-INF/spring.factories
+++ b/hutool-extra/src/main/resources/META-INF/spring.factories
@@ -1,4 +1,5 @@
# Auto Configure
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
cn.hutool.extra.spring.SpringUtil,\
-cn.hutool.extra.spring.config.SpringCronConfig
+cn.hutool.extra.spring.config.SpringCronConfig,\
+cn.hutool.extra.spring.SpringCronUtil
--
Gitee
From 57212bfdbac019b6e3eb050271ca6ed973585580 Mon Sep 17 00:00:00 2001
From: jcsun <80867809@qq.com>
Date: Sun, 13 Mar 2022 18:40:13 +0800
Subject: [PATCH 37/57] =?UTF-8?q?=E6=B7=BB=E5=8A=A0=20junit=20=E6=B5=8B?=
=?UTF-8?q?=E8=AF=95?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.../java/cn/hutool/extra/spring/SpringCronUtil.java | 13 ++-----------
1 file changed, 2 insertions(+), 11 deletions(-)
diff --git a/hutool-extra/src/main/java/cn/hutool/extra/spring/SpringCronUtil.java b/hutool-extra/src/main/java/cn/hutool/extra/spring/SpringCronUtil.java
index 01947029a5..0599a74fce 100644
--- a/hutool-extra/src/main/java/cn/hutool/extra/spring/SpringCronUtil.java
+++ b/hutool-extra/src/main/java/cn/hutool/extra/spring/SpringCronUtil.java
@@ -97,10 +97,10 @@ public class SpringCronUtil {
* @return 是否移除成功,{@code false}表示未找到对应ID的任务
*/
public static boolean cancel(Serializable schedulerId) {
- ScheduledFuture> future = getScheduledFuture(schedulerId);
- if (future == null) {
+ if (!TASK_FUTURE.containsKey(schedulerId)) {
return false;
}
+ ScheduledFuture> future = TASK_FUTURE.get(schedulerId);
boolean cancel = future.cancel(false);
if (cancel) {
TASK_FUTURE.remove(schedulerId);
@@ -121,15 +121,6 @@ public class SpringCronUtil {
return taskScheduler;
}
- /**
- * 可在项目中 进行细粒度控制
- *
- * @return 获得ScheduledFuture对象
- */
- private static ScheduledFuture> getScheduledFuture(Serializable id) {
- return TASK_FUTURE.get(id);
- }
-
/**
* 获得当前运行的所有任务
*
--
Gitee
From 0fee267e4a2d2959b390fc59dd707cf6e3d05bd3 Mon Sep 17 00:00:00 2001
From: jcsun <80867809@qq.com>
Date: Sun, 13 Mar 2022 18:40:21 +0800
Subject: [PATCH 38/57] =?UTF-8?q?=E6=B7=BB=E5=8A=A0=20junit=20=E6=B5=8B?=
=?UTF-8?q?=E8=AF=95?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.../extra/spring/SpringCronUtilTest.java | 94 +++++++++++++++++++
1 file changed, 94 insertions(+)
create mode 100644 hutool-extra/src/test/java/cn/hutool/extra/spring/SpringCronUtilTest.java
diff --git a/hutool-extra/src/test/java/cn/hutool/extra/spring/SpringCronUtilTest.java b/hutool-extra/src/test/java/cn/hutool/extra/spring/SpringCronUtilTest.java
new file mode 100644
index 0000000000..a3eb1e6159
--- /dev/null
+++ b/hutool-extra/src/test/java/cn/hutool/extra/spring/SpringCronUtilTest.java
@@ -0,0 +1,94 @@
+package cn.hutool.extra.spring;
+
+import cn.hutool.core.date.DateUtil;
+import cn.hutool.extra.spring.config.SpringCronConfig;
+import lombok.SneakyThrows;
+import lombok.extern.slf4j.Slf4j;
+import org.junit.After;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.scheduling.TaskScheduler;
+import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
+
+import java.io.Serializable;
+import java.time.Duration;
+import java.time.Instant;
+import java.util.List;
+
+/**
+ * @author JC
+ * @date 03/13
+ */
+@Slf4j
+@RunWith(SpringJUnit4ClassRunner.class)
+@SpringBootTest(classes = {SpringCronConfig.class, SpringCronUtil.class})
+public class SpringCronUtilTest {
+ /**
+ * 创建一个定时任务
+ * 观察日志可进行验证
+ */
+ @Test
+ public void registerTask() {
+ String ID1 = SpringCronUtil.schedule(this::task, "0/1 * * * * ?");
+ String ID2 = SpringCronUtil.schedule(888, this::task, "0/1 * * * * ?");
+ log.info("taskId: {},{}", ID1, ID2);
+ }
+
+ /**
+ * 修改一个定时任务
+ */
+ @Test
+ @SneakyThrows
+ public void updateTask() {
+ SpringCronUtil.schedule(888, this::task, "0/1 * * * * ?");
+ Thread.sleep(5000);
+ boolean update = SpringCronUtil.update(888, "0/5 * * * * ?");
+ log.info("update task result: {}", update);
+ }
+
+ /**
+ * 取消一个定时任务
+ */
+ @Test
+ @SneakyThrows
+ public void cancelTask() {
+ SpringCronUtil.schedule(888, this::task, "0/1 * * * * ?");
+ Thread.sleep(5000);
+ boolean cancel = SpringCronUtil.cancel(888);
+ log.info("cancel task result: {}", cancel);
+ }
+
+ /**
+ * 高级用法
+ * 参考:
+ */
+ @Test
+ public void senior() {
+ TaskScheduler scheduler = SpringCronUtil.getScheduler();
+ // 给定时间 开始, 间隔时间..
+ scheduler.scheduleAtFixedRate(this::task, Instant.now(), Duration.ofMinutes(10));
+ // ...
+ }
+
+ /**
+ * 取消全部定时任务
+ * 查看当前所有的任务
+ */
+ @After
+ @SneakyThrows
+ public void cancelAll() {
+ Thread.sleep(10000);
+ List allTask = SpringCronUtil.getAllTask();
+ log.info("allTask: {}", allTask);
+
+ SpringCronUtil.destroy();
+
+ allTask = SpringCronUtil.getAllTask();
+ log.info("allTask: {}", allTask);
+ }
+
+ private void task() {
+ log.info("information only.. (date:{})", DateUtil.now());
+ }
+}
--
Gitee
From b730ccf47070986956173fcca25a4e2d90bbe013 Mon Sep 17 00:00:00 2001
From: jcsun <80867809@qq.com>
Date: Sun, 13 Mar 2022 18:44:31 +0800
Subject: [PATCH 39/57] =?UTF-8?q?=E6=B7=BB=E5=8A=A0=20=E5=8F=82=E8=80=83?=
=?UTF-8?q?=E9=93=BE=E6=8E=A5?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.../src/main/java/cn/hutool/extra/spring/SpringCronUtil.java | 1 +
.../test/java/cn/hutool/extra/spring/SpringCronUtilTest.java | 2 +-
2 files changed, 2 insertions(+), 1 deletion(-)
diff --git a/hutool-extra/src/main/java/cn/hutool/extra/spring/SpringCronUtil.java b/hutool-extra/src/main/java/cn/hutool/extra/spring/SpringCronUtil.java
index 0599a74fce..29287bdd8a 100644
--- a/hutool-extra/src/main/java/cn/hutool/extra/spring/SpringCronUtil.java
+++ b/hutool-extra/src/main/java/cn/hutool/extra/spring/SpringCronUtil.java
@@ -22,6 +22,7 @@ import java.util.concurrent.ScheduledFuture;
* 取消定时任务
* 高级操作
*
+ * 参考:Spring doc
*
* @author JC
* @date 03/13
diff --git a/hutool-extra/src/test/java/cn/hutool/extra/spring/SpringCronUtilTest.java b/hutool-extra/src/test/java/cn/hutool/extra/spring/SpringCronUtilTest.java
index a3eb1e6159..e4959da270 100644
--- a/hutool-extra/src/test/java/cn/hutool/extra/spring/SpringCronUtilTest.java
+++ b/hutool-extra/src/test/java/cn/hutool/extra/spring/SpringCronUtilTest.java
@@ -61,7 +61,7 @@ public class SpringCronUtilTest {
/**
* 高级用法
- * 参考:
+ * 参考:Spring doc
*/
@Test
public void senior() {
--
Gitee
From 056b501e8e645c1eb991735e20a595b7bf3462b7 Mon Sep 17 00:00:00 2001
From: VampireAchao
Date: Sun, 13 Mar 2022 21:34:59 +0800
Subject: [PATCH 40/57] =?UTF-8?q?=E6=8F=90=E4=BA=A4=E6=B5=8B=E8=AF=95?=
=?UTF-8?q?=E7=94=A8=E4=BE=8B?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.../java/cn/hutool/db/nosql/MongoDBTest.java | 20 +++++++++++++++++++
.../src/test/resources/config/mongo.setting | 20 +++++++++++++++++++
2 files changed, 40 insertions(+)
create mode 100644 hutool-db/src/test/java/cn/hutool/db/nosql/MongoDBTest.java
create mode 100644 hutool-db/src/test/resources/config/mongo.setting
diff --git a/hutool-db/src/test/java/cn/hutool/db/nosql/MongoDBTest.java b/hutool-db/src/test/java/cn/hutool/db/nosql/MongoDBTest.java
new file mode 100644
index 0000000000..278431b096
--- /dev/null
+++ b/hutool-db/src/test/java/cn/hutool/db/nosql/MongoDBTest.java
@@ -0,0 +1,20 @@
+package cn.hutool.db.nosql;
+
+import cn.hutool.db.nosql.mongo.MongoFactory;
+import com.mongodb.client.MongoDatabase;
+import org.junit.Assert;
+import org.junit.Ignore;
+import org.junit.Test;
+
+/**
+ * @author VampireAchao
+ */
+public class MongoDBTest {
+
+ @Test
+ @Ignore
+ public void redisDSTest() {
+ MongoDatabase db = MongoFactory.getDS("master").getDb("test");
+ Assert.assertEquals("test", db.getName());
+ }
+}
diff --git a/hutool-db/src/test/resources/config/mongo.setting b/hutool-db/src/test/resources/config/mongo.setting
new file mode 100644
index 0000000000..dc5ae2b337
--- /dev/null
+++ b/hutool-db/src/test/resources/config/mongo.setting
@@ -0,0 +1,20 @@
+#每个主机答应的连接数(每个主机的连接池大小),当连接池被用光时,会被阻塞住 ,默以为10 --int
+connectionsPerHost=100
+#线程队列数,它以connectionsPerHost值相乘的结果就是线程队列最大值。如果连接线程排满了队列就会抛出“Out of semaphores to get db”错误 --int
+threadsAllowedToBlockForConnectionMultiplier=10
+#被阻塞线程从连接池获取连接的最长等待时间(ms) --int
+maxWaitTime = 120000
+#在建立(打开)套接字连接时的超时时间(ms),默以为0(无穷) --int
+connectTimeout=0
+#套接字超时时间;该值会被传递给Socket.setSoTimeout(int)。默以为0(无穷) --int
+socketTimeout=0
+#是否打开长连接. defaults to false --boolean
+socketKeepAlive=false
+
+#---------------------------------- MongoDB实例连接
+[master]
+host = 127.0.0.1:27017
+
+[slave]
+host = 127.0.0.1:27018
+#-----------------------------------------------------
--
Gitee
From 9e2fa7d2b61293c246333ba07a1308628dbb1b1f Mon Sep 17 00:00:00 2001
From: VampireAchao
Date: Sun, 13 Mar 2022 21:59:27 +0800
Subject: [PATCH 41/57] =?UTF-8?q?=E5=A4=AA=E6=A3=92=E4=BA=86=EF=BC=81?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
hutool-db/pom.xml | 11 +-
.../cn/hutool/db/nosql/mongo/MongoDS.java | 44 +-
.../cn/hutool/db/nosql/mongo/MongoDS4.java | 404 ++++++++++++++++++
.../hutool/db/nosql/mongo/MongoFactory4.java | 120 ++++++
.../java/cn/hutool/db/nosql/MongoDBTest.java | 4 +-
5 files changed, 553 insertions(+), 30 deletions(-)
create mode 100644 hutool-db/src/main/java/cn/hutool/db/nosql/mongo/MongoDS4.java
create mode 100644 hutool-db/src/main/java/cn/hutool/db/nosql/mongo/MongoFactory4.java
diff --git a/hutool-db/pom.xml b/hutool-db/pom.xml
index c80ea176d8..f14a0cc1b4 100644
--- a/hutool-db/pom.xml
+++ b/hutool-db/pom.xml
@@ -23,7 +23,8 @@
10.0.14
1.2.8
2.4.13
- 4.5.0
+ 3.12.10
+ 4.5.0
3.36.0.3
2.5.2
@@ -99,10 +100,16 @@
| | | | | | | | | | | | | | | | |