From 0e9893d365434cecba0eee8b8babcf273130e676 Mon Sep 17 00:00:00 2001 From: Looly Date: Sat, 9 Oct 2021 23:56:44 +0800 Subject: [PATCH 01/51] prepare 5.7.15 --- 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 +- 26 files changed, 30 insertions(+), 30 deletions(-) diff --git a/README-EN.md b/README-EN.md index 817081b8ca..9783a1a9f4 100644 --- a/README-EN.md +++ b/README-EN.md @@ -124,18 +124,18 @@ Each module can be introduced individually, or all modules can be introduced by cn.hutool hutool-all - 5.7.14 + 5.7.15 ``` ### 🍐Gradle ``` -implementation 'cn.hutool:hutool-all:5.7.14' +implementation 'cn.hutool:hutool-all:5.7.15' ``` ## 📥Download -- [Maven Repo](https://repo1.maven.org/maven2/cn/hutool/hutool-all/5.7.14/) +- [Maven Repo](https://repo1.maven.org/maven2/cn/hutool/hutool-all/5.7.15/) > 🔔️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 62b43a57ab..f9ea97fd92 100644 --- a/README.md +++ b/README.md @@ -122,20 +122,20 @@ Hutool的存在就是为了减少代码搜索成本,避免网络上参差不 cn.hutool hutool-all - 5.7.14 + 5.7.15 ``` ### 🍐Gradle ``` -implementation 'cn.hutool:hutool-all:5.7.14' +implementation 'cn.hutool:hutool-all:5.7.15' ``` ### 📥下载jar 点击以下链接,下载`hutool-all-X.X.X.jar`即可: -- [Maven中央库](https://repo1.maven.org/maven2/cn/hutool/hutool-all/5.7.14/) +- [Maven中央库](https://repo1.maven.org/maven2/cn/hutool/hutool-all/5.7.15/) > 🔔️注意 > Hutool 5.x支持JDK8+,对Android平台没有测试,不能保证所有工具类或工具方法可用。 diff --git a/bin/version.txt b/bin/version.txt index 2ad2244f60..7b9e0e8bb8 100755 --- a/bin/version.txt +++ b/bin/version.txt @@ -1 +1 @@ -5.7.14 +5.7.15 diff --git a/docs/js/version.js b/docs/js/version.js index 71f2db4e9f..36f2fde5c0 100644 --- a/docs/js/version.js +++ b/docs/js/version.js @@ -1 +1 @@ -var version = '5.7.14' \ No newline at end of file +var version = '5.7.15' \ No newline at end of file diff --git a/hutool-all/pom.xml b/hutool-all/pom.xml index 7466d7fbb8..6d1004deb4 100644 --- a/hutool-all/pom.xml +++ b/hutool-all/pom.xml @@ -9,7 +9,7 @@ cn.hutool hutool-parent - 5.7.14 + 5.7.15-SNAPSHOT hutool-all diff --git a/hutool-aop/pom.xml b/hutool-aop/pom.xml index 8aeb93af55..aa06006926 100644 --- a/hutool-aop/pom.xml +++ b/hutool-aop/pom.xml @@ -9,7 +9,7 @@ cn.hutool hutool-parent - 5.7.14 + 5.7.15-SNAPSHOT hutool-aop diff --git a/hutool-bloomFilter/pom.xml b/hutool-bloomFilter/pom.xml index 6d5aca57a8..8554dd99c2 100644 --- a/hutool-bloomFilter/pom.xml +++ b/hutool-bloomFilter/pom.xml @@ -9,7 +9,7 @@ cn.hutool hutool-parent - 5.7.14 + 5.7.15-SNAPSHOT hutool-bloomFilter diff --git a/hutool-bom/pom.xml b/hutool-bom/pom.xml index efa4b7d42f..5d56cbcc3a 100644 --- a/hutool-bom/pom.xml +++ b/hutool-bom/pom.xml @@ -9,7 +9,7 @@ cn.hutool hutool-parent - 5.7.14 + 5.7.15-SNAPSHOT hutool-bom diff --git a/hutool-cache/pom.xml b/hutool-cache/pom.xml index 42faadbade..c2081d8e28 100644 --- a/hutool-cache/pom.xml +++ b/hutool-cache/pom.xml @@ -9,7 +9,7 @@ cn.hutool hutool-parent - 5.7.14 + 5.7.15-SNAPSHOT hutool-cache diff --git a/hutool-captcha/pom.xml b/hutool-captcha/pom.xml index 0636c9a62c..68df03a4a6 100644 --- a/hutool-captcha/pom.xml +++ b/hutool-captcha/pom.xml @@ -9,7 +9,7 @@ cn.hutool hutool-parent - 5.7.14 + 5.7.15-SNAPSHOT hutool-captcha diff --git a/hutool-core/pom.xml b/hutool-core/pom.xml index 817ceed238..af165e71e8 100644 --- a/hutool-core/pom.xml +++ b/hutool-core/pom.xml @@ -9,7 +9,7 @@ cn.hutool hutool-parent - 5.7.14 + 5.7.15-SNAPSHOT hutool-core diff --git a/hutool-cron/pom.xml b/hutool-cron/pom.xml index 2f6d5502c8..0b753dbd74 100644 --- a/hutool-cron/pom.xml +++ b/hutool-cron/pom.xml @@ -9,7 +9,7 @@ cn.hutool hutool-parent - 5.7.14 + 5.7.15-SNAPSHOT hutool-cron diff --git a/hutool-crypto/pom.xml b/hutool-crypto/pom.xml index b352416051..96c365b9e6 100644 --- a/hutool-crypto/pom.xml +++ b/hutool-crypto/pom.xml @@ -9,7 +9,7 @@ cn.hutool hutool-parent - 5.7.14 + 5.7.15-SNAPSHOT hutool-crypto diff --git a/hutool-db/pom.xml b/hutool-db/pom.xml index 2d0c46b64b..b434f61d5b 100644 --- a/hutool-db/pom.xml +++ b/hutool-db/pom.xml @@ -9,7 +9,7 @@ cn.hutool hutool-parent - 5.7.14 + 5.7.15-SNAPSHOT hutool-db diff --git a/hutool-dfa/pom.xml b/hutool-dfa/pom.xml index a2a5ad72d4..77f87ced8a 100644 --- a/hutool-dfa/pom.xml +++ b/hutool-dfa/pom.xml @@ -9,7 +9,7 @@ cn.hutool hutool-parent - 5.7.14 + 5.7.15-SNAPSHOT hutool-dfa diff --git a/hutool-extra/pom.xml b/hutool-extra/pom.xml index 141c2fd167..dee8ad2512 100644 --- a/hutool-extra/pom.xml +++ b/hutool-extra/pom.xml @@ -9,7 +9,7 @@ cn.hutool hutool-parent - 5.7.14 + 5.7.15-SNAPSHOT hutool-extra diff --git a/hutool-http/pom.xml b/hutool-http/pom.xml index 5778eff373..5e8f4ad11b 100644 --- a/hutool-http/pom.xml +++ b/hutool-http/pom.xml @@ -9,7 +9,7 @@ cn.hutool hutool-parent - 5.7.14 + 5.7.15-SNAPSHOT hutool-http diff --git a/hutool-json/pom.xml b/hutool-json/pom.xml index ed5f981687..53479ea030 100644 --- a/hutool-json/pom.xml +++ b/hutool-json/pom.xml @@ -9,7 +9,7 @@ cn.hutool hutool-parent - 5.7.14 + 5.7.15-SNAPSHOT hutool-json diff --git a/hutool-jwt/pom.xml b/hutool-jwt/pom.xml index c3c8e1a661..94f10eb9dd 100644 --- a/hutool-jwt/pom.xml +++ b/hutool-jwt/pom.xml @@ -9,7 +9,7 @@ cn.hutool hutool-parent - 5.7.14 + 5.7.15-SNAPSHOT hutool-jwt diff --git a/hutool-log/pom.xml b/hutool-log/pom.xml index 6f0a6fb6a5..e2e516e6b9 100644 --- a/hutool-log/pom.xml +++ b/hutool-log/pom.xml @@ -9,7 +9,7 @@ cn.hutool hutool-parent - 5.7.14 + 5.7.15-SNAPSHOT hutool-log diff --git a/hutool-poi/pom.xml b/hutool-poi/pom.xml index 931bc7d424..a3e0cc10b5 100644 --- a/hutool-poi/pom.xml +++ b/hutool-poi/pom.xml @@ -9,7 +9,7 @@ cn.hutool hutool-parent - 5.7.14 + 5.7.15-SNAPSHOT hutool-poi diff --git a/hutool-script/pom.xml b/hutool-script/pom.xml index d76f4aa4c4..334119e1ce 100644 --- a/hutool-script/pom.xml +++ b/hutool-script/pom.xml @@ -9,7 +9,7 @@ cn.hutool hutool-parent - 5.7.14 + 5.7.15-SNAPSHOT hutool-script diff --git a/hutool-setting/pom.xml b/hutool-setting/pom.xml index a79515e519..346588b87c 100644 --- a/hutool-setting/pom.xml +++ b/hutool-setting/pom.xml @@ -9,7 +9,7 @@ cn.hutool hutool-parent - 5.7.14 + 5.7.15-SNAPSHOT hutool-setting diff --git a/hutool-socket/pom.xml b/hutool-socket/pom.xml index dce9850615..0f023e57bb 100644 --- a/hutool-socket/pom.xml +++ b/hutool-socket/pom.xml @@ -9,7 +9,7 @@ cn.hutool hutool-parent - 5.7.14 + 5.7.15-SNAPSHOT hutool-socket diff --git a/hutool-system/pom.xml b/hutool-system/pom.xml index 9d9645c60c..0a2badeace 100644 --- a/hutool-system/pom.xml +++ b/hutool-system/pom.xml @@ -9,7 +9,7 @@ cn.hutool hutool-parent - 5.7.14 + 5.7.15-SNAPSHOT hutool-system diff --git a/pom.xml b/pom.xml index e73b78ebf8..7341d1fa86 100644 --- a/pom.xml +++ b/pom.xml @@ -8,7 +8,7 @@ cn.hutool hutool-parent - 5.7.14 + 5.7.15-SNAPSHOT hutool Hutool是一个小而全的Java工具类库,通过静态方法封装,降低相关API的学习成本,提高工作效率,使Java拥有函数式语言般的优雅,让Java语言也可以“甜甜的”。 https://github.com/dromara/hutool From 34caf6a826b18fc36b5eb0fcef02b779ca9e7159 Mon Sep 17 00:00:00 2001 From: Looly Date: Sat, 9 Oct 2021 23:57:09 +0800 Subject: [PATCH 02/51] prepare 5.7.15 --- CHANGELOG.md | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index dbc4719848..3957c6130d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,13 @@ ------------------------------------------------------------------------------------------------------------- +# 5.7.15 (2021-10-09) + +### 🐣新特性 +### 🐞Bug修复 + +------------------------------------------------------------------------------------------------------------- + # 5.7.14 (2021-10-09) ### 🐣新特性 From a81306ef5cc8e881a75b788fe221da411c888ba0 Mon Sep 17 00:00:00 2001 From: Looly Date: Sun, 10 Oct 2021 00:00:49 +0800 Subject: [PATCH 03/51] prepare 5.7.15 --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3957c6130d..7ee4ac3f23 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,7 +3,7 @@ ------------------------------------------------------------------------------------------------------------- -# 5.7.15 (2021-10-09) +# 5.7.15 (2021-10-10) ### 🐣新特性 ### 🐞Bug修复 From 03d40cf4ff9a2852443597f11bd3c0a2bd3b4598 Mon Sep 17 00:00:00 2001 From: zeyu cai Date: Sat, 9 Oct 2021 18:40:43 -0500 Subject: [PATCH 04/51] fix cn.hutool.core.text.StrJoinerTest#joinMultiArrayTest --- .../src/test/java/cn/hutool/core/text/StrJoinerTest.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/hutool-core/src/test/java/cn/hutool/core/text/StrJoinerTest.java b/hutool-core/src/test/java/cn/hutool/core/text/StrJoinerTest.java index 1a5e2df3e5..6da47a54a0 100644 --- a/hutool-core/src/test/java/cn/hutool/core/text/StrJoinerTest.java +++ b/hutool-core/src/test/java/cn/hutool/core/text/StrJoinerTest.java @@ -4,6 +4,7 @@ import cn.hutool.core.collection.ListUtil; import org.junit.Assert; import org.junit.Test; +import org.hamcrest.CoreMatchers; import java.util.ArrayList; import java.util.List; @@ -36,7 +37,7 @@ public void joinMultiArrayTest(){ append.append(new Object[]{ListUtil.of("1", "2"), CollUtil.newHashSet("3", "4") }); - Assert.assertEquals("1,2,3,4", append.toString()); + Assert.assertThat(append.toString(), CoreMatchers.anyOf(CoreMatchers.is("1,2,3,4"), CoreMatchers.is("1,2,4,3"))); } @Test From 0dd57b9e313e4dc69ef92e62c6055872651f53b8 Mon Sep 17 00:00:00 2001 From: Looly Date: Sun, 10 Oct 2021 14:03:04 +0800 Subject: [PATCH 05/51] add null check --- CHANGELOG.md | 1 + hutool-db/src/main/java/cn/hutool/db/Db.java | 41 ++++++++++---------- 2 files changed, 21 insertions(+), 21 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7ee4ac3f23..4c45814e58 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,7 @@ # 5.7.15 (2021-10-10) ### 🐣新特性 +* 【db 】 Db.quietSetAutoCommit增加判空(issue#I4D75B@Gitee) ### 🐞Bug修复 ------------------------------------------------------------------------------------------------------------- diff --git a/hutool-db/src/main/java/cn/hutool/db/Db.java b/hutool-db/src/main/java/cn/hutool/db/Db.java index 4af1b1dcb5..f1b3f23aa9 100644 --- a/hutool-db/src/main/java/cn/hutool/db/Db.java +++ b/hutool-db/src/main/java/cn/hutool/db/Db.java @@ -1,10 +1,5 @@ package cn.hutool.db; -import java.sql.Connection; -import java.sql.SQLException; - -import javax.sql.DataSource; - import cn.hutool.core.lang.func.VoidFunc1; import cn.hutool.db.dialect.Dialect; import cn.hutool.db.dialect.DialectFactory; @@ -13,10 +8,14 @@ import cn.hutool.db.transaction.TransactionLevel; import cn.hutool.log.StaticLog; +import javax.sql.DataSource; +import java.sql.Connection; +import java.sql.SQLException; + /** * 数据库操作类
* 通过给定的数据源执行给定SQL或者给定数据源和方言,执行相应的CRUD操作
- * + * * @author Looly * @since 4.1.2 */ @@ -26,7 +25,7 @@ public class Db extends AbstractDb { /** * 创建Db
* 使用默认数据源,自动探测数据库连接池 - * + * * @return Db */ public static Db use() { @@ -36,7 +35,7 @@ public static Db use() { /** * 创建Db
* 使用默认数据源,自动探测数据库连接池 - * + * * @param group 数据源分组 * @return Db */ @@ -47,7 +46,7 @@ public static Db use(String group) { /** * 创建Db
* 会根据数据源连接的元信息识别目标数据库类型,进而使用合适的数据源 - * + * * @param ds 数据源 * @return Db */ @@ -57,7 +56,7 @@ public static Db use(DataSource ds) { /** * 创建Db - * + * * @param ds 数据源 * @param dialect 方言 * @return Db @@ -68,7 +67,7 @@ public static Db use(DataSource ds, Dialect dialect) { /** * 创建Db - * + * * @param ds 数据源 * @param driverClassName 数据库连接驱动类名 * @return Db @@ -80,7 +79,7 @@ public static Db use(DataSource ds, String driverClassName) { // ---------------------------------------------------------------------------- Constructor start /** * 构造,从DataSource中识别方言 - * + * * @param ds 数据源 */ public Db(DataSource ds) { @@ -89,7 +88,7 @@ public Db(DataSource ds) { /** * 构造 - * + * * @param ds 数据源 * @param driverClassName 数据库连接驱动类名,用于识别方言 */ @@ -99,7 +98,7 @@ public Db(DataSource ds, String driverClassName) { /** * 构造 - * + * * @param ds 数据源 * @param dialect 方言 */ @@ -118,7 +117,7 @@ public Db setWrapper(Character wrapperChar) { public Db setWrapper(Wrapper wrapper) { return (Db) super.setWrapper(wrapper); } - + @Override public Db disableWrapper() { return (Db)super.disableWrapper(); @@ -147,7 +146,7 @@ public void closeConnection(Connection conn) { /** * 执行事务,使用默认的事务级别
* 在同一事务中,所有对数据库操作都是原子的,同时提交或者同时回滚 - * + * * @param func 事务函数,所有操作应在同一函数下执行,确保在同一事务中 * @return this * @throws SQLException SQL异常 @@ -159,7 +158,7 @@ public Db tx(VoidFunc1 func) throws SQLException { /** * 执行事务
* 在同一事务中,所有对数据库操作都是原子的,同时提交或者同时回滚 - * + * * @param transactionLevel 事务级别枚举,null表示使用JDBC默认事务 * @param func 事务函数,所有操作应在同一函数下执行,确保在同一事务中 * @return this @@ -208,7 +207,7 @@ public Db tx(TransactionLevel transactionLevel, VoidFunc1 func) throws SQLEx // ---------------------------------------------------------------------------- Private method start /** * 静默回滚事务 - * + * * @param conn Connection */ private void quietRollback(Connection conn) { @@ -223,12 +222,12 @@ private void quietRollback(Connection conn) { /** * 静默设置自动提交 - * + * * @param conn Connection * @param autoCommit 是否自动提交 */ private void quietSetAutoCommit(Connection conn, Boolean autoCommit) { - if (null != autoCommit) { + if (null != conn && null != autoCommit) { try { conn.setAutoCommit(autoCommit); } catch (Exception e) { @@ -237,4 +236,4 @@ private void quietSetAutoCommit(Connection conn, Boolean autoCommit) { } } // ---------------------------------------------------------------------------- Private method end -} \ No newline at end of file +} From 4552fa27c63d4033b8eb807ab9bf7d055bd6ac91 Mon Sep 17 00:00:00 2001 From: Looly Date: Sun, 10 Oct 2021 14:15:55 +0800 Subject: [PATCH 06/51] fix test --- .../src/test/java/cn/hutool/core/text/StrJoinerTest.java | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/hutool-core/src/test/java/cn/hutool/core/text/StrJoinerTest.java b/hutool-core/src/test/java/cn/hutool/core/text/StrJoinerTest.java index 6da47a54a0..2ce2eee8c6 100644 --- a/hutool-core/src/test/java/cn/hutool/core/text/StrJoinerTest.java +++ b/hutool-core/src/test/java/cn/hutool/core/text/StrJoinerTest.java @@ -4,7 +4,6 @@ import cn.hutool.core.collection.ListUtil; import org.junit.Assert; import org.junit.Test; -import org.hamcrest.CoreMatchers; import java.util.ArrayList; import java.util.List; @@ -35,9 +34,9 @@ public void noJoinTest(){ public void joinMultiArrayTest(){ final StrJoiner append = StrJoiner.of(","); append.append(new Object[]{ListUtil.of("1", "2"), - CollUtil.newHashSet("3", "4") + CollUtil.newLinkedHashSet("3", "4") }); - Assert.assertThat(append.toString(), CoreMatchers.anyOf(CoreMatchers.is("1,2,3,4"), CoreMatchers.is("1,2,4,3"))); + Assert.assertEquals("1,2,3,4", append.toString()); } @Test From e42154de0965b26ffd663933400be43288ce2355 Mon Sep 17 00:00:00 2001 From: handy <13546606929@163.com> Date: Sun, 10 Oct 2021 22:10:35 +0800 Subject: [PATCH 07/51] https://gitee.com/dromara/hutool/issues/I4D7CB MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 修改 --- .../java/cn/hutool/http/useragent/Engine.java | 20 +++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/hutool-http/src/main/java/cn/hutool/http/useragent/Engine.java b/hutool-http/src/main/java/cn/hutool/http/useragent/Engine.java index ed6e57b2dd..358a8e2729 100644 --- a/hutool-http/src/main/java/cn/hutool/http/useragent/Engine.java +++ b/hutool-http/src/main/java/cn/hutool/http/useragent/Engine.java @@ -21,16 +21,16 @@ public class Engine extends UserAgentInfo { /** * 支持的引擎类型 */ - public static final List engines = CollUtil.newArrayList(// - new Engine("Trident", "trident"), // - new Engine("Webkit", "webkit"), // - new Engine("Chrome", "chrome"), // - new Engine("Opera", "opera"), // - new Engine("Presto", "presto"), // - new Engine("Gecko", "gecko"), // - new Engine("KHTML", "khtml"), // - new Engine("Konqeror", "konqueror"), // - new Engine("MIDP", "MIDP")// + public static final List engines = CollUtil.newArrayList( + new Engine("Trident", "trident"), + new Engine("Webkit", "webkit"), + new Engine("Chrome", "chrome"), + new Engine("Opera", "opera"), + new Engine("Presto", "presto"), + new Engine("Gecko", "gecko"), + new Engine("KHTML", "khtml"), + new Engine("Konqueror", "konqueror"), + new Engine("MIDP", "MIDP") ); private final Pattern versionPattern; From fddb24dc9bbd9c8a259df4930f0b40afc318e861 Mon Sep 17 00:00:00 2001 From: Looly Date: Mon, 11 Oct 2021 21:06:30 +0800 Subject: [PATCH 08/51] fix bug --- CHANGELOG.md | 4 +++- .../src/main/java/cn/hutool/core/collection/CollUtil.java | 3 +++ .../src/main/java/cn/hutool/core/collection/IterUtil.java | 6 +++--- 3 files changed, 9 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4c45814e58..1d752a3946 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,11 +3,13 @@ ------------------------------------------------------------------------------------------------------------- -# 5.7.15 (2021-10-10) +# 5.7.15 (2021-10-11) ### 🐣新特性 * 【db 】 Db.quietSetAutoCommit增加判空(issue#I4D75B@Gitee) +* ### 🐞Bug修复 +* 【core 】 修复CollUtil.isEqualList两个null返回错误问题(issue#1885@Github) ------------------------------------------------------------------------------------------------------------- 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 2083ced3db..7ab4a9c23a 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 @@ -2948,6 +2948,9 @@ public static int size(final Object object) { * @since 5.6.0 */ public static boolean isEqualList(final Collection list1, final Collection list2) { + if (list1 == list2){ + return true; + } if (list1 == null || list2 == null || list1.size() != list2.size()) { return false; } 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 ef0991fc89..c538e317ce 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 @@ -819,7 +819,7 @@ public static Iterator trans(Iterator iterator, Function iterable) { + public static int size(Iterable iterable) { if (null == iterable) { return 0; } @@ -838,7 +838,7 @@ public static int size(final Iterable iterable) { * @return Iterator对象的元素数量 * @since 5.5.0 */ - public static int size(final Iterator iterator) { + public static int size(Iterator iterator) { int size = 0; if (iterator != null) { while (iterator.hasNext()) { @@ -862,7 +862,7 @@ public static int size(final Iterator iterator) { * @return 是否相同 * @since 5.6.0 */ - public static boolean isEqualList(final Iterable list1, final Iterable list2) { + public static boolean isEqualList(Iterable list1, Iterable list2) { if (list1 == list2) { return true; } From 85bc21b6c1d1683944ada3e7c7ed1a6655de356a Mon Sep 17 00:00:00 2001 From: Looly Date: Mon, 11 Oct 2021 21:23:55 +0800 Subject: [PATCH 09/51] fix bug --- CHANGELOG.md | 1 + .../main/java/cn/hutool/poi/excel/ExcelWriter.java | 2 -- .../test/java/cn/hutool/poi/excel/ExcelWriteTest.java | 11 +++++++++++ 3 files changed, 12 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1d752a3946..6c772c8123 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,7 @@ * ### 🐞Bug修复 * 【core 】 修复CollUtil.isEqualList两个null返回错误问题(issue#1885@Github) +* 【poi 】 修复ExcelWriter多余调试信息导致的问题(issue#1884@Github) ------------------------------------------------------------------------------------------------------------- diff --git a/hutool-poi/src/main/java/cn/hutool/poi/excel/ExcelWriter.java b/hutool-poi/src/main/java/cn/hutool/poi/excel/ExcelWriter.java index e6a11c2d8b..02663d60b9 100644 --- a/hutool-poi/src/main/java/cn/hutool/poi/excel/ExcelWriter.java +++ b/hutool-poi/src/main/java/cn/hutool/poi/excel/ExcelWriter.java @@ -7,7 +7,6 @@ import cn.hutool.core.io.IORuntimeException; import cn.hutool.core.io.IoUtil; import cn.hutool.core.lang.Assert; -import cn.hutool.core.lang.Console; import cn.hutool.core.map.MapUtil; import cn.hutool.core.util.CharsetUtil; import cn.hutool.core.util.IdUtil; @@ -765,7 +764,6 @@ public ExcelWriter merge(int firstRow, int lastRow, int firstColumn, int lastCol if (null != content) { final Cell cell = getOrCreateCell(firstColumn, firstRow); CellUtil.setCellValue(cell, content, cellStyle); - Console.log("{} {} {}", firstColumn, firstRow, cell.getStringCellValue()); } return this; } diff --git a/hutool-poi/src/test/java/cn/hutool/poi/excel/ExcelWriteTest.java b/hutool-poi/src/test/java/cn/hutool/poi/excel/ExcelWriteTest.java index ad5c3c5fc0..d33fc890b8 100644 --- a/hutool-poi/src/test/java/cn/hutool/poi/excel/ExcelWriteTest.java +++ b/hutool-poi/src/test/java/cn/hutool/poi/excel/ExcelWriteTest.java @@ -783,4 +783,15 @@ public void writeHyperlinkTest(){ writer.write(ListUtil.of(hyperlink)); writer.close(); } + + @Test + @Ignore + public void mergeNumberTest(){ + File tempFile=new File("d:/test/mergeNumber.xlsx"); + FileUtil.del(tempFile); + + BigExcelWriter writer= new BigExcelWriter(tempFile); + writer.merge(0,1,2,2,3.99,false); + writer.close(); + } } From 28fefdeb1b1d2272af3680b8375489db0804ffb7 Mon Sep 17 00:00:00 2001 From: Looly Date: Wed, 13 Oct 2021 01:27:21 +0800 Subject: [PATCH 10/51] add SECURITY --- SECURITY.md | 15 +++++++++++++++ .../java/cn/hutool/core/compress/ZipWriter.java | 8 ++++---- .../cn/hutool/core/compress/ZipWriterTest.java | 11 +++++++++++ 3 files changed, 30 insertions(+), 4 deletions(-) create mode 100644 SECURITY.md diff --git a/SECURITY.md b/SECURITY.md new file mode 100644 index 0000000000..d0c2c55899 --- /dev/null +++ b/SECURITY.md @@ -0,0 +1,15 @@ +# Security Policy + +## Supported Versions(支持的版本) + +| Version | Supported | +| ------- | ------------------ | +| 5.x.x | :white_check_mark: | +| 4.x.x | :x: | +| 3.x.x | :x: | + +## Reporting a Vulnerability(报告漏洞) + +如果你发现有安全问题或漏洞,请发送邮件到`loolly@aliyun.com`。 + +To report any found security issues or vulnerabilities, please send a mail to `loolly@aliyun.com`. \ No newline at end of file diff --git a/hutool-core/src/main/java/cn/hutool/core/compress/ZipWriter.java b/hutool-core/src/main/java/cn/hutool/core/compress/ZipWriter.java index 48962fe5f3..b27d863382 100755 --- a/hutool-core/src/main/java/cn/hutool/core/compress/ZipWriter.java +++ b/hutool-core/src/main/java/cn/hutool/core/compress/ZipWriter.java @@ -26,22 +26,22 @@ public class ZipWriter implements Closeable { /** - * 创建{@link ZipWriter} + * 创建ZipWriter * * @param zipFile 生成的Zip文件 * @param charset 编码 - * @return {@link ZipWriter} + * @return ZipWriter */ public static ZipWriter of(File zipFile, Charset charset) { return new ZipWriter(zipFile, charset); } /** - * 创建{@link ZipWriter} + * 创建ZipWriter * * @param out Zip输出的流,一般为输出文件流 * @param charset 编码 - * @return {@link ZipWriter} + * @return ZipWriter */ public static ZipWriter of(OutputStream out, Charset charset) { return new ZipWriter(out, charset); diff --git a/hutool-core/src/test/java/cn/hutool/core/compress/ZipWriterTest.java b/hutool-core/src/test/java/cn/hutool/core/compress/ZipWriterTest.java index 97bb6af520..c2a2f5bd4c 100755 --- a/hutool-core/src/test/java/cn/hutool/core/compress/ZipWriterTest.java +++ b/hutool-core/src/test/java/cn/hutool/core/compress/ZipWriterTest.java @@ -1,5 +1,8 @@ package cn.hutool.core.compress; +import cn.hutool.core.io.FileUtil; +import cn.hutool.core.io.resource.FileResource; +import cn.hutool.core.util.CharsetUtil; import cn.hutool.core.util.ZipUtil; import org.junit.Ignore; import org.junit.Test; @@ -13,4 +16,12 @@ public class ZipWriterTest { public void zipDirTest() { ZipUtil.zip(new File("d:/test")); } + + @Test + @Ignore + public void addTest(){ + final ZipWriter writer = ZipWriter.of(FileUtil.file("d:/test/test.zip"), CharsetUtil.CHARSET_UTF_8); + writer.add(new FileResource("d:/test/qr_c.png")); + writer.close(); + } } From c3dc4713d3f4cc6ed122c964b9d03aa0af9c29d7 Mon Sep 17 00:00:00 2001 From: Looly Date: Wed, 13 Oct 2021 01:28:41 +0800 Subject: [PATCH 11/51] add codeql --- .github/codeql-analysis.yml | 71 +++++++++++++++++++++++++++++++++++++ 1 file changed, 71 insertions(+) create mode 100644 .github/codeql-analysis.yml diff --git a/.github/codeql-analysis.yml b/.github/codeql-analysis.yml new file mode 100644 index 0000000000..1f58c7a8f0 --- /dev/null +++ b/.github/codeql-analysis.yml @@ -0,0 +1,71 @@ +# For most projects, this workflow file will not need changing; you simply need +# to commit it to your repository. +# +# You may wish to alter this file to override the set of languages analyzed, +# or to provide custom queries or build logic. +# +# ******** NOTE ******** +# We have attempted to detect the languages in your repository. Please check +# the `language` matrix defined below to confirm you have the correct set of +# supported CodeQL languages. +# +name: "CodeQL" + +on: + push: + branches: [ v5-dev ] + pull_request: + # The branches below must be a subset of the branches above + branches: [ v5-dev ] + schedule: + - cron: '45 6 * * 1' + +jobs: + analyze: + name: Analyze + runs-on: ubuntu-latest + permissions: + actions: read + contents: read + security-events: write + + strategy: + fail-fast: false + matrix: + language: [ 'java', 'javascript' ] + # CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python' ] + # Learn more: + # https://docs.github.com/en/free-pro-team@latest/github/finding-security-vulnerabilities-and-errors-in-your-code/configuring-code-scanning#changing-the-languages-that-are-analyzed + + steps: + - name: Checkout repository + uses: actions/checkout@v2 + + # Initializes the CodeQL tools for scanning. + - name: Initialize CodeQL + uses: github/codeql-action/init@v1 + with: + languages: ${{ matrix.language }} + # If you wish to specify custom queries, you can do so here or in a config file. + # By default, queries listed here will override any specified in a config file. + # Prefix the list here with "+" to use these queries and those in the config file. + # queries: ./path/to/local/query, your-org/your-repo/queries@main + + # Autobuild attempts to build any compiled languages (C/C++, C#, or Java). + # If this step fails, then you should remove it and run the build manually (see below) + - name: Autobuild + uses: github/codeql-action/autobuild@v1 + + # ℹ️ Command-line programs to run using the OS shell. + # 📚 https://git.io/JvXDl + + # ✏️ If the Autobuild fails above, remove it and uncomment the following three lines + # and modify them (or add more) to build your code if your project + # uses a compiled language + + #- run: | + # make bootstrap + # make release + + - name: Perform CodeQL Analysis + uses: github/codeql-action/analyze@v1 From 230ec1c479e2f5f63d1227621abd7eae542db026 Mon Sep 17 00:00:00 2001 From: Looly Date: Wed, 13 Oct 2021 10:03:34 +0800 Subject: [PATCH 12/51] add test --- .../src/test/java/cn/hutool/jwt/JWTValidatorTest.java | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/hutool-jwt/src/test/java/cn/hutool/jwt/JWTValidatorTest.java b/hutool-jwt/src/test/java/cn/hutool/jwt/JWTValidatorTest.java index f2aa714e83..b3df4faff6 100644 --- a/hutool-jwt/src/test/java/cn/hutool/jwt/JWTValidatorTest.java +++ b/hutool-jwt/src/test/java/cn/hutool/jwt/JWTValidatorTest.java @@ -69,4 +69,14 @@ public void validateTest(){ boolean validate = JWT.of(token).setKey(key).validate(0); Assert.assertFalse(validate); } + + @Test(expected = ValidateException.class) + public void validateDateTest(){ + final JWT jwt = JWT.create() + .setPayload("id", 123) + .setPayload("username", "hutool") + .setExpiresAt(DateUtil.parse("2021-10-13 09:59:00")); + + JWTValidator.of(jwt).validateDate(DateUtil.date()); + } } From 210ff8882bd625464c03578adb887fde8522a057 Mon Sep 17 00:00:00 2001 From: Uncarbon <4840454+uncarbon97@user.noreply.gitee.com> Date: Wed, 13 Oct 2021 11:04:58 +0800 Subject: [PATCH 13/51] =?UTF-8?q?feat=20=E5=A2=9E=E5=8A=A0checkBetween?= =?UTF-8?q?=E7=9A=84=E5=87=A0=E4=B8=AA=E9=87=8D=E8=BD=BD=E6=96=B9=E6=B3=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../main/java/cn/hutool/core/lang/Assert.java | 104 ++++++++++++++++-- 1 file changed, 97 insertions(+), 7 deletions(-) diff --git a/hutool-core/src/main/java/cn/hutool/core/lang/Assert.java b/hutool-core/src/main/java/cn/hutool/core/lang/Assert.java index 1efdd5aefb..e977351635 100644 --- a/hutool-core/src/main/java/cn/hutool/core/lang/Assert.java +++ b/hutool-core/src/main/java/cn/hutool/core/lang/Assert.java @@ -16,6 +16,9 @@ */ public class Assert { + private static final String TEMPLATE_VALUE_MUST_BE_BETWEEN_AND = "The value must be between {} and {}."; + + /** * 断言是否为真,如果为 {@code false} 抛出给定的异常
* @@ -834,6 +837,38 @@ public static int checkIndex(int index, int size, String errorMsgTemplate, Objec return index; } + /** + * 检查值是否在指定范围内 + * + * @param value 值 + * @param min 最小值(包含) + * @param max 最大值(包含) + * @param errorSupplier 错误抛出异常附带的消息生产接口 + * @throws X if value is out of bound + * @return 经过检查后的值 + * @since 5.7.15 + */ + public static int checkBetween(int value, int min, int max, Supplier errorSupplier) throws X { + if (value < min || value > max) { + throw errorSupplier.get(); + } + + return value; + } + + /** + * 检查值是否在指定范围内 + * + * @param value 值 + * @param min 最小值(包含) + * @param max 最大值(包含) + * @return 经过检查后的值 + * @since 5.7.15 + */ + public static int checkBetween(int value, int min, int max, String errorMsgTemplate, Object... params) { + return checkBetween(value, min, max, () -> new IllegalArgumentException(StrUtil.format(errorMsgTemplate, params))); + } + /** * 检查值是否在指定范围内 * @@ -844,12 +879,41 @@ public static int checkIndex(int index, int size, String errorMsgTemplate, Objec * @since 4.1.10 */ public static int checkBetween(int value, int min, int max) { + return checkBetween(value, min, max, TEMPLATE_VALUE_MUST_BE_BETWEEN_AND, min, max); + } + + /** + * 检查值是否在指定范围内 + * + * @param value 值 + * @param min 最小值(包含) + * @param max 最大值(包含) + * @param errorSupplier 错误抛出异常附带的消息生产接口 + * @throws X if value is out of bound + * @return 经过检查后的值 + * @since 5.7.15 + */ + public static long checkBetween(long value, long min, long max, Supplier errorSupplier) throws X { if (value < min || value > max) { - throw new IllegalArgumentException(StrUtil.format("Length must be between {} and {}.", min, max)); + throw errorSupplier.get(); } + return value; } + /** + * 检查值是否在指定范围内 + * + * @param value 值 + * @param min 最小值(包含) + * @param max 最大值(包含) + * @return 经过检查后的值 + * @since 5.7.15 + */ + public static long checkBetween(long value, long min, long max, String errorMsgTemplate, Object... params) { + return checkBetween(value, min, max, () -> new IllegalArgumentException(StrUtil.format(errorMsgTemplate, params))); + } + /** * 检查值是否在指定范围内 * @@ -860,12 +924,41 @@ public static int checkBetween(int value, int min, int max) { * @since 4.1.10 */ public static long checkBetween(long value, long min, long max) { + return checkBetween(value, min, max, TEMPLATE_VALUE_MUST_BE_BETWEEN_AND, min, max); + } + + /** + * 检查值是否在指定范围内 + * + * @param value 值 + * @param min 最小值(包含) + * @param max 最大值(包含) + * @param errorSupplier 错误抛出异常附带的消息生产接口 + * @throws X if value is out of bound + * @return 经过检查后的值 + * @since 5.7.15 + */ + public static double checkBetween(double value, double min, double max, Supplier errorSupplier) throws X { if (value < min || value > max) { - throw new IllegalArgumentException(StrUtil.format("Length must be between {} and {}.", min, max)); + throw errorSupplier.get(); } + return value; } + /** + * 检查值是否在指定范围内 + * + * @param value 值 + * @param min 最小值(包含) + * @param max 最大值(包含) + * @return 经过检查后的值 + * @since 5.7.15 + */ + public static double checkBetween(double value, double min, double max, String errorMsgTemplate, Object... params) { + return checkBetween(value, min, max, () -> new IllegalArgumentException(StrUtil.format(errorMsgTemplate, params))); + } + /** * 检查值是否在指定范围内 * @@ -876,10 +969,7 @@ public static long checkBetween(long value, long min, long max) { * @since 4.1.10 */ public static double checkBetween(double value, double min, double max) { - if (value < min || value > max) { - throw new IllegalArgumentException(StrUtil.format("Length must be between {} and {}.", min, max)); - } - return value; + return checkBetween(value, min, max, TEMPLATE_VALUE_MUST_BE_BETWEEN_AND, min, max); } /** @@ -899,7 +989,7 @@ public static Number checkBetween(Number value, Number min, Number max) { double minDouble = min.doubleValue(); double maxDouble = max.doubleValue(); if (valueDouble < minDouble || valueDouble > maxDouble) { - throw new IllegalArgumentException(StrUtil.format("Length must be between {} and {}.", min, max)); + throw new IllegalArgumentException(StrUtil.format(TEMPLATE_VALUE_MUST_BE_BETWEEN_AND, min, max)); } return value; } From db86a2ad7692927ddc83480b0d2ae1f87f5b00a4 Mon Sep 17 00:00:00 2001 From: ZhouChuGang Date: Wed, 13 Oct 2021 20:34:05 +0800 Subject: [PATCH 14/51] =?UTF-8?q?feat:=20=E5=8A=A0=E5=85=A5=E9=9B=86?= =?UTF-8?q?=E5=90=88=E7=8E=AF=E5=BD=A2=E7=B4=A2=E5=BC=95=E8=8E=B7=E5=8F=96?= =?UTF-8?q?=E5=B7=A5=E5=85=B7=E7=B1=BBRingIndexUtil?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 加入集合环形索引获取工具类RingIndexUtil --- .../hutool/core/collection/RingIndexUtil.java | 100 ++++++++++++++++++ .../core/collection/RingIndexUtilTest.java | 51 +++++++++ 2 files changed, 151 insertions(+) create mode 100644 hutool-core/src/main/java/cn/hutool/core/collection/RingIndexUtil.java create mode 100644 hutool-core/src/test/java/cn/hutool/core/collection/RingIndexUtilTest.java diff --git a/hutool-core/src/main/java/cn/hutool/core/collection/RingIndexUtil.java b/hutool-core/src/main/java/cn/hutool/core/collection/RingIndexUtil.java new file mode 100644 index 0000000000..31c9ebc7af --- /dev/null +++ b/hutool-core/src/main/java/cn/hutool/core/collection/RingIndexUtil.java @@ -0,0 +1,100 @@ +package cn.hutool.core.collection; + +import cn.hutool.core.lang.Assert; + +import java.util.concurrent.atomic.AtomicInteger; +import java.util.concurrent.atomic.AtomicLong; + +/** + * 集合索引环形获取工具类 + * + * @author ZhouChuGang + * @since 5.7.15 + */ +public class RingIndexUtil { + + /** + * 通过cas操作 实现对指定值内的回环累加 + * + * @param object 集合 + *
    + *
  • Collection - the collection size + *
  • Map - the map size + *
  • Array - the array size + *
  • Iterator - the number of elements remaining in the iterator + *
  • Enumeration - the number of elements remaining in the enumeration + *
+ * @param atomicLong 原子操作类 + * @return 索引位置 + */ + public static long ringNextLongByObj(Object object, AtomicLong atomicLong) { + Assert.notNull(object); + int modulo = CollUtil.size(object); + return ringNextLong(modulo, atomicLong); + } + + /** + * 通过cas操作 实现对指定值内的回环累加 + * + * @param object 集合 + *
    + *
  • Collection - the collection size + *
  • Map - the map size + *
  • Array - the array size + *
  • Iterator - the number of elements remaining in the iterator + *
  • Enumeration - the number of elements remaining in the enumeration + *
+ * @param atomicInteger 原子操作类 + * @return 索引位置 + */ + public static int ringNextIntByObj(Object object, AtomicInteger atomicInteger) { + Assert.notNull(object); + int modulo = CollUtil.size(object); + return ringNextInt(modulo, atomicInteger); + } + + /** + * 通过cas操作 实现对指定值内的回环累加 + * + * @param modulo 回环周期值 + * @param atomicInteger 原子操作类 + * @return 索引位置 + */ + public static int ringNextInt(int modulo, AtomicInteger atomicInteger) { + Assert.notNull(atomicInteger); + Assert.isTrue(modulo > 0); + if (modulo == 1) { + return 0; + } + for (; ; ) { + int current = atomicInteger.get(); + int next = (current + 1) % modulo; + if (atomicInteger.compareAndSet(current, next)) { + return next; + } + } + } + + /** + * 通过cas操作 实现对指定值内的回环累加 + * + * @param modulo 回环周期值 + * @param atomicLong 原子操作类 + * @return 索引位置 + */ + public static long ringNextLong(long modulo, AtomicLong atomicLong) { + Assert.notNull(atomicLong); + Assert.isTrue(modulo > 0); + if (modulo == 1) { + return 0; + } + for (; ; ) { + long current = atomicLong.get(); + long next = (current + 1) % modulo; + if (atomicLong.compareAndSet(current, next)) { + return next; + } + } + } + +} diff --git a/hutool-core/src/test/java/cn/hutool/core/collection/RingIndexUtilTest.java b/hutool-core/src/test/java/cn/hutool/core/collection/RingIndexUtilTest.java new file mode 100644 index 0000000000..7ea1df65ed --- /dev/null +++ b/hutool-core/src/test/java/cn/hutool/core/collection/RingIndexUtilTest.java @@ -0,0 +1,51 @@ +package cn.hutool.core.collection; + +import cn.hutool.core.thread.ThreadUtil; +import org.junit.Test; + +import java.util.Arrays; +import java.util.List; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.concurrent.atomic.AtomicLong; + +/** + * 集合索引环形获取工具类测试类 + * + * @author ZhouChuGang + * @version 1.0 + * @project hutool + * @date 2021/10/13 18:47 + */ +public class RingIndexUtilTest { + + private final List strList = Arrays.asList("0", "1", "2", "3", "4", "5", "6", "7", "8", "9"); + + /** + * 观察输出的打印为不重复的 + */ + @Test + public void ringNextLongByObjTest() { + final AtomicLong atomicLong = new AtomicLong(); + // 开启并发测试,每个线程获取到的元素都是唯一的 + ThreadUtil.concurrencyTest(strList.size(), () -> { + final long index = RingIndexUtil.ringNextLongByObj(strList, atomicLong); + final String s = strList.get((int) index); + System.out.println(s); + }); + } + + /** + * 观察输出的打印为不重复的 + */ + @Test + public void ringNextIntByObjTest() { + final AtomicInteger atomicInteger = new AtomicInteger(); + // 开启并发测试,每个线程获取到的元素都是唯一的 + ThreadUtil.concurrencyTest(strList.size(), () -> { + final int index = RingIndexUtil.ringNextIntByObj(strList, atomicInteger); + final String s = strList.get(index); + System.out.println(s); + }); + } + +} From 6dc2192caa7f03d3a51c62ad9c476fc5f82ae175 Mon Sep 17 00:00:00 2001 From: Looly Date: Thu, 14 Oct 2021 09:44:34 +0800 Subject: [PATCH 15/51] add RingIndexUtil --- CHANGELOG.md | 3 ++- .../hutool/core/collection/RingIndexUtil.java | 25 ++----------------- .../core/collection/RingIndexUtilTest.java | 21 ++-------------- 3 files changed, 6 insertions(+), 43 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6c772c8123..905612610b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,10 +3,11 @@ ------------------------------------------------------------------------------------------------------------- -# 5.7.15 (2021-10-11) +# 5.7.15 (2021-10-14) ### 🐣新特性 * 【db 】 Db.quietSetAutoCommit增加判空(issue#I4D75B@Gitee) +* 【core 】 增加RingIndexUtil(pr#438@Gitee) * ### 🐞Bug修复 * 【core 】 修复CollUtil.isEqualList两个null返回错误问题(issue#1885@Github) diff --git a/hutool-core/src/main/java/cn/hutool/core/collection/RingIndexUtil.java b/hutool-core/src/main/java/cn/hutool/core/collection/RingIndexUtil.java index 31c9ebc7af..b997b14280 100644 --- a/hutool-core/src/main/java/cn/hutool/core/collection/RingIndexUtil.java +++ b/hutool-core/src/main/java/cn/hutool/core/collection/RingIndexUtil.java @@ -13,26 +13,6 @@ */ public class RingIndexUtil { - /** - * 通过cas操作 实现对指定值内的回环累加 - * - * @param object 集合 - *
    - *
  • Collection - the collection size - *
  • Map - the map size - *
  • Array - the array size - *
  • Iterator - the number of elements remaining in the iterator - *
  • Enumeration - the number of elements remaining in the enumeration - *
- * @param atomicLong 原子操作类 - * @return 索引位置 - */ - public static long ringNextLongByObj(Object object, AtomicLong atomicLong) { - Assert.notNull(object); - int modulo = CollUtil.size(object); - return ringNextLong(modulo, atomicLong); - } - /** * 通过cas操作 实现对指定值内的回环累加 * @@ -63,7 +43,7 @@ public static int ringNextIntByObj(Object object, AtomicInteger atomicInteger) { public static int ringNextInt(int modulo, AtomicInteger atomicInteger) { Assert.notNull(atomicInteger); Assert.isTrue(modulo > 0); - if (modulo == 1) { + if (modulo <= 1) { return 0; } for (; ; ) { @@ -85,7 +65,7 @@ public static int ringNextInt(int modulo, AtomicInteger atomicInteger) { public static long ringNextLong(long modulo, AtomicLong atomicLong) { Assert.notNull(atomicLong); Assert.isTrue(modulo > 0); - if (modulo == 1) { + if (modulo <= 1) { return 0; } for (; ; ) { @@ -96,5 +76,4 @@ public static long ringNextLong(long modulo, AtomicLong atomicLong) { } } } - } diff --git a/hutool-core/src/test/java/cn/hutool/core/collection/RingIndexUtilTest.java b/hutool-core/src/test/java/cn/hutool/core/collection/RingIndexUtilTest.java index 7ea1df65ed..4ff70cdd1f 100644 --- a/hutool-core/src/test/java/cn/hutool/core/collection/RingIndexUtilTest.java +++ b/hutool-core/src/test/java/cn/hutool/core/collection/RingIndexUtilTest.java @@ -1,39 +1,22 @@ package cn.hutool.core.collection; +import cn.hutool.core.lang.Assert; import cn.hutool.core.thread.ThreadUtil; import org.junit.Test; import java.util.Arrays; import java.util.List; import java.util.concurrent.atomic.AtomicInteger; -import java.util.concurrent.atomic.AtomicLong; /** * 集合索引环形获取工具类测试类 * * @author ZhouChuGang - * @version 1.0 - * @project hutool - * @date 2021/10/13 18:47 */ public class RingIndexUtilTest { private final List strList = Arrays.asList("0", "1", "2", "3", "4", "5", "6", "7", "8", "9"); - /** - * 观察输出的打印为不重复的 - */ - @Test - public void ringNextLongByObjTest() { - final AtomicLong atomicLong = new AtomicLong(); - // 开启并发测试,每个线程获取到的元素都是唯一的 - ThreadUtil.concurrencyTest(strList.size(), () -> { - final long index = RingIndexUtil.ringNextLongByObj(strList, atomicLong); - final String s = strList.get((int) index); - System.out.println(s); - }); - } - /** * 观察输出的打印为不重复的 */ @@ -44,7 +27,7 @@ public void ringNextIntByObjTest() { ThreadUtil.concurrencyTest(strList.size(), () -> { final int index = RingIndexUtil.ringNextIntByObj(strList, atomicInteger); final String s = strList.get(index); - System.out.println(s); + Assert.notNull(s); }); } From b5036b4b0bef922559fc299e1f3904494e48112d Mon Sep 17 00:00:00 2001 From: Looly Date: Thu, 14 Oct 2021 09:56:04 +0800 Subject: [PATCH 16/51] add method --- CHANGELOG.md | 1 + .../main/java/cn/hutool/core/lang/Assert.java | 24 +++++++++---------- .../java/cn/hutool/core/util/RandomUtil.java | 10 ++++++++ .../cn/hutool/core/util/RandomUtilTest.java | 6 +++++ 4 files changed, 29 insertions(+), 12 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 905612610b..f854a8372d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,7 @@ ### 🐣新特性 * 【db 】 Db.quietSetAutoCommit增加判空(issue#I4D75B@Gitee) * 【core 】 增加RingIndexUtil(pr#438@Gitee) +* 【core 】 Assert增加checkBetween重载(pr#436@Gitee) * ### 🐞Bug修复 * 【core 】 修复CollUtil.isEqualList两个null返回错误问题(issue#1885@Github) diff --git a/hutool-core/src/main/java/cn/hutool/core/lang/Assert.java b/hutool-core/src/main/java/cn/hutool/core/lang/Assert.java index e977351635..fd4751d6a3 100644 --- a/hutool-core/src/main/java/cn/hutool/core/lang/Assert.java +++ b/hutool-core/src/main/java/cn/hutool/core/lang/Assert.java @@ -840,12 +840,12 @@ public static int checkIndex(int index, int size, String errorMsgTemplate, Objec /** * 检查值是否在指定范围内 * - * @param value 值 - * @param min 最小值(包含) - * @param max 最大值(包含) + * @param value 值 + * @param min 最小值(包含) + * @param max 最大值(包含) * @param errorSupplier 错误抛出异常附带的消息生产接口 - * @throws X if value is out of bound * @return 经过检查后的值 + * @throws X if value is out of bound * @since 5.7.15 */ public static int checkBetween(int value, int min, int max, Supplier errorSupplier) throws X { @@ -885,12 +885,12 @@ public static int checkBetween(int value, int min, int max) { /** * 检查值是否在指定范围内 * - * @param value 值 - * @param min 最小值(包含) - * @param max 最大值(包含) + * @param value 值 + * @param min 最小值(包含) + * @param max 最大值(包含) * @param errorSupplier 错误抛出异常附带的消息生产接口 - * @throws X if value is out of bound * @return 经过检查后的值 + * @throws X if value is out of bound * @since 5.7.15 */ public static long checkBetween(long value, long min, long max, Supplier errorSupplier) throws X { @@ -930,12 +930,12 @@ public static long checkBetween(long value, long min, long max) { /** * 检查值是否在指定范围内 * - * @param value 值 - * @param min 最小值(包含) - * @param max 最大值(包含) + * @param value 值 + * @param min 最小值(包含) + * @param max 最大值(包含) * @param errorSupplier 错误抛出异常附带的消息生产接口 - * @throws X if value is out of bound * @return 经过检查后的值 + * @throws X if value is out of bound * @since 5.7.15 */ public static double checkBetween(double value, double min, double max, Supplier errorSupplier) throws X { diff --git a/hutool-core/src/main/java/cn/hutool/core/util/RandomUtil.java b/hutool-core/src/main/java/cn/hutool/core/util/RandomUtil.java index aae07f8be8..8d77384e72 100644 --- a/hutool-core/src/main/java/cn/hutool/core/util/RandomUtil.java +++ b/hutool-core/src/main/java/cn/hutool/core/util/RandomUtil.java @@ -163,6 +163,16 @@ public static boolean randomBoolean() { return 0 == randomInt(2); } + /** + * 随机汉字('\u4E00'-'\u9FFF') + * + * @return 随机的汉字字符 + * @since 5.7.15 + */ + public static char randomChinese() { + return (char) randomInt('\u4E00', '\u9FFF'); + } + /** * 获得指定范围内的随机数 * diff --git a/hutool-core/src/test/java/cn/hutool/core/util/RandomUtilTest.java b/hutool-core/src/test/java/cn/hutool/core/util/RandomUtilTest.java index 42ba46299f..978fcd2eda 100644 --- a/hutool-core/src/test/java/cn/hutool/core/util/RandomUtilTest.java +++ b/hutool-core/src/test/java/cn/hutool/core/util/RandomUtilTest.java @@ -53,4 +53,10 @@ public void randomBytesTest() { final byte[] c = RandomUtil.randomBytes(10); Assert.assertNotNull(c); } + + @Test + public void randomChineseTest(){ + char c = RandomUtil.randomChinese(); + Assert.assertTrue(c > 0); + } } From a15eecd2c5a23baee0e10fa347ea1406289550c5 Mon Sep 17 00:00:00 2001 From: lihai03 Date: Thu, 14 Oct 2021 18:18:25 +0800 Subject: [PATCH 17/51] =?UTF-8?q?ReUtil.java=20=E6=B7=BB=E5=8A=A0=202=20?= =?UTF-8?q?=E4=B8=AA=E5=91=BD=E5=90=8D=E6=8D=95=E8=8E=B7=E7=BB=84=E7=9B=B8?= =?UTF-8?q?=E5=85=B3=E7=9A=84=E6=96=B9=E6=B3=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ReUtil.java 添加 2 个命名捕获组相关的方法 ReUtil.java 添加 2 个命名捕获组相关的方法 --- .../main/java/cn/hutool/core/util/ReUtil.java | 57 +++++++++++++++++++ .../java/cn/hutool/core/util/ReUtilTest.java | 23 ++++++++ 2 files changed, 80 insertions(+) diff --git a/hutool-core/src/main/java/cn/hutool/core/util/ReUtil.java b/hutool-core/src/main/java/cn/hutool/core/util/ReUtil.java index fcad035a5a..a5f28313c3 100644 --- a/hutool-core/src/main/java/cn/hutool/core/util/ReUtil.java +++ b/hutool-core/src/main/java/cn/hutool/core/util/ReUtil.java @@ -9,10 +9,15 @@ import cn.hutool.core.lang.Validator; import cn.hutool.core.lang.func.Func1; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; import java.util.ArrayList; import java.util.Collection; +import java.util.HashMap; import java.util.HashSet; import java.util.List; +import java.util.Map; +import java.util.Map.Entry; import java.util.Set; import java.util.TreeSet; import java.util.regex.MatchResult; @@ -83,6 +88,58 @@ public static String get(String regex, CharSequence content, int groupIndex) { return get(pattern, content, groupIndex); } + /** + * 获得匹配的字符串 + * + * @param regex 匹配的正则 + * @param content 被匹配的内容 + * @param groupName 匹配正则的分组名称 + * @return 匹配后得到的字符串,未匹配返回null + */ + public static String getByGroupName(String regex, CharSequence content, String groupName) { + if (null == content || null == regex || null == groupName) { + return null; + } + final Pattern pattern = PatternPool.get(regex, Pattern.DOTALL); + Matcher m = pattern.matcher(content); + if (m.find()) { + return m.group(groupName); + } + return null; + } + + /** + * 获得匹配的字符串 + * + * @param regex 匹配的正则 + * @param content 被匹配的内容 + * @return 命名捕获组 + */ + @SuppressWarnings("unchecked") + public static Map getAllGroupNames(String regex, CharSequence content) { + if (null == content || null == regex) { + return null; + } + Map result = new HashMap<>(); + try { + final Pattern pattern = PatternPool.get(regex, Pattern.DOTALL); + Matcher m = pattern.matcher(content); + // 通过反射获取 namedGroups 方法 + Method method = ReflectUtil.getMethod(Pattern.class, "namedGroups"); + ReflectUtil.setAccessible(method); + Map map = (Map) method.invoke(pattern); + // 组合返回值 + if (m.matches()) { + for (Entry e : map.entrySet()) { + result.put(e.getKey(), m.group(e.getValue())); + } + } + return result; + } catch (InvocationTargetException | IllegalAccessException ex) { + throw new UtilException("call getAllGroupNames(...) method error: " + ex.getMessage()); + } + } + /** * 获得匹配的字符串,,获得正则中分组0的内容 * diff --git a/hutool-core/src/test/java/cn/hutool/core/util/ReUtilTest.java b/hutool-core/src/test/java/cn/hutool/core/util/ReUtilTest.java index 4c392edc07..583274fd86 100644 --- a/hutool-core/src/test/java/cn/hutool/core/util/ReUtilTest.java +++ b/hutool-core/src/test/java/cn/hutool/core/util/ReUtilTest.java @@ -8,6 +8,7 @@ import java.util.ArrayList; import java.util.List; +import java.util.Map; import java.util.regex.Pattern; public class ReUtilTest { @@ -163,4 +164,26 @@ public void matchTest(){ "(.+?)省(.+?)市(.+?)区", "广东省深圳市南山区"); Console.log(match); } + + @Test + public void getByGroupNameTest() { + String content = "2021-10-11"; + String regex = "(?\\d+)-(?\\d+)-(?\\d+)"; + String year = ReUtil.getByGroupName(regex, content, "year"); + Assert.assertEquals("2021", year); + String month = ReUtil.getByGroupName(regex, content, "month"); + Assert.assertEquals("10", month); + String day = ReUtil.getByGroupName(regex, content, "day"); + Assert.assertEquals("11", day); + } + + @Test + public void getAllGroupNamesTest() { + String content = "2021-10-11"; + String regex = "(?\\d+)-(?\\d+)-(?\\d+)"; + Map map = ReUtil.getAllGroupNames(regex, content); + Assert.assertEquals(map.get("year"), "2021"); + Assert.assertEquals(map.get("month"), "10"); + Assert.assertEquals(map.get("day"), "11"); + } } From a5ea0ef1ed17b09f0a12299e2fcc142d730697e1 Mon Sep 17 00:00:00 2001 From: Looly Date: Thu, 14 Oct 2021 19:29:44 +0800 Subject: [PATCH 18/51] fix parse bug --- CHANGELOG.md | 1 + .../java/cn/hutool/core/date/DateTime.java | 6 +-- .../core/date/TemporalAccessorUtil.java | 5 ++- .../java/cn/hutool/core/date/ZoneUtil.java | 41 +++++++++++++++++++ .../cn/hutool/core/date/DateUtilTest.java | 6 +++ 5 files changed, 55 insertions(+), 4 deletions(-) create mode 100644 hutool-core/src/main/java/cn/hutool/core/date/ZoneUtil.java diff --git a/CHANGELOG.md b/CHANGELOG.md index f854a8372d..7a583c895e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,6 +13,7 @@ ### 🐞Bug修复 * 【core 】 修复CollUtil.isEqualList两个null返回错误问题(issue#1885@Github) * 【poi 】 修复ExcelWriter多余调试信息导致的问题(issue#1884@Github) +* 【poi 】 修复TemporalAccessorUtil.toInstant使用DateTimeFormatter导致问题(issue#1891@Github) ------------------------------------------------------------------------------------------------------------- diff --git a/hutool-core/src/main/java/cn/hutool/core/date/DateTime.java b/hutool-core/src/main/java/cn/hutool/core/date/DateTime.java index f9b37927f4..6758c37689 100644 --- a/hutool-core/src/main/java/cn/hutool/core/date/DateTime.java +++ b/hutool-core/src/main/java/cn/hutool/core/date/DateTime.java @@ -167,7 +167,7 @@ public DateTime(Instant instant) { * @since 5.0.5 */ public DateTime(Instant instant, ZoneId zoneId) { - this(instant.toEpochMilli(), TimeZone.getTimeZone(ObjectUtil.defaultIfNull(zoneId, ZoneId.systemDefault()))); + this(instant.toEpochMilli(), ZoneUtil.toTimeZone(zoneId)); } /** @@ -177,7 +177,7 @@ public DateTime(Instant instant, ZoneId zoneId) { * @since 5.0.0 */ public DateTime(TemporalAccessor temporalAccessor) { - this(DateUtil.toInstant(temporalAccessor)); + this(TemporalAccessorUtil.toInstant(temporalAccessor)); } /** @@ -276,7 +276,7 @@ public DateTime(CharSequence dateStr, DateFormat dateFormat) { * @since 5.0.0 */ public DateTime(CharSequence dateStr, DateTimeFormatter formatter) { - this(Instant.from(formatter.parse(dateStr)), formatter.getZone()); + this(TemporalAccessorUtil.toInstant(formatter.parse(dateStr)), formatter.getZone()); } /** diff --git a/hutool-core/src/main/java/cn/hutool/core/date/TemporalAccessorUtil.java b/hutool-core/src/main/java/cn/hutool/core/date/TemporalAccessorUtil.java index f2918c55e5..1d9baef328 100644 --- a/hutool-core/src/main/java/cn/hutool/core/date/TemporalAccessorUtil.java +++ b/hutool-core/src/main/java/cn/hutool/core/date/TemporalAccessorUtil.java @@ -138,7 +138,10 @@ public static Instant toInstant(TemporalAccessor temporalAccessor) { // 指定本地时间转换 为Instant,取当天日期 result = ((OffsetTime) temporalAccessor).atDate(LocalDate.now()).toInstant(); } else { - result = Instant.from(temporalAccessor); + // issue#1891@Github + // Instant.from不能完成日期转换 + //result = Instant.from(temporalAccessor); + result = toInstant(LocalDateTimeUtil.of(temporalAccessor)); } return result; diff --git a/hutool-core/src/main/java/cn/hutool/core/date/ZoneUtil.java b/hutool-core/src/main/java/cn/hutool/core/date/ZoneUtil.java new file mode 100644 index 0000000000..1b12788da7 --- /dev/null +++ b/hutool-core/src/main/java/cn/hutool/core/date/ZoneUtil.java @@ -0,0 +1,41 @@ +package cn.hutool.core.date; + +import java.time.ZoneId; +import java.util.TimeZone; + +/** + * {@link ZoneId}和{@link TimeZone}相关封装 + * + * @author looly + * @since 5.7.15 + */ +public class ZoneUtil { + + /** + * {@link ZoneId}转换为{@link TimeZone},{@code null}则返回系统默认值 + * + * @param zoneId {@link ZoneId},{@code null}则返回系统默认值 + * @return {@link TimeZone} + */ + public static TimeZone toTimeZone(ZoneId zoneId) { + if (null == zoneId) { + return TimeZone.getDefault(); + } + + return TimeZone.getTimeZone(zoneId); + } + + /** + * {@link TimeZone}转换为{@link ZoneId},{@code null}则返回系统默认值 + * + * @param timeZone {@link TimeZone},{@code null}则返回系统默认值 + * @return {@link ZoneId} + */ + public static ZoneId toZoneId(TimeZone timeZone) { + if (null == timeZone) { + return ZoneId.systemDefault(); + } + + return timeZone.toZoneId(); + } +} diff --git a/hutool-core/src/test/java/cn/hutool/core/date/DateUtilTest.java b/hutool-core/src/test/java/cn/hutool/core/date/DateUtilTest.java index 05c674cf52..7389955331 100644 --- a/hutool-core/src/test/java/cn/hutool/core/date/DateUtilTest.java +++ b/hutool-core/src/test/java/cn/hutool/core/date/DateUtilTest.java @@ -989,4 +989,10 @@ public void parseSingleMonthAndDayTest(){ Assert.assertNotNull(parse); Assert.assertEquals("2021-01-01 00:00:00", parse.toString()); } + + @Test + public void parseByDateTimeFormatterTest(){ + final DateTime parse = DateUtil.parse("2021-12-01", DatePattern.NORM_DATE_FORMATTER); + Assert.assertEquals("2021-12-01 00:00:00", parse.toString()); + } } From 7afad66075267f0073501c6c93c717113267d54d Mon Sep 17 00:00:00 2001 From: Looly Date: Thu, 14 Oct 2021 23:45:38 +0800 Subject: [PATCH 19/51] add FileSystemUtil --- .../hutool/core/io/file/FileSystemUtil.java | 47 +++++++++++++++++++ 1 file changed, 47 insertions(+) create mode 100644 hutool-core/src/main/java/cn/hutool/core/io/file/FileSystemUtil.java diff --git a/hutool-core/src/main/java/cn/hutool/core/io/file/FileSystemUtil.java b/hutool-core/src/main/java/cn/hutool/core/io/file/FileSystemUtil.java new file mode 100644 index 0000000000..65dc37a5fe --- /dev/null +++ b/hutool-core/src/main/java/cn/hutool/core/io/file/FileSystemUtil.java @@ -0,0 +1,47 @@ +package cn.hutool.core.io.file; + +import cn.hutool.core.io.IORuntimeException; +import cn.hutool.core.map.MapUtil; +import cn.hutool.core.util.StrUtil; + +import java.io.IOException; +import java.nio.file.FileSystem; +import java.nio.file.FileSystems; +import java.nio.file.Path; +import java.nio.file.Paths; + +/** + * {@link FileSystem}相关工具类封装
+ * 参考:https://blog.csdn.net/j16421881/article/details/78858690 + * + * @author looly + * @since 5.7.15 + */ +public class FileSystemUtil { + + /** + * 创建 {@link FileSystem} + * + * @param path 文件路径,可以是目录或Zip文件等 + * @return {@link FileSystem} + */ + public static FileSystem create(String path) { + try { + return FileSystems.newFileSystem( + Paths.get(path).toUri(), + MapUtil.of("create", "true")); + } catch (IOException e) { + throw new IORuntimeException(e); + } + } + + /** + * 获取目录的根路径,或Zip文件中的根路径 + * + * @param fileSystem {@link FileSystem} + * @return 根 {@link Path} + */ + public static Path getRoot(FileSystem fileSystem) { + return fileSystem.getPath(StrUtil.SLASH); + } +} From 1b39b8271af5d98c79b30eec0a4ed9df6291c310 Mon Sep 17 00:00:00 2001 From: Looly Date: Fri, 15 Oct 2021 00:06:42 +0800 Subject: [PATCH 20/51] add method --- CHANGELOG.md | 3 +- .../main/java/cn/hutool/core/util/ReUtil.java | 126 ++++++++++-------- .../java/cn/hutool/core/util/ReUtilTest.java | 8 +- 3 files changed, 73 insertions(+), 64 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7a583c895e..a1df0b9d19 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,12 +3,13 @@ ------------------------------------------------------------------------------------------------------------- -# 5.7.15 (2021-10-14) +# 5.7.15 (2021-10-15) ### 🐣新特性 * 【db 】 Db.quietSetAutoCommit增加判空(issue#I4D75B@Gitee) * 【core 】 增加RingIndexUtil(pr#438@Gitee) * 【core 】 Assert增加checkBetween重载(pr#436@Gitee) +* 【core 】 ReUtil增加命名分组重载(pr#439@Gitee) * ### 🐞Bug修复 * 【core 】 修复CollUtil.isEqualList两个null返回错误问题(issue#1885@Github) diff --git a/hutool-core/src/main/java/cn/hutool/core/util/ReUtil.java b/hutool-core/src/main/java/cn/hutool/core/util/ReUtil.java index a5f28313c3..19bced433a 100644 --- a/hutool-core/src/main/java/cn/hutool/core/util/ReUtil.java +++ b/hutool-core/src/main/java/cn/hutool/core/util/ReUtil.java @@ -8,16 +8,13 @@ import cn.hutool.core.lang.RegexPool; import cn.hutool.core.lang.Validator; import cn.hutool.core.lang.func.Func1; +import cn.hutool.core.map.MapUtil; -import java.lang.reflect.InvocationTargetException; -import java.lang.reflect.Method; import java.util.ArrayList; import java.util.Collection; -import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; -import java.util.Map.Entry; import java.util.Set; import java.util.TreeSet; import java.util.regex.MatchResult; @@ -83,7 +80,6 @@ public static String get(String regex, CharSequence content, int groupIndex) { return null; } - // Pattern pattern = Pattern.compile(regex, Pattern.DOTALL); final Pattern pattern = PatternPool.get(regex, Pattern.DOTALL); return get(pattern, content, groupIndex); } @@ -91,53 +87,18 @@ public static String get(String regex, CharSequence content, int groupIndex) { /** * 获得匹配的字符串 * - * @param regex 匹配的正则 - * @param content 被匹配的内容 + * @param regex 匹配的正则 + * @param content 被匹配的内容 * @param groupName 匹配正则的分组名称 * @return 匹配后得到的字符串,未匹配返回null */ - public static String getByGroupName(String regex, CharSequence content, String groupName) { - if (null == content || null == regex || null == groupName) { - return null; - } - final Pattern pattern = PatternPool.get(regex, Pattern.DOTALL); - Matcher m = pattern.matcher(content); - if (m.find()) { - return m.group(groupName); - } - return null; - } - - /** - * 获得匹配的字符串 - * - * @param regex 匹配的正则 - * @param content 被匹配的内容 - * @return 命名捕获组 - */ - @SuppressWarnings("unchecked") - public static Map getAllGroupNames(String regex, CharSequence content) { + public static String get(String regex, CharSequence content, String groupName) { if (null == content || null == regex) { return null; } - Map result = new HashMap<>(); - try { - final Pattern pattern = PatternPool.get(regex, Pattern.DOTALL); - Matcher m = pattern.matcher(content); - // 通过反射获取 namedGroups 方法 - Method method = ReflectUtil.getMethod(Pattern.class, "namedGroups"); - ReflectUtil.setAccessible(method); - Map map = (Map) method.invoke(pattern); - // 组合返回值 - if (m.matches()) { - for (Entry e : map.entrySet()) { - result.put(e.getKey(), m.group(e.getValue())); - } - } - return result; - } catch (InvocationTargetException | IllegalAccessException ex) { - throw new UtilException("call getAllGroupNames(...) method error: " + ex.getMessage()); - } + + final Pattern pattern = PatternPool.get(regex, Pattern.DOTALL); + return get(pattern, content, groupName); } /** @@ -184,6 +145,26 @@ public static String get(Pattern pattern, CharSequence content, int groupIndex) return null; } + /** + * 获得匹配的字符串 + * + * @param pattern 匹配的正则 + * @param content 被匹配的内容 + * @param groupName 匹配正则的分组名称 + * @return 匹配后得到的字符串,未匹配返回null + * @since 5.7.15 + */ + public static String get(Pattern pattern, CharSequence content, String groupName) { + if (null == content || null == pattern || null == groupName) { + return null; + } + final Matcher m = pattern.matcher(content); + if (m.find()) { + return m.group(groupName); + } + return null; + } + /** * 获得匹配的字符串匹配到的所有分组 * @@ -222,6 +203,33 @@ public static List getAllGroups(Pattern pattern, CharSequence content, b return result; } + /** + * 根据给定正则查找字符串中的匹配项,返回所有匹配的分组名对应分组值
+ *
+	 * pattern: (?<year>\\d+)-(?<month>\\d+)-(?<day>\\d+)
+	 * content: 2021-10-11
+	 * result : year: 2021, month: 10, day: 11
+	 * 
+ * + * @param pattern 匹配的正则 + * @param content 被匹配的内容 + * @return 命名捕获组,key为分组名,value为对应值 + * @since 5.7.15 + */ + public static Map getAllGroupNames(Pattern pattern, CharSequence content) { + if (null == content || null == pattern) { + return null; + } + final Matcher m = pattern.matcher(content); + final Map result = MapUtil.newHashMap(m.groupCount()); + if (m.find()) { + // 通过反射获取 namedGroups 方法 + final Map map = ReflectUtil.invoke(pattern, "namedGroups"); + map.forEach((key, value)-> result.put(key, m.group(value))); + } + return result; + } + /** * 从content中匹配出多个值并根据template生成新的字符串
* 例如:
@@ -357,8 +365,8 @@ public static String delFirst(Pattern pattern, CharSequence content) { /** * 替换匹配的第一个内容 * - * @param pattern 正则 - * @param content 被匹配的内容 + * @param pattern 正则 + * @param content 被匹配的内容 * @param replacement 替换的内容 * @return 替换后剩余的内容 * @since 5.6.5 @@ -399,7 +407,7 @@ public static String delLast(String regex, CharSequence str) { public static String delLast(Pattern pattern, CharSequence str) { if (null != pattern && StrUtil.isNotEmpty(str)) { final MatchResult matchResult = lastIndexOf(pattern, str); - if(null != matchResult){ + if (null != matchResult) { return StrUtil.subPre(str, matchResult.start()) + StrUtil.subSuf(str, matchResult.end()); } } @@ -651,12 +659,12 @@ public static boolean contains(Pattern pattern, CharSequence content) { /** * 找到指定正则匹配到字符串的开始位置 * - * @param regex 正则 + * @param regex 正则 * @param content 字符串 * @return 位置,{@code null}表示未找到 * @since 5.6.5 */ - public static MatchResult indexOf(String regex, CharSequence content){ + public static MatchResult indexOf(String regex, CharSequence content) { if (null == regex || null == content) { return null; } @@ -673,10 +681,10 @@ public static MatchResult indexOf(String regex, CharSequence content){ * @return 位置,{@code null}表示未找到 * @since 5.6.5 */ - public static MatchResult indexOf(Pattern pattern, CharSequence content){ - if(null != pattern && null != content){ + public static MatchResult indexOf(Pattern pattern, CharSequence content) { + if (null != pattern && null != content) { final Matcher matcher = pattern.matcher(content); - if(matcher.find()){ + if (matcher.find()) { return matcher.toMatchResult(); } } @@ -687,12 +695,12 @@ public static MatchResult indexOf(Pattern pattern, CharSequence content){ /** * 找到指定正则匹配到第一个字符串的位置 * - * @param regex 正则 + * @param regex 正则 * @param content 字符串 * @return 位置,{@code null}表示未找到 * @since 5.6.5 */ - public static MatchResult lastIndexOf(String regex, CharSequence content){ + public static MatchResult lastIndexOf(String regex, CharSequence content) { if (null == regex || null == content) { return null; } @@ -709,11 +717,11 @@ public static MatchResult lastIndexOf(String regex, CharSequence content){ * @return 位置,{@code null}表示未找到 * @since 5.6.5 */ - public static MatchResult lastIndexOf(Pattern pattern, CharSequence content){ + public static MatchResult lastIndexOf(Pattern pattern, CharSequence content) { MatchResult result = null; - if(null != pattern && null != content){ + if (null != pattern && null != content) { final Matcher matcher = pattern.matcher(content); - while(matcher.find()){ + while (matcher.find()) { result = matcher.toMatchResult(); } } diff --git a/hutool-core/src/test/java/cn/hutool/core/util/ReUtilTest.java b/hutool-core/src/test/java/cn/hutool/core/util/ReUtilTest.java index 583274fd86..0b26e62414 100644 --- a/hutool-core/src/test/java/cn/hutool/core/util/ReUtilTest.java +++ b/hutool-core/src/test/java/cn/hutool/core/util/ReUtilTest.java @@ -169,11 +169,11 @@ public void matchTest(){ public void getByGroupNameTest() { String content = "2021-10-11"; String regex = "(?\\d+)-(?\\d+)-(?\\d+)"; - String year = ReUtil.getByGroupName(regex, content, "year"); + String year = ReUtil.get(regex, content, "year"); Assert.assertEquals("2021", year); - String month = ReUtil.getByGroupName(regex, content, "month"); + String month = ReUtil.get(regex, content, "month"); Assert.assertEquals("10", month); - String day = ReUtil.getByGroupName(regex, content, "day"); + String day = ReUtil.get(regex, content, "day"); Assert.assertEquals("11", day); } @@ -181,7 +181,7 @@ public void getByGroupNameTest() { public void getAllGroupNamesTest() { String content = "2021-10-11"; String regex = "(?\\d+)-(?\\d+)-(?\\d+)"; - Map map = ReUtil.getAllGroupNames(regex, content); + Map map = ReUtil.getAllGroupNames(PatternPool.get(regex, Pattern.DOTALL), content); Assert.assertEquals(map.get("year"), "2021"); Assert.assertEquals(map.get("month"), "10"); Assert.assertEquals(map.get("day"), "11"); From 172d457beaf0c0997aaa78f96680c48a6fcf576b Mon Sep 17 00:00:00 2001 From: Looly Date: Fri, 15 Oct 2021 00:33:02 +0800 Subject: [PATCH 21/51] add methods --- .../hutool/core/lang/mutable/MutableBool.java | 8 +- .../hutool/core/lang/mutable/MutableByte.java | 8 +- .../hutool/core/lang/mutable/MutableObj.java | 2 +- .../main/java/cn/hutool/core/util/ReUtil.java | 81 ++++++++++++++----- 4 files changed, 70 insertions(+), 29 deletions(-) diff --git a/hutool-core/src/main/java/cn/hutool/core/lang/mutable/MutableBool.java b/hutool-core/src/main/java/cn/hutool/core/lang/mutable/MutableBool.java index 2edf65727a..1afbfbd188 100644 --- a/hutool-core/src/main/java/cn/hutool/core/lang/mutable/MutableBool.java +++ b/hutool-core/src/main/java/cn/hutool/core/lang/mutable/MutableBool.java @@ -3,7 +3,7 @@ import java.io.Serializable; /** - * 可变 boolean 类型 + * 可变 {@code boolean} 类型 * * @see Boolean * @since 3.0.1 @@ -59,12 +59,12 @@ public void set(final Boolean value) { * 相等需同时满足如下条件: *
    *
  1. 非空
  2. - *
  3. 类型为 {@link MutableBool}
  4. + *
  5. 类型为 MutableBool
  6. *
  7. 值相等
  8. *
* * @param obj 比对的对象 - * @return 相同返回true,否则 false + * @return 相同返回true,否则 {@code false} */ @Override public boolean equals(final Object obj) { @@ -83,7 +83,7 @@ public int hashCode() { /** * 比较 * - * @param other 其它 {@link MutableBool} 对象 + * @param other 其它 MutableBool 对象 * @return x==y返回0,x<y返回-1,x>y返回1 */ @Override diff --git a/hutool-core/src/main/java/cn/hutool/core/lang/mutable/MutableByte.java b/hutool-core/src/main/java/cn/hutool/core/lang/mutable/MutableByte.java index 0d0755bf8d..55d539fc4b 100644 --- a/hutool-core/src/main/java/cn/hutool/core/lang/mutable/MutableByte.java +++ b/hutool-core/src/main/java/cn/hutool/core/lang/mutable/MutableByte.java @@ -3,7 +3,7 @@ import cn.hutool.core.util.NumberUtil; /** - * 可变 byte 类型 + * 可变 {@code byte} 类型 * * @see Byte * @since 3.0.1 @@ -157,12 +157,12 @@ public double doubleValue() { * 相等需同时满足如下条件: *
    *
  1. 非空
  2. - *
  3. 类型为 {@link MutableByte}
  4. + *
  5. 类型为 MutableByte
  6. *
  7. 值相等
  8. *
* * @param obj 比对的对象 - * @return 相同返回true,否则 false + * @return 相同返回true,否则 {@code false} */ @Override public boolean equals(final Object obj) { @@ -181,7 +181,7 @@ public int hashCode() { /** * 比较 * - * @param other 其它 {@link MutableByte} 对象 + * @param other 其它 MutableByte 对象 * @return x==y返回0,x<y返回-1,x>y返回1 */ @Override diff --git a/hutool-core/src/main/java/cn/hutool/core/lang/mutable/MutableObj.java b/hutool-core/src/main/java/cn/hutool/core/lang/mutable/MutableObj.java index b516c454e1..72a4903661 100644 --- a/hutool-core/src/main/java/cn/hutool/core/lang/mutable/MutableObj.java +++ b/hutool-core/src/main/java/cn/hutool/core/lang/mutable/MutableObj.java @@ -3,7 +3,7 @@ import java.io.Serializable; /** - * 可变Object + * 可变{@code Object} * * @param 可变的类型 * @since 3.0.1 diff --git a/hutool-core/src/main/java/cn/hutool/core/util/ReUtil.java b/hutool-core/src/main/java/cn/hutool/core/util/ReUtil.java index 19bced433a..e9212367f3 100644 --- a/hutool-core/src/main/java/cn/hutool/core/util/ReUtil.java +++ b/hutool-core/src/main/java/cn/hutool/core/util/ReUtil.java @@ -3,11 +3,13 @@ import cn.hutool.core.collection.CollUtil; import cn.hutool.core.convert.Convert; import cn.hutool.core.exceptions.UtilException; +import cn.hutool.core.lang.Assert; import cn.hutool.core.lang.Holder; import cn.hutool.core.lang.PatternPool; import cn.hutool.core.lang.RegexPool; import cn.hutool.core.lang.Validator; import cn.hutool.core.lang.func.Func1; +import cn.hutool.core.lang.mutable.MutableObj; import cn.hutool.core.map.MapUtil; import java.util.ArrayList; @@ -17,6 +19,7 @@ import java.util.Map; import java.util.Set; import java.util.TreeSet; +import java.util.function.Consumer; import java.util.regex.MatchResult; import java.util.regex.Matcher; import java.util.regex.Pattern; @@ -138,11 +141,9 @@ public static String get(Pattern pattern, CharSequence content, int groupIndex) return null; } - final Matcher matcher = pattern.matcher(content); - if (matcher.find()) { - return matcher.group(groupIndex); - } - return null; + final MutableObj result = new MutableObj<>(); + get(pattern, content, matcher -> result.set(matcher.group(groupIndex))); + return result.get(); } /** @@ -158,11 +159,29 @@ public static String get(Pattern pattern, CharSequence content, String groupName if (null == content || null == pattern || null == groupName) { return null; } + + final MutableObj result = new MutableObj<>(); + get(pattern, content, matcher -> result.set(matcher.group(groupName))); + return result.get(); + } + + /** + * 在给定字符串中查找给定规则的字符,如果找到则使用{@link Consumer}处理之
+ * 如果内容中有多个匹配项,则只处理找到的第一个结果。 + * + * @param pattern 匹配的正则 + * @param content 被匹配的内容 + * @param consumer 匹配到的内容处理器 + * @since 5.7.15 + */ + public static void get(Pattern pattern, CharSequence content, Consumer consumer) { + if (null == content || null == pattern || null == consumer) { + return; + } final Matcher m = pattern.matcher(content); if (m.find()) { - return m.group(groupName); + consumer.accept(m); } - return null; } /** @@ -225,7 +244,7 @@ public static Map getAllGroupNames(Pattern pattern, CharSequence if (m.find()) { // 通过反射获取 namedGroups 方法 final Map map = ReflectUtil.invoke(pattern, "namedGroups"); - map.forEach((key, value)-> result.put(key, m.group(value))); + map.forEach((key, value) -> result.put(key, m.group(value))); } return result; } @@ -278,7 +297,6 @@ public static String extractMulti(String regex, CharSequence content, String tem return null; } - // Pattern pattern = Pattern.compile(regex, Pattern.DOTALL); final Pattern pattern = PatternPool.get(regex, Pattern.DOTALL); return extractMulti(pattern, content, template); } @@ -329,7 +347,6 @@ public static String extractMultiAndDelPre(String regex, Holder co return null; } - // Pattern pattern = Pattern.compile(regex, Pattern.DOTALL); final Pattern pattern = PatternPool.get(regex, Pattern.DOTALL); return extractMultiAndDelPre(pattern, contentHolder, template); } @@ -346,7 +363,6 @@ public static String delFirst(String regex, CharSequence content) { return StrUtil.str(content); } - // Pattern pattern = Pattern.compile(regex, Pattern.DOTALL); final Pattern pattern = PatternPool.get(regex, Pattern.DOTALL); return delFirst(pattern, content); } @@ -427,7 +443,6 @@ public static String delAll(String regex, CharSequence content) { return StrUtil.str(content); } - // Pattern pattern = Pattern.compile(regex, Pattern.DOTALL); final Pattern pattern = PatternPool.get(regex, Pattern.DOTALL); return delAll(pattern, content); } @@ -459,9 +474,23 @@ public static String delPre(String regex, CharSequence content) { return StrUtil.str(content); } - // Pattern pattern = Pattern.compile(regex, Pattern.DOTALL); final Pattern pattern = PatternPool.get(regex, Pattern.DOTALL); - Matcher matcher = pattern.matcher(content); + return delPre(pattern, content); + } + + /** + * 删除正则匹配到的内容之前的字符 如果没有找到,则返回原文 + * + * @param pattern 定位正则模式 + * @param content 被查找的内容 + * @return 删除前缀后的新内容 + */ + public static String delPre(Pattern pattern, CharSequence content) { + if (null == content || null == pattern) { + return StrUtil.str(content); + } + + final Matcher matcher = pattern.matcher(content); if (matcher.find()) { return StrUtil.sub(content, matcher.end(), content.length()); } @@ -520,7 +549,7 @@ public static > T findAll(String regex, CharSequenc return collection; } - return findAll(Pattern.compile(regex, Pattern.DOTALL), content, group, collection); + return findAll(PatternPool.get(regex, Pattern.DOTALL), content, group, collection); } /** @@ -574,16 +603,29 @@ public static > T findAll(Pattern pattern, CharSequ if (null == pattern || null == content) { return null; } + Assert.notNull(collection, "Collection must be not null !"); - if (null == collection) { - throw new NullPointerException("Null collection param provided!"); + findAll(pattern, content, (matcher) -> collection.add(matcher.group(group))); + return collection; + } + + /** + * 取得内容中匹配的所有结果,使用{@link Consumer}完成匹配结果处理 + * + * @param pattern 编译后的正则模式 + * @param content 被查找的内容 + * @param consumer 匹配结果处理函数 + * @since 5.7.15 + */ + public static void findAll(Pattern pattern, CharSequence content, Consumer consumer) { + if (null == pattern || null == content) { + return; } final Matcher matcher = pattern.matcher(content); while (matcher.find()) { - collection.add(matcher.group(group)); + consumer.accept(matcher); } - return collection; } /** @@ -598,7 +640,6 @@ public static int count(String regex, CharSequence content) { return 0; } - // Pattern pattern = Pattern.compile(regex, Pattern.DOTALL); final Pattern pattern = PatternPool.get(regex, Pattern.DOTALL); return count(pattern, content); } From 05141a7927192bc729fc81147e655b4b977a8dd4 Mon Sep 17 00:00:00 2001 From: Looly Date: Fri, 15 Oct 2021 00:43:15 +0800 Subject: [PATCH 22/51] add methods --- .../src/main/java/cn/hutool/json/serialize/JSONWriter.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/hutool-json/src/main/java/cn/hutool/json/serialize/JSONWriter.java b/hutool-json/src/main/java/cn/hutool/json/serialize/JSONWriter.java index f08548ee58..c57ef24883 100755 --- a/hutool-json/src/main/java/cn/hutool/json/serialize/JSONWriter.java +++ b/hutool-json/src/main/java/cn/hutool/json/serialize/JSONWriter.java @@ -62,13 +62,13 @@ public class JSONWriter extends Writer { private boolean arrayMode; /** - * 创建{@link JSONWriter} + * 创建JSONWriter * * @param writer {@link Writer} * @param indentFactor 缩进因子,定义每一级别增加的缩进量 * @param indent 本级别缩进量 * @param config JSON选项 - * @return {@link JSONWriter} + * @return JSONWriter */ public static JSONWriter of(Writer writer, int indentFactor, int indent, JSONConfig config) { return new JSONWriter(writer, indentFactor, indent, config); From 5cd7e806e1f7f7e969c8a4f0eb1c5b53d744ab0b Mon Sep 17 00:00:00 2001 From: Looly Date: Fri, 15 Oct 2021 01:27:16 +0800 Subject: [PATCH 23/51] add methods --- CHANGELOG.md | 1 + .../main/java/cn/hutool/json/JSONArray.java | 42 +++++++++++++++++- .../main/java/cn/hutool/json/JSONObject.java | 44 ++++++++++++++++++- .../java/cn/hutool/json/JSONArrayTest.java | 24 ++++++++++ .../java/cn/hutool/json/JSONObjectTest.java | 24 ++++++++++ 5 files changed, 132 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a1df0b9d19..99d34efd73 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,7 @@ * 【core 】 增加RingIndexUtil(pr#438@Gitee) * 【core 】 Assert增加checkBetween重载(pr#436@Gitee) * 【core 】 ReUtil增加命名分组重载(pr#439@Gitee) +* 【json 】 toString和writer增加Filter(issue#I4DQNQ@Gitee) * ### 🐞Bug修复 * 【core 】 修复CollUtil.isEqualList两个null返回错误问题(issue#1885@Github) diff --git a/hutool-json/src/main/java/cn/hutool/json/JSONArray.java b/hutool-json/src/main/java/cn/hutool/json/JSONArray.java index fdfafabc7d..3cdea5a839 100644 --- a/hutool-json/src/main/java/cn/hutool/json/JSONArray.java +++ b/hutool-json/src/main/java/cn/hutool/json/JSONArray.java @@ -3,6 +3,8 @@ import cn.hutool.core.bean.BeanPath; import cn.hutool.core.collection.ArrayIter; import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.lang.Filter; +import cn.hutool.core.lang.Pair; import cn.hutool.core.text.StrJoiner; import cn.hutool.core.util.ArrayUtil; import cn.hutool.core.util.ObjectUtil; @@ -12,6 +14,7 @@ import cn.hutool.json.serialize.JSONSerializer; import cn.hutool.json.serialize.JSONWriter; +import java.io.StringWriter; import java.io.Writer; import java.util.ArrayList; import java.util.Collection; @@ -529,11 +532,48 @@ public String toString() { return this.toJSONString(0); } + /** + * 返回JSON字符串
+ * 支持过滤器,即选择哪些字段或值不写出 + * + * @param indentFactor 每层缩进空格数 + * @param filter 键值对过滤器 + * @return JSON字符串 + * @since 5.7.15 + */ + public String toJSONString(int indentFactor, Filter> filter){ + final StringWriter sw = new StringWriter(); + synchronized (sw.getBuffer()) { + return this.write(sw, indentFactor, 0, filter).toString(); + } + } + @Override public Writer write(Writer writer, int indentFactor, int indent) throws JSONException { + return write(writer, indentFactor, indent, null); + } + + /** + * 将JSON内容写入Writer
+ * 支持过滤器,即选择哪些字段或值不写出 + * + * @param writer writer + * @param indentFactor 缩进因子,定义每一级别增加的缩进量 + * @param indent 本级别缩进量 + * @param filter 过滤器 + * @return Writer + * @throws JSONException JSON相关异常 + * @since 5.7.15 + */ + public Writer write(Writer writer, int indentFactor, int indent, Filter> filter) throws JSONException { final JSONWriter jsonWriter = JSONWriter.of(writer, indentFactor, indent, config) .beginArray(); - this.forEach(jsonWriter::writeValue); + + CollUtil.forEach(this, (value, index)->{ + if (null == filter || filter.accept(new Pair<>(index, value))) { + jsonWriter.writeValue(value); + } + }); jsonWriter.end(); // 此处不关闭Writer,考虑writer后续还需要填内容 return writer; diff --git a/hutool-json/src/main/java/cn/hutool/json/JSONObject.java b/hutool-json/src/main/java/cn/hutool/json/JSONObject.java index 841d043c38..7358cedec3 100644 --- a/hutool-json/src/main/java/cn/hutool/json/JSONObject.java +++ b/hutool-json/src/main/java/cn/hutool/json/JSONObject.java @@ -6,6 +6,8 @@ import cn.hutool.core.bean.copier.CopyOptions; import cn.hutool.core.collection.CollectionUtil; import cn.hutool.core.convert.Convert; +import cn.hutool.core.lang.Filter; +import cn.hutool.core.lang.Pair; import cn.hutool.core.map.CaseInsensitiveLinkedMap; import cn.hutool.core.map.CaseInsensitiveMap; import cn.hutool.core.map.MapUtil; @@ -18,6 +20,7 @@ import cn.hutool.json.serialize.JSONSerializer; import cn.hutool.json.serialize.JSONWriter; +import java.io.StringWriter; import java.io.Writer; import java.math.BigDecimal; import java.math.BigInteger; @@ -552,17 +555,54 @@ public String toString() { return this.toJSONString(0); } + /** + * 返回JSON字符串
+ * 支持过滤器,即选择哪些字段或值不写出 + * + * @param indentFactor 每层缩进空格数 + * @param filter 键值对过滤器 + * @return JSON字符串 + * @since 5.7.15 + */ + public String toJSONString(int indentFactor, Filter> filter){ + final StringWriter sw = new StringWriter(); + synchronized (sw.getBuffer()) { + return this.write(sw, indentFactor, 0, filter).toString(); + } + } + @Override public Writer write(Writer writer, int indentFactor, int indent) throws JSONException { + return write(writer, indentFactor, indent, null); + } + + /** + * 将JSON内容写入Writer
+ * 支持过滤器,即选择哪些字段或值不写出 + * + * @param writer writer + * @param indentFactor 缩进因子,定义每一级别增加的缩进量 + * @param indent 本级别缩进量 + * @param filter 过滤器 + * @return Writer + * @throws JSONException JSON相关异常 + * @since 5.7.15 + */ + public Writer write(Writer writer, int indentFactor, int indent, Filter> filter) throws JSONException { final JSONWriter jsonWriter = JSONWriter.of(writer, indentFactor, indent, config) .beginObj(); - this.forEach(jsonWriter::writeField); + this.forEach((key, value) -> { + if (null == filter || filter.accept(new Pair<>(key, value))) { + jsonWriter.writeField(key, value); + } + }); jsonWriter.end(); - + // 此处不关闭Writer,考虑writer后续还需要填内容 return writer; } // ------------------------------------------------------------------------------------------------- Private method start + /** * Bean对象转Map * diff --git a/hutool-json/src/test/java/cn/hutool/json/JSONArrayTest.java b/hutool-json/src/test/java/cn/hutool/json/JSONArrayTest.java index c4f8c6b6a4..2b3522b9a4 100644 --- a/hutool-json/src/test/java/cn/hutool/json/JSONArrayTest.java +++ b/hutool-json/src/test/java/cn/hutool/json/JSONArrayTest.java @@ -239,4 +239,28 @@ static class User { private Integer id; private String name; } + + @Test + public void filterIncludeTest(){ + JSONArray json1 = JSONUtil.createArray() + .set("value1") + .set("value2") + .set("value3") + .set(true); + + final String s = json1.toJSONString(0, (pair) -> pair.getValue().equals("value2")); + Assert.assertEquals("[\"value2\"]", s); + } + + @Test + public void filterExcludeTest(){ + JSONArray json1 = JSONUtil.createArray() + .set("value1") + .set("value2") + .set("value3") + .set(true); + + final String s = json1.toJSONString(0, (pair) -> false == pair.getValue().equals("value2")); + Assert.assertEquals("[\"value1\",\"value3\",true]", s); + } } diff --git a/hutool-json/src/test/java/cn/hutool/json/JSONObjectTest.java b/hutool-json/src/test/java/cn/hutool/json/JSONObjectTest.java index 03ce355415..f2983b8591 100644 --- a/hutool-json/src/test/java/cn/hutool/json/JSONObjectTest.java +++ b/hutool-json/src/test/java/cn/hutool/json/JSONObjectTest.java @@ -609,4 +609,28 @@ public void bigDecimalTest(){ class BigDecimalBean{ private BigDecimal orderId; } + + @Test + public void filterIncludeTest(){ + JSONObject json1 = JSONUtil.createObj(JSONConfig.create().setOrder(true)) + .set("a", "value1") + .set("b", "value2") + .set("c", "value3") + .set("d", true); + + final String s = json1.toJSONString(0, (pair) -> pair.getKey().equals("b")); + Assert.assertEquals("{\"b\":\"value2\"}", s); + } + + @Test + public void filterExcludeTest(){ + JSONObject json1 = JSONUtil.createObj(JSONConfig.create().setOrder(true)) + .set("a", "value1") + .set("b", "value2") + .set("c", "value3") + .set("d", true); + + final String s = json1.toJSONString(0, (pair) -> false == pair.getKey().equals("b")); + Assert.assertEquals("{\"a\":\"value1\",\"c\":\"value3\",\"d\":true}", s); + } } From 6b3fc153d7b71bf747e39ef54a1c138aac6a8b18 Mon Sep 17 00:00:00 2001 From: Looly Date: Fri, 15 Oct 2021 02:04:42 +0800 Subject: [PATCH 24/51] add test --- .../hutool/core/io/file/FileSystemUtil.java | 37 +++++++++++++++++++ .../java/cn/hutool/core/io/file/PathUtil.java | 13 +++++++ .../core/io/file/FileSystemUtilTest.java | 31 ++++++++++++++++ 3 files changed, 81 insertions(+) create mode 100644 hutool-core/src/test/java/cn/hutool/core/io/file/FileSystemUtilTest.java diff --git a/hutool-core/src/main/java/cn/hutool/core/io/file/FileSystemUtil.java b/hutool-core/src/main/java/cn/hutool/core/io/file/FileSystemUtil.java index 65dc37a5fe..502bf09beb 100644 --- a/hutool-core/src/main/java/cn/hutool/core/io/file/FileSystemUtil.java +++ b/hutool-core/src/main/java/cn/hutool/core/io/file/FileSystemUtil.java @@ -2,13 +2,17 @@ import cn.hutool.core.io.IORuntimeException; import cn.hutool.core.map.MapUtil; +import cn.hutool.core.util.CharsetUtil; import cn.hutool.core.util.StrUtil; import java.io.IOException; +import java.net.URI; +import java.nio.charset.Charset; import java.nio.file.FileSystem; import java.nio.file.FileSystems; import java.nio.file.Path; import java.nio.file.Paths; +import java.util.HashMap; /** * {@link FileSystem}相关工具类封装
@@ -35,6 +39,39 @@ public static FileSystem create(String path) { } } + /** + * 创建 Zip的{@link FileSystem},默认UTF-8编码 + * + * @param path 文件路径,可以是目录或Zip文件等 + * @return {@link FileSystem} + */ + public static FileSystem createZip(String path) { + return createZip(path, null); + } + + /** + * 创建 Zip的{@link FileSystem} + * + * @param path 文件路径,可以是目录或Zip文件等 + * @param charset 编码 + * @return {@link FileSystem} + */ + public static FileSystem createZip(String path, Charset charset) { + if(null == charset){ + charset = CharsetUtil.CHARSET_UTF_8; + } + final HashMap env = new HashMap<>(); + env.put("create", "true"); + env.put("encoding", charset.name()); + + try { + return FileSystems.newFileSystem( + URI.create("jar:" + Paths.get(path).toUri()), env); + } catch (IOException e) { + throw new IORuntimeException(e); + } + } + /** * 获取目录的根路径,或Zip文件中的根路径 * diff --git a/hutool-core/src/main/java/cn/hutool/core/io/file/PathUtil.java b/hutool-core/src/main/java/cn/hutool/core/io/file/PathUtil.java index d9423d1267..95abe2c522 100644 --- a/hutool-core/src/main/java/cn/hutool/core/io/file/PathUtil.java +++ b/hutool-core/src/main/java/cn/hutool/core/io/file/PathUtil.java @@ -55,6 +55,19 @@ public static boolean isDirEmpty(Path dirPath) { } } + /** + * 递归遍历目录以及子目录中的所有文件
+ * 如果提供path为文件,直接返回过滤结果 + * + * @param path 当前遍历文件或目录 + * @param fileFilter 文件过滤规则对象,选择要保留的文件,只对文件有效,不过滤目录,null表示接收全部文件 + * @return 文件列表 + * @since 5.4.1 + */ + public static List loopFiles(Path path, FileFilter fileFilter) { + return loopFiles(path, -1, fileFilter); + } + /** * 递归遍历目录以及子目录中的所有文件
* 如果提供path为文件,直接返回过滤结果 diff --git a/hutool-core/src/test/java/cn/hutool/core/io/file/FileSystemUtilTest.java b/hutool-core/src/test/java/cn/hutool/core/io/file/FileSystemUtilTest.java new file mode 100644 index 0000000000..130b400f81 --- /dev/null +++ b/hutool-core/src/test/java/cn/hutool/core/io/file/FileSystemUtilTest.java @@ -0,0 +1,31 @@ +package cn.hutool.core.io.file; + +import cn.hutool.core.lang.Console; +import cn.hutool.core.util.CharsetUtil; +import org.junit.Ignore; +import org.junit.Test; + +import java.nio.file.FileSystem; +import java.nio.file.FileVisitResult; +import java.nio.file.Path; +import java.nio.file.SimpleFileVisitor; +import java.nio.file.attribute.BasicFileAttributes; + +public class FileSystemUtilTest { + + @Test + @Ignore + public void listTest(){ + final FileSystem fileSystem = FileSystemUtil.createZip("d:/test/test.zip", + CharsetUtil.CHARSET_GBK); + final Path root = FileSystemUtil.getRoot(fileSystem); + PathUtil.walkFiles(root, new SimpleFileVisitor() { + + @Override + public FileVisitResult visitFile(Path path, BasicFileAttributes attrs) { + Console.log(path); + return FileVisitResult.CONTINUE; + } + }); + } +} From 612c2d8a98285e1fddf330f91cea99864b993553 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=94=B3=E5=8A=AD=E6=98=8E?= <626546063@qq.com> Date: Fri, 15 Oct 2021 01:37:16 +0000 Subject: [PATCH 25/51] add method --- .../java/cn/hutool/core/util/ZipUtil.java | 68 ++++++++++++++++--- 1 file changed, 60 insertions(+), 8 deletions(-) diff --git a/hutool-core/src/main/java/cn/hutool/core/util/ZipUtil.java b/hutool-core/src/main/java/cn/hutool/core/util/ZipUtil.java index c40ded1cd2..064642772e 100644 --- a/hutool-core/src/main/java/cn/hutool/core/util/ZipUtil.java +++ b/hutool-core/src/main/java/cn/hutool/core/util/ZipUtil.java @@ -9,17 +9,14 @@ import cn.hutool.core.io.FileUtil; import cn.hutool.core.io.IORuntimeException; import cn.hutool.core.io.IoUtil; +import cn.hutool.core.io.file.FileSystemUtil; import cn.hutool.core.io.resource.Resource; -import java.io.BufferedInputStream; -import java.io.ByteArrayInputStream; -import java.io.ByteArrayOutputStream; -import java.io.File; -import java.io.FileFilter; -import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; +import java.io.*; import java.nio.charset.Charset; +import java.nio.file.FileSystem; +import java.nio.file.*; +import java.nio.file.attribute.BasicFileAttributes; import java.util.ArrayList; import java.util.Collections; import java.util.List; @@ -75,6 +72,61 @@ public static InputStream getStream(ZipFile zipFile, ZipEntry zipEntry) { } } + /** + * 在zip文件中添加新文件, 如果已经存在则不会有效果 + * + * @param zipFilePathStr zip文件存储路径 + * @param appendFilePathStr 待添加文件路径(可以是文件夹) + */ + public static void addFile(String zipFilePathStr, String appendFilePathStr) throws IOException { + Path zipPath = Paths.get(zipFilePathStr); + Path appendFilePath = Paths.get(appendFilePathStr); + + try (FileSystem zipFileSystem = FileSystemUtil.createZip(zipPath.toString())) { + Path root = zipFileSystem.getPath("/"); + Path dest = zipFileSystem.getPath(root.toString(), appendFilePath.getFileName().toString()); + if (!Files.isDirectory(appendFilePath)) { + Files.copy(appendFilePath, dest, StandardCopyOption.COPY_ATTRIBUTES); + } else { + Files.walkFileTree(appendFilePath, new SimpleFileVisitor() { + /** + * 用于保证文件夹拷贝后的效果跟常见压缩软件的效果相同 + */ + private String dirRoot = null; + + @Override + public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException { + final Path dest; + if (dirRoot != null) { + dest = zipFileSystem.getPath(root.toString(), dirRoot, File.separator, StrUtil.subAfter(file.toString(), dirRoot, false)); + } else { + dest = zipFileSystem.getPath(root.toString(), file.toString()); + } + Files.copy(file, dest, StandardCopyOption.COPY_ATTRIBUTES); + return FileVisitResult.CONTINUE; + } + + @Override + public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) throws IOException { + final Path dirToCreate; + if (dirRoot == null) { + dirToCreate = zipFileSystem.getPath(root.toString(), dir.getFileName().toString()); + dirRoot = dir.getFileName().toString(); + } else { + dirToCreate = zipFileSystem.getPath(root.toString(), dirRoot, File.separator, StrUtil.subAfter(dir.toString(), dirRoot, false)); + } + if (Files.notExists(dirToCreate)) { + Files.createDirectories(dirToCreate); + } + return FileVisitResult.CONTINUE; + } + }); + } + } catch (FileAlreadyExistsException ignored) { + // 文件已存在, 跳过 + } + } + /** * 打包到当前目录,使用默认编码UTF-8 * From 3ed26fe7619ac0684f09fd14e175b85293fff487 Mon Sep 17 00:00:00 2001 From: Looly Date: Fri, 15 Oct 2021 19:40:50 +0800 Subject: [PATCH 26/51] add test --- .../cn/hutool/poi/excel/CellEditorTest.java | 46 ++++++++++++++++++ .../src/test/resources/cell_editor_test.xlsx | Bin 0 -> 9271 bytes 2 files changed, 46 insertions(+) create mode 100644 hutool-poi/src/test/java/cn/hutool/poi/excel/CellEditorTest.java create mode 100644 hutool-poi/src/test/resources/cell_editor_test.xlsx diff --git a/hutool-poi/src/test/java/cn/hutool/poi/excel/CellEditorTest.java b/hutool-poi/src/test/java/cn/hutool/poi/excel/CellEditorTest.java new file mode 100644 index 0000000000..9046324f45 --- /dev/null +++ b/hutool-poi/src/test/java/cn/hutool/poi/excel/CellEditorTest.java @@ -0,0 +1,46 @@ +package cn.hutool.poi.excel; + +import cn.hutool.poi.excel.cell.CellEditor; +import lombok.AllArgsConstructor; +import lombok.Data; +import org.apache.poi.ss.usermodel.Cell; +import org.junit.Assert; + +import java.io.Serializable; +import java.util.List; + +public class CellEditorTest { + + @org.junit.Test + public void readTest(){ + ExcelReader excelReader= ExcelUtil.getReader("cell_editor_test.xlsx"); + excelReader.setCellEditor(new ExcelHandler()); + List excelReaderObjects=excelReader.readAll(Test.class); + + Assert.assertEquals("0", excelReaderObjects.get(0).getTest1()); + Assert.assertEquals("b", excelReaderObjects.get(0).getTest2()); + Assert.assertEquals("0", excelReaderObjects.get(1).getTest1()); + Assert.assertEquals("b1", excelReaderObjects.get(1).getTest2()); + Assert.assertEquals("0", excelReaderObjects.get(2).getTest1()); + Assert.assertEquals("c2", excelReaderObjects.get(2).getTest2()); + } + + @AllArgsConstructor + @Data + public static class Test implements Serializable { + private static final long serialVersionUID = 1L; + + private String test1; + private String test2; + } + + public static class ExcelHandler implements CellEditor { + @ Override + public Object edit(Cell cell, Object o) { + if (cell.getColumnIndex()==0 && cell.getRowIndex() != 0){ + o="0"; + } + return o; + } + } +} diff --git a/hutool-poi/src/test/resources/cell_editor_test.xlsx b/hutool-poi/src/test/resources/cell_editor_test.xlsx new file mode 100644 index 0000000000000000000000000000000000000000..af1e0326ed196383c26b7652c648d76e19dca7bd GIT binary patch literal 9271 zcmeHtg9oZ~HH{K&AGub1xT8r{0c)Y?p3KVN`iJ zDtQ~Z5&afQrf;%7ee+im<{yDf)*3o>s67lCjuS?V$D+hli;M=+l3yG-t_;-Z%`i&i ztK1L8ZWQw6z$KNs@ePS_`~}1hpitzQCX^>FixwRAw0E=&g#3-@yx)3`RJWxoq9tS) zI$1Z%;VeAP|^q|n;NIj2d0oJLvHeOLF z?u<+W3ry;4NrYvnB<(8U%A9zpFnJ@ zs++w`e~5sm5})f7sLj^s+89PjZBy{d{~A29^&sh*?V`VxZzPF)_B92133 zY*FWNCm#BBhNF5!;@B+%R@%Pfo{)P~!Ed1*p6Ch^9S~ioV!inI=utw>A|S)ZoG7VU z_!JW{6mw6;-FE)$Ir(J;1Zz3BNQ!DL{Sb@n7UpMncL)IWf4FIr4j0`C40}&tPcdL_ zYU&Ph^x)+9dHkPy{ule?--ce9tfJP(g&7G|_z^X9KD(TVBdzQsrO-~J9TcLtgxwTh zOi#Al%|wo)O&W$EAKV%AV+67+8ox6{bM~39G7b+YLf7n56`676=7qw_=$`)At#Z8| z*L(JK_AE9ltWd-EDh4YS7u9gH z(pulEB5U6Y7VM-#@CV4$Hm>j!o|hmXB?~4nR0%~1q{q@43-QkHG@Y~SDdAn zjr*vg0cqk)V@1g^L*i=4Wz?AsUgZxjv3uI)&0JT zNva~%rqb~J*7^5-%tRW3H$0jLvG2FClsvgFZodcg4cL!gjsQZG-ZSf!zj&zpT8KSH z^b3C#b^TfqJ$kx@-LRp0@^Fr(4q>__q)P`+w~&wqkE&-a4oP`CvvfCEw;Tz#ks5t# zG-tM6hHWO{U7Bp=r>3l1_7!o2 z{j`TQdl*d1cte^Er#i50LmP=bSdP~S=ZrlQ%tR$d4NpKR`AA{z}IilvV_9hy{vn>9+J z(OO3+_x$}-82k>lOUGl?_#13zx;Ltyz=%e8-!HGNLgS)^<;POF)&hjPz~w86Ol926 zS>K=bumEw-DJ!&PFAl#>_bKumI`M9|=WTyhoWNXiwL#Xhb+<8OFwv(^vm;Ua zvlq%p@mIGPV+xOVT3NU=1*Ru(W-tZuyx+bmQLyQq)bc2VhPRZ~6c~9i=Veo5l~dKB=f^-6zR?_1FE)iXFm#mP z1TzLvqzMhq)YWouac7_O2aKov8~LH)sTs#G#J9oFPYOVVgCYOV2=RCP|0i0&!N?rU zfBw6-N=+5zei)g-xQ*iSdFf4nyWq)5yQi~@jXK=O0%4@#4LVsRV{J3h|D?=`;1cNv z9UJsM=g0btfPdPDuU!Pc07iFYVo~_5-g8|kGHq8Tk#zOE87FVRXlm< zVIJ_&<^?kYNmTJ`TG^A<=}12NaV{?&dyh$q5!h-u+PD#N6|S{c1#W!%mO7io`s-b* z4CD+sAM8gpbxeY$DEhz;FtzoOhV7z9?hN&)k*c2$kD^m6R7pZ@Zjt!Y^MJWnc#9b)qWUUu3&&7hQ0CE zV(DQE0(p9H{&C{|xoYMl8>#hj5l2E*eqgIW4Fo0WMk6)QrbNDMcX_Vr%V0OKmlEH# zn?{A83HF0_+nW7XpiZlwy^9G9LXW4jldTn%(1^|1>^2z!gI}IGGarV&jAe5uC^0%X z@ec6QsH`%&K&Hl_CpMjC3>UEZ5Qofn9^=%r_QWZ76Ha0|=kgO=OCQ=)I2OH z@>xzgha-&>#l{$rVa(Cwi@9Hq98mz)@=4 z+@++%f#BV;4Aur1vX|JICZ*W`u85z-)T7ldJBOuT}C)D#)z$86!r2F1%}&(nR&F@ zu-=wOXXo}a9Fr3-wAGJap>ulQpt`Rhrc%(;@sXw^>p{?bLviJ{=|9~d{G7u-CNgzO z3O$SqCt|%KTu9QAuZh~SU?47>*cB9q@92N6TKGi9wNT{C-OyXqX`m%(z!6r)wHCV= zvHHmY@1dSv*fWw{8Nse71)A|UQ9D*W<`=3p9bYB-zs>5D=|u_uSY&s3oNX<~+!Z7^ zU{tsR9x-ZA;YVR!K?h;5x_omQp)Bxa`}nHh=o4Y;Z8rqv|9=(V3n}zoNldvKo(;e! zT3V%2GCdPuc%E6K9aC9K;$t~AsP^7|clQNU4^OCY>T8Mfnw1K)5Yfg(3OgntC$9|O z4O~=B|9l>qf<{X-`x_^s&6^W3C;V_C2Li#i1{R8=) zwjd`E=O4#EK)+`&;Q|o=c45!oQE%-VHZv?>?Esq{nH*A@ZI`gbrUjhp3T0(VQb^@O zO?*hW3dp=kY9UyCA@C5pj2l7r=KX|uItC-FCxSOg@XM3LsPsGj2~?~8R~IsKE~2Vs z9+AAN`ipK3bE0!S+u81S*|u*HvxrF_y);upAR-MUS!Oo$ zCT-Ua-w@E>zFDg`mu|q{CM6?87ZIycL^M5&wjL)bq9D4;sz1gJdT1O)%dsXkc=|El zXu12ToPPu^frul2d*Kz^9(vfeoylp|*x*amK#7CC{rBDQ>UHsCd?@SGOBY%IzN~i& z9vp2G1#|nSVel29N6K-~poY~l65p|4%Qz3gLhbIC8LSA@SP&5`%<* zKQ5Pg1v{NrT}XYAVp$Z`-cCNn5hyyf6-*>4j?(oNIfxp2w%#Et#qU_I?9FZYW?shu z?OA_44S+t8R!UR8lF&DjQGEcn$SMc-Tl5D@<_;t!k|^t%{Rf@MKQbgtP2NNoh&|_X z7{g~3&D*`m=nlF$N{}%9M)}?ld41q~!u0m`#{l-~kJC}!xO3;o_rd|Pcpq{h7fY>! zc*NNe5|lPBHr%ne+z-_;yV$Eya2`?k`bU&KnCr7frLrpE6jl`c8tt_BqvkGLy->HO_pLP!sl|Ia%KRRAtR46Ye#|X4 zf-b*K>p6R|zd|(boF&gNu?_FKD7`8+u7kp3D@UNFou>lc(CElx98IVG&5O_(%Z~y( z;^MqX2Zq%4G!nfZQNO?$qNmOqzfhMx4WXjJg(&1z*2lacbA`*wvmD9k0eUz$w5Ig< zHkGWxJJprmWLD$hXQ8>SC4>*-JvF9D~ zZWY(7IJNp_5%`!{43{l7p~bMqBQxo>jIA~qTGPuV#}z_uV?rYY1i^{KdN&@%E_zz= zm??O|Q~svYoa5fp91)E7=2bP>vy8;12UXsfqZ^qj$71$XjOWFzzOM8m-kkG|eYesZ zTeSxJ?E?F=s@~OSgAVZ1XHE*Xxmon2gm`0KbXMzL^dE<+!{s2oMGVU|T214h8G({c zDFpgFLba1SEus0)8*{P<=DZQnuY-Wyy^#%LyVpKQDLVv!-6$`l)GTM^YLO@=`HYN3 zGOI=E!nBu)(rkUiDsCFNSLVYyn{6hqKNc{_)CG|#k&bBx`ep6O(E znq{WLlO>WFkT3?#sb6f1c;XnO_dt;G25*?TL)eMx`Cm47T8~L*$Ol%(&Fw%L#$ua zKK^WC6I9!--{cO~uVK?!uSlS{I!6@M_T;51!~%ANZP538$dahA4AW`}Yj%t zeLK?VhwrWM{eFk>JGvHPm-%i2M%P+lf$PDaLCeF_&k^MDlcSaBj5*Ip;&jm+VpVju z-zt(P4|&UF8h$vifiS~)yp#(K} z>*d%YTdF?(DB*M7c86Lnynz!7mNZ5TEbU!a1$6BwI*LL=XfEG0N)RGjBTsofQ^;;b zxdl?7g3o8Q08R2lz@?pn`5wxY#$Hnp?eyfF=cw_eYr3|QVHkQ-lE45iju%Vjio&VX zrYaJ_er0`sfUHrk6rP2IGWtN;V?rgAEC>2z)J4Okz`JMXUrHOwqs;>5l<1gjW0$kg zkl*-7uTI$xlP^8V+2sBpB?FRc3~jbwco3OzX}3gjF!%_Hw2EGj*w(Mf?lD=kAJAze zlZr>$_6nn1zA7Voua|jW?0_uFpkBQ#9B#368Pq}mLMu&+sO2}RUYaZC|G|;kG-+j^ z$y2YL?3z)36Sogo2c{mg30grb==R#A-Ra!TIY)qfEp1lN1RMn)$HT?AK4@{%{6wUj zm)CRbXt72466iAB96Zadm;d!OgvfRh3!Yuzxe?=?G%?rcRU-JE&bu}=mt+qp5|74o ztYb@Om)`3O*B}|?W?U}(+*zY$o#s%5!}Wk|R)QBf*oF{g1Sd?>#;Jz``QtVEVCe>?%|eqWjo)?KGUfQw=X>cjR~k3D%#S)o zklr|rw=!}%RT7Q8Dx&g}PUYJja7bMKY^nlqFu*tmiQA z{&%(};>4re0~@?FEODp%(+EFP`@gcaKXd(GIoltZet1GpY%dp3?i}G(#@9dPQv{}r zmLP45&Nl>$t3_l;M&dc}#=n6a^zQ3d*NLwJFRw-4kXd6TJ^a`PAF*LcWLtaSzsTM# zRrhFT35donnrJ@5(Upy$s;U$-n5a*HQ5T^>e?#0C)W1;o0_{zr_~wFQj33wVWgOXL zWx2i6kts?er)m@Q;YOfdn6Q|p!R_9QKq^t^;_h*Uqls`up+qzBA_bu#x$j3I7E-%D zXL$~RSctEkK8zlpq5aMRuK2N{?XZ!r!4^QKKP})2n}1J`yB5gP^Cu8E@7;5ASlBrq znW37gn;n=dSTS2GfS4@~;FB37lK|8rR0`KTEulIWJj6W2+?l*v@N$w5F$&00x$3zZ zmd(w~Jyyow^$)!p=8A)a(?x_Ph33=157{6le~_F`>Ori`7DlA|NQ^Uv*e@;lKA2ik zaCu;cQ#*zx>=LyCp|3c6JL|$R1K|=$1rE%KnE`ic2gIDfp=Pk=%_JcP2g-Cv($Kua zf!l}&PvHb~2N<3CBmg?>|@Vq;m?QCy*D`L@%p zeRfB7NX1<>FXuSf(JfKu1npCr;n!$-mRLzQcBQ7#sbg!B35^X=EijB9ym+xTS=7=s z(o$$_~%coS0g?v|$VFEF>1`LK!y3BWH~^a$-0(ckr3#x*hipk+&1>!xP2- zz`F#noWY+$WARPkqiVK?16!6!f{(CL!q;4CP}r99i3~w&sjnomC%1tX)?EsqBT7u} z=oh-WXmLqd1Qt4s40K((NRn@gbCYF!Gz&?Zm-Eaee9kR3ti|ioyVuC%M>C=lUuxxC z8B&?dU51bc4dS5u?4A1;(_{8TsQR&SIF(cAc1oACTU#kLT-P15W=K?V;H~rmXmL;O z2PCRei#KTzLL{R~3y5F|qJI7|XFCj#MRL)A0S0N{hvZ9D2NBRhmD1!o5Y}a3jtZY` zdZ0)JN#q&{4h|CgadUY0&80_?%0BRsGsWO$an9KTAi01yG6D)(R5)80;q+VkLfS4k zQ3`}}OoaWAhDtbBUyl!SYEL*j5Av=VncvcxQw4JGb5!+>n6ieKI#Ci1)a&9Z3BL=1 zT!UL)%RPoAW!uPe@FQ@&|a*Xxc;@W)$_fwl+3d#unQ1C0!xi5O3 z8~ze4g4MKPBk;J-6z^-ePpp1vC?&aXgWuAu`wH*#lwS(p!0L^#PrAoa?%)5xTJ9_T yXYl`}5&(EZ1_1nv3fvd}=gR$eaW=}oiT|~Zt1BbHFbDu(!gfX&jIXJG-u*v0A)lrI literal 0 HcmV?d00001 From 644d1c22c7c9adf13c26ed3e24fc1f8be6a28604 Mon Sep 17 00:00:00 2001 From: Looly Date: Sat, 16 Oct 2021 00:27:13 +0800 Subject: [PATCH 27/51] fix null bug --- CHANGELOG.md | 3 ++- .../main/java/cn/hutool/poi/excel/ExcelReader.java | 12 +++++++----- .../test/java/cn/hutool/poi/excel/ExcelReadTest.java | 8 ++++++++ 3 files changed, 17 insertions(+), 6 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 99d34efd73..ad080878ef 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,7 +3,7 @@ ------------------------------------------------------------------------------------------------------------- -# 5.7.15 (2021-10-15) +# 5.7.15 (2021-10-16) ### 🐣新特性 * 【db 】 Db.quietSetAutoCommit增加判空(issue#I4D75B@Gitee) @@ -16,6 +16,7 @@ * 【core 】 修复CollUtil.isEqualList两个null返回错误问题(issue#1885@Github) * 【poi 】 修复ExcelWriter多余调试信息导致的问题(issue#1884@Github) * 【poi 】 修复TemporalAccessorUtil.toInstant使用DateTimeFormatter导致问题(issue#1891@Github) +* 【poi 】 修复sheet.getRow(y)为null导致的问题(issue#1893@Github) ------------------------------------------------------------------------------------------------------------- diff --git a/hutool-poi/src/main/java/cn/hutool/poi/excel/ExcelReader.java b/hutool-poi/src/main/java/cn/hutool/poi/excel/ExcelReader.java index 80dfb4e4d3..301d393091 100644 --- a/hutool-poi/src/main/java/cn/hutool/poi/excel/ExcelReader.java +++ b/hutool-poi/src/main/java/cn/hutool/poi/excel/ExcelReader.java @@ -281,11 +281,13 @@ public void read(int startRowIndex, int endRowIndex, CellHandler cellHandler) { short columnSize; for (int y = startRowIndex; y <= endRowIndex; y++) { row = this.sheet.getRow(y); - columnSize = row.getLastCellNum(); - Cell cell; - for (short x = 0; x < columnSize; x++) { - cell = row.getCell(x); - cellHandler.handle(cell, CellUtil.getCellValue(cell)); + if(null != row){ + columnSize = row.getLastCellNum(); + Cell cell; + for (short x = 0; x < columnSize; x++) { + cell = row.getCell(x); + cellHandler.handle(cell, CellUtil.getCellValue(cell)); + } } } } diff --git a/hutool-poi/src/test/java/cn/hutool/poi/excel/ExcelReadTest.java b/hutool-poi/src/test/java/cn/hutool/poi/excel/ExcelReadTest.java index 4326a86089..ebe04bf0f9 100644 --- a/hutool-poi/src/test/java/cn/hutool/poi/excel/ExcelReadTest.java +++ b/hutool-poi/src/test/java/cn/hutool/poi/excel/ExcelReadTest.java @@ -4,6 +4,7 @@ import cn.hutool.core.lang.Console; import cn.hutool.core.map.MapUtil; import cn.hutool.core.util.ObjectUtil; +import cn.hutool.poi.excel.cell.CellHandler; import lombok.Data; import org.junit.Assert; import org.junit.Ignore; @@ -225,4 +226,11 @@ public void readEmptyTest(){ final List> maps = reader.readAll(); Console.log(maps); } + + @Test + @Ignore + public void readNullRowTest(){ + final ExcelReader reader = ExcelUtil.getReader("d:/test/1.-.xls"); + reader.read((CellHandler) Console::log); + } } From e04789c32270ec2e4038dfcfa38c5afc4b098b34 Mon Sep 17 00:00:00 2001 From: Looly Date: Mon, 18 Oct 2021 16:54:25 +0800 Subject: [PATCH 28/51] add method --- CHANGELOG.md | 3 ++- .../src/main/java/cn/hutool/http/ContentType.java | 13 +++++++++++++ 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index ad080878ef..baa866a4b1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,7 +3,7 @@ ------------------------------------------------------------------------------------------------------------- -# 5.7.15 (2021-10-16) +# 5.7.15 (2021-10-18) ### 🐣新特性 * 【db 】 Db.quietSetAutoCommit增加判空(issue#I4D75B@Gitee) @@ -11,6 +11,7 @@ * 【core 】 Assert增加checkBetween重载(pr#436@Gitee) * 【core 】 ReUtil增加命名分组重载(pr#439@Gitee) * 【json 】 toString和writer增加Filter(issue#I4DQNQ@Gitee) +* 【core 】 ContentType增加build重载(pr#1898@Github) * ### 🐞Bug修复 * 【core 】 修复CollUtil.isEqualList两个null返回错误问题(issue#1885@Github) diff --git a/hutool-http/src/main/java/cn/hutool/http/ContentType.java b/hutool-http/src/main/java/cn/hutool/http/ContentType.java index d6d998267e..29064428c5 100644 --- a/hutool-http/src/main/java/cn/hutool/http/ContentType.java +++ b/hutool-http/src/main/java/cn/hutool/http/ContentType.java @@ -45,6 +45,7 @@ public enum ContentType { /** * 构造 + * * @param value ContentType值 */ ContentType(String value) { @@ -141,4 +142,16 @@ public static ContentType get(String body) { public static String build(String contentType, Charset charset) { return StrUtil.format("{};charset={}", contentType, charset.name()); } + + /** + * 输出Content-Type字符串,附带编码信息 + * + * @param contentType Content-Type 枚举类型 + * @param charset 编码 + * @return Content-Type字符串 + * @since 5.7.15 + */ + public static String build(ContentType contentType, Charset charset) { + return build(contentType.getValue(), charset); + } } From c9bb53dd03a93e5265162f36adc905c49c217a0b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=94=B3=E5=8A=AD=E6=98=8E?= <626546063@qq.com> Date: Mon, 18 Oct 2021 09:58:56 +0000 Subject: [PATCH 29/51] add support for zip file --- .../core/io/file/visitor/CopyVisitor.java | 38 ++++++++++++++++--- 1 file changed, 33 insertions(+), 5 deletions(-) diff --git a/hutool-core/src/main/java/cn/hutool/core/io/file/visitor/CopyVisitor.java b/hutool-core/src/main/java/cn/hutool/core/io/file/visitor/CopyVisitor.java index 842dbff1ea..e8bd619be7 100644 --- a/hutool-core/src/main/java/cn/hutool/core/io/file/visitor/CopyVisitor.java +++ b/hutool-core/src/main/java/cn/hutool/core/io/file/visitor/CopyVisitor.java @@ -1,7 +1,11 @@ package cn.hutool.core.io.file.visitor; import cn.hutool.core.io.file.PathUtil; +import cn.hutool.core.util.StrUtil; +import com.sun.nio.zipfs.ZipFileSystem; +import com.sun.nio.zipfs.ZipPath; +import java.io.File; import java.io.IOException; import java.nio.file.CopyOption; import java.nio.file.FileAlreadyExistsException; @@ -23,6 +27,8 @@ public class CopyVisitor extends SimpleFileVisitor { private final Path source; private final Path target; private boolean isTargetCreated; + private final boolean isZipFile; + private String dirRoot = null; private final CopyOption[] copyOptions; /** @@ -38,15 +44,28 @@ public CopyVisitor(Path source, Path target, CopyOption... copyOptions) { } this.source = source; this.target = target; + this.isZipFile = target instanceof ZipPath; this.copyOptions = copyOptions; } @Override public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) throws IOException { - initTarget(); - // 将当前目录相对于源路径转换为相对于目标路径 - final Path targetDir = target.resolve(source.relativize(dir)); + final Path targetDir; + if (isZipFile) { + ZipPath zipPath = (ZipPath) target; + ZipFileSystem fileSystem = zipPath.getFileSystem(); + if (dirRoot == null) { + targetDir = fileSystem.getPath(dir.getFileName().toString()); + dirRoot = dir.getFileName().toString() + File.separator; + } else { + targetDir = fileSystem.getPath(dirRoot, StrUtil.subAfter(dir.toString(), dirRoot, false)); + } + } else { + initTarget(); + // 将当前目录相对于源路径转换为相对于目标路径 + targetDir = target.resolve(source.relativize(dir)); + } try { Files.copy(dir, targetDir, copyOptions); } catch (FileAlreadyExistsException e) { @@ -59,8 +78,17 @@ public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) @Override public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException { - initTarget(); - Files.copy(file, target.resolve(source.relativize(file)), copyOptions); + if (isZipFile) { + if (dirRoot == null) { + Files.copy(file, target, copyOptions); + } else { + ZipPath zipPath = (ZipPath) target; + Files.copy(file, zipPath.getFileSystem().getPath(dirRoot, StrUtil.subAfter(file.toString(), dirRoot, false)), copyOptions); + } + } else { + initTarget(); + Files.copy(file, target.resolve(source.relativize(file)), copyOptions); + } return FileVisitResult.CONTINUE; } From 121bcc159ea086bc0b8fff4752d3601fcbfa3d4a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=8E=8B=E5=AD=98=E9=9C=B2?= <1137738840@qq.com> Date: Mon, 18 Oct 2021 18:07:43 +0800 Subject: [PATCH 30/51] =?UTF-8?q?=E5=A2=9E=E5=8A=A0ContentType=E6=96=B0?= =?UTF-8?q?=E7=9A=84=E6=9E=84=E5=BB=BA=E6=96=B9=E6=B3=95=E7=9A=84=E5=8D=95?= =?UTF-8?q?=E5=85=83=E6=B5=8B=E8=AF=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/cn/hutool/http/ContentTypeTest.java | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) create mode 100644 hutool-http/src/test/java/cn/hutool/http/ContentTypeTest.java diff --git a/hutool-http/src/test/java/cn/hutool/http/ContentTypeTest.java b/hutool-http/src/test/java/cn/hutool/http/ContentTypeTest.java new file mode 100644 index 0000000000..1d03c44eb4 --- /dev/null +++ b/hutool-http/src/test/java/cn/hutool/http/ContentTypeTest.java @@ -0,0 +1,19 @@ +package cn.hutool.http; + +import cn.hutool.core.util.CharsetUtil; +import org.junit.Assert; +import org.junit.Test; + +/** + * ContentType 单元测试 + * + * + */ +public class ContentTypeTest { + + @Test + public void testBuild() { + String result = ContentType.build(ContentType.JSON, CharsetUtil.CHARSET_UTF_8); + Assert.assertEquals("application/json;charset=UTF-8", result); + } +} From 3e36d7fd2117ebbbb706215a9d0c00dc5eb4f42f Mon Sep 17 00:00:00 2001 From: Looly Date: Tue, 19 Oct 2021 00:27:10 +0800 Subject: [PATCH 31/51] fix bug --- CHANGELOG.md | 3 +- .../cn/hutool/cache/impl/AbstractCache.java | 132 +--------------- .../java/cn/hutool/cache/impl/FIFOCache.java | 2 +- .../java/cn/hutool/cache/impl/LFUCache.java | 2 +- .../java/cn/hutool/cache/impl/LRUCache.java | 2 +- .../cn/hutool/cache/impl/ReentrantCache.java | 136 +++++++++++++++++ .../cn/hutool/cache/impl/StampedCache.java | 141 ++++++++++++++++++ .../java/cn/hutool/cache/impl/TimedCache.java | 2 +- .../java/cn/hutool/cache/LRUCacheTest.java | 52 +++++++ 9 files changed, 337 insertions(+), 135 deletions(-) create mode 100644 hutool-cache/src/main/java/cn/hutool/cache/impl/ReentrantCache.java create mode 100644 hutool-cache/src/main/java/cn/hutool/cache/impl/StampedCache.java create mode 100644 hutool-cache/src/test/java/cn/hutool/cache/LRUCacheTest.java diff --git a/CHANGELOG.md b/CHANGELOG.md index baa866a4b1..8cde66ed3a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,7 +3,7 @@ ------------------------------------------------------------------------------------------------------------- -# 5.7.15 (2021-10-18) +# 5.7.15 (2021-10-19) ### 🐣新特性 * 【db 】 Db.quietSetAutoCommit增加判空(issue#I4D75B@Gitee) @@ -18,6 +18,7 @@ * 【poi 】 修复ExcelWriter多余调试信息导致的问题(issue#1884@Github) * 【poi 】 修复TemporalAccessorUtil.toInstant使用DateTimeFormatter导致问题(issue#1891@Github) * 【poi 】 修复sheet.getRow(y)为null导致的问题(issue#1893@Github) +* 【cache 】 修复LRUCache线程安全问题(issue#1895@Github) ------------------------------------------------------------------------------------------------------------- diff --git a/hutool-cache/src/main/java/cn/hutool/cache/impl/AbstractCache.java b/hutool-cache/src/main/java/cn/hutool/cache/impl/AbstractCache.java index 6999922ee5..6bb5496894 100644 --- a/hutool-cache/src/main/java/cn/hutool/cache/impl/AbstractCache.java +++ b/hutool-cache/src/main/java/cn/hutool/cache/impl/AbstractCache.java @@ -2,7 +2,6 @@ import cn.hutool.cache.Cache; import cn.hutool.cache.CacheListener; -import cn.hutool.core.collection.CopiedIter; import cn.hutool.core.lang.func.Func0; import java.util.Iterator; @@ -12,7 +11,6 @@ import java.util.concurrent.atomic.LongAdder; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; -import java.util.concurrent.locks.StampedLock; /** * 超时和限制大小的缓存的默认实现
@@ -31,11 +29,6 @@ public abstract class AbstractCache implements Cache { protected Map> cacheMap; - // 乐观锁,此处使用乐观锁解决读多写少的场景 - // get时乐观读,再检查是否修改,修改则转入悲观读重新读一遍,可以有效解决在写时阻塞大量读操作的情况。 - // see: https://www.cnblogs.com/jiagoushijuzi/p/13721319.html - protected final StampedLock lock = new StampedLock(); - /** * 写的时候每个key一把锁,降低锁的粒度 */ @@ -75,16 +68,6 @@ public void put(K key, V object) { put(key, object, timeout); } - @Override - public void put(K key, V object, long timeout) { - final long stamp = lock.writeLock(); - try { - putWithoutLock(key, object, timeout); - } finally { - lock.unlockWrite(stamp); - } - } - /** * 加入元素,无锁 * @@ -93,7 +76,7 @@ public void put(K key, V object, long timeout) { * @param timeout 超时时长 * @since 4.5.16 */ - private void putWithoutLock(K key, V object, long timeout) { + protected void putWithoutLock(K key, V object, long timeout) { CacheObj co = new CacheObj<>(key, object, timeout); if (timeout != 0) { existCustomTimeout = true; @@ -106,29 +89,6 @@ private void putWithoutLock(K key, V object, long timeout) { // ---------------------------------------------------------------- put end // ---------------------------------------------------------------- get start - @Override - public boolean containsKey(K key) { - final long stamp = lock.readLock(); - try { - // 不存在或已移除 - final CacheObj co = cacheMap.get(key); - if (co == null) { - return false; - } - - if (false == co.isExpired()) { - // 命中 - return true; - } - } finally { - lock.unlockRead(stamp); - } - - // 过期 - remove(key, true); - return false; - } - /** * @return 命中数 */ @@ -170,36 +130,6 @@ public V get(K key, boolean isUpdateLastAccess, Func0 supplier) { } return v; } - - @Override - public V get(K key, boolean isUpdateLastAccess) { - // 尝试读取缓存,使用乐观读锁 - long stamp = lock.tryOptimisticRead(); - CacheObj co = cacheMap.get(key); - if(false == lock.validate(stamp)){ - // 有写线程修改了此对象,悲观读 - stamp = lock.readLock(); - try { - co = cacheMap.get(key); - } finally { - lock.unlockRead(stamp); - } - } - - // 未命中 - if (null == co) { - missCount.increment(); - return null; - } else if (false == co.isExpired()) { - hitCount.increment(); - return co.get(isUpdateLastAccess); - } - - // 过期,既不算命中也不算非命中 - remove(key, true); - return null; - } - // ---------------------------------------------------------------- get end @Override @@ -207,21 +137,7 @@ public Iterator iterator() { CacheObjIterator copiedIterator = (CacheObjIterator) this.cacheObjIterator(); return new CacheValuesIterator<>(copiedIterator); } - - @Override - public Iterator> cacheObjIterator() { - CopiedIter> copiedIterator; - final long stamp = lock.readLock(); - try { - copiedIterator = CopiedIter.copyOf(this.cacheMap.values().iterator()); - } finally { - lock.unlockRead(stamp); - } - return new CacheObjIterator<>(copiedIterator); - } - // ---------------------------------------------------------------- prune start - /** * 清理实现
* 子类实现此方法时无需加锁 @@ -229,16 +145,6 @@ public Iterator> cacheObjIterator() { * @return 清理数 */ protected abstract int pruneCache(); - - @Override - public final int prune() { - final long stamp = lock.writeLock(); - try { - return pruneCache(); - } finally { - lock.unlockWrite(stamp); - } - } // ---------------------------------------------------------------- prune end // ---------------------------------------------------------------- common start @@ -270,21 +176,6 @@ public boolean isFull() { return (capacity > 0) && (cacheMap.size() >= capacity); } - @Override - public void remove(K key) { - remove(key, false); - } - - @Override - public void clear() { - final long stamp = lock.writeLock(); - try { - cacheMap.clear(); - } finally { - lock.unlockWrite(stamp); - } - } - @Override public int size() { return cacheMap.size(); @@ -338,25 +229,6 @@ protected void onRemove(K key, V cachedObject) { } } - /** - * 移除key对应的对象 - * - * @param key 键 - * @param withMissCount 是否计数丢失数 - */ - private void remove(K key, boolean withMissCount) { - final long stamp = lock.writeLock(); - CacheObj co; - try { - co = removeWithoutLock(key, withMissCount); - } finally { - lock.unlockWrite(stamp); - } - if (null != co) { - onRemove(co.key, co.obj); - } - } - /** * 移除key对应的对象,不加锁 * @@ -364,7 +236,7 @@ private void remove(K key, boolean withMissCount) { * @param withMissCount 是否计数丢失数 * @return 移除的对象,无返回null */ - private CacheObj removeWithoutLock(K key, boolean withMissCount) { + protected CacheObj removeWithoutLock(K key, boolean withMissCount) { final CacheObj co = cacheMap.remove(key); if (withMissCount) { // 在丢失计数有效的情况下,移除一般为get时的超时操作,此处应该丢失数+1 diff --git a/hutool-cache/src/main/java/cn/hutool/cache/impl/FIFOCache.java b/hutool-cache/src/main/java/cn/hutool/cache/impl/FIFOCache.java index c403ae10cd..558fed75f6 100644 --- a/hutool-cache/src/main/java/cn/hutool/cache/impl/FIFOCache.java +++ b/hutool-cache/src/main/java/cn/hutool/cache/impl/FIFOCache.java @@ -16,7 +16,7 @@ * @param 值类型 * @author Looly */ -public class FIFOCache extends AbstractCache { +public class FIFOCache extends StampedCache { private static final long serialVersionUID = 1L; /** diff --git a/hutool-cache/src/main/java/cn/hutool/cache/impl/LFUCache.java b/hutool-cache/src/main/java/cn/hutool/cache/impl/LFUCache.java index 32709ed6b9..4b1e2608a5 100644 --- a/hutool-cache/src/main/java/cn/hutool/cache/impl/LFUCache.java +++ b/hutool-cache/src/main/java/cn/hutool/cache/impl/LFUCache.java @@ -15,7 +15,7 @@ * @param 键类型 * @param 值类型 */ -public class LFUCache extends AbstractCache { +public class LFUCache extends StampedCache { private static final long serialVersionUID = 1L; /** diff --git a/hutool-cache/src/main/java/cn/hutool/cache/impl/LRUCache.java b/hutool-cache/src/main/java/cn/hutool/cache/impl/LRUCache.java index b385b57fea..29f83f02fe 100644 --- a/hutool-cache/src/main/java/cn/hutool/cache/impl/LRUCache.java +++ b/hutool-cache/src/main/java/cn/hutool/cache/impl/LRUCache.java @@ -16,7 +16,7 @@ * @param 键类型 * @param 值类型 */ -public class LRUCache extends AbstractCache { +public class LRUCache extends ReentrantCache { private static final long serialVersionUID = 1L; /** diff --git a/hutool-cache/src/main/java/cn/hutool/cache/impl/ReentrantCache.java b/hutool-cache/src/main/java/cn/hutool/cache/impl/ReentrantCache.java new file mode 100644 index 0000000000..180bc77853 --- /dev/null +++ b/hutool-cache/src/main/java/cn/hutool/cache/impl/ReentrantCache.java @@ -0,0 +1,136 @@ +package cn.hutool.cache.impl; + +import cn.hutool.core.collection.CopiedIter; + +import java.util.Iterator; +import java.util.concurrent.locks.ReentrantLock; + +/** + * 使用{@link ReentrantLock}保护的缓存,读写都使用悲观锁完成,主要避免某些Map无法使用读写锁的问题
+ * 例如使用了LinkedHashMap的缓存,由于get方法也会改变Map的结构,因此读写必须加互斥锁 + * + * @param 键类型 + * @param 值类型 + * @author looly + * @since 5.7.15 + */ +public abstract class ReentrantCache extends AbstractCache { + private static final long serialVersionUID = 1L; + + // 一些特殊缓存,例如使用了LinkedHashMap的缓存,由于get方法也会改变Map的结构,导致无法使用读写锁 + // 最优的解决方案是使用Guava的ConcurrentLinkedHashMap,此处使用简化的互斥锁 + protected final ReentrantLock lock = new ReentrantLock(); + + @Override + public void put(K key, V object, long timeout) { + lock.lock(); + try { + putWithoutLock(key, object, timeout); + } finally { + lock.unlock(); + } + } + + @Override + public boolean containsKey(K key) { + lock.lock(); + try { + // 不存在或已移除 + final CacheObj co = cacheMap.get(key); + if (co == null) { + return false; + } + + if (false == co.isExpired()) { + // 命中 + return true; + } + } finally { + lock.unlock(); + } + + // 过期 + remove(key, true); + return false; + } + + @Override + public V get(K key, boolean isUpdateLastAccess) { + CacheObj co; + lock.lock(); + try { + co = cacheMap.get(key); + } finally { + lock.unlock(); + } + + // 未命中 + if (null == co) { + missCount.increment(); + return null; + } else if (false == co.isExpired()) { + hitCount.increment(); + return co.get(isUpdateLastAccess); + } + + // 过期,既不算命中也不算非命中 + remove(key, true); + return null; + } + + @Override + public Iterator> cacheObjIterator() { + CopiedIter> copiedIterator; + lock.lock(); + try { + copiedIterator = CopiedIter.copyOf(this.cacheMap.values().iterator()); + } finally { + lock.unlock(); + } + return new CacheObjIterator<>(copiedIterator); + } + + @Override + public final int prune() { + lock.lock(); + try { + return pruneCache(); + } finally { + lock.unlock(); + } + } + + @Override + public void remove(K key) { + remove(key, false); + } + + @Override + public void clear() { + lock.lock(); + try { + cacheMap.clear(); + } finally { + lock.unlock(); + } + } + + /** + * 移除key对应的对象 + * + * @param key 键 + * @param withMissCount 是否计数丢失数 + */ + private void remove(K key, boolean withMissCount) { + lock.lock(); + CacheObj co; + try { + co = removeWithoutLock(key, withMissCount); + } finally { + lock.unlock(); + } + if (null != co) { + onRemove(co.key, co.obj); + } + } +} diff --git a/hutool-cache/src/main/java/cn/hutool/cache/impl/StampedCache.java b/hutool-cache/src/main/java/cn/hutool/cache/impl/StampedCache.java new file mode 100644 index 0000000000..79534f2bf2 --- /dev/null +++ b/hutool-cache/src/main/java/cn/hutool/cache/impl/StampedCache.java @@ -0,0 +1,141 @@ +package cn.hutool.cache.impl; + +import cn.hutool.core.collection.CopiedIter; + +import java.util.Iterator; +import java.util.concurrent.locks.StampedLock; + +/** + * 使用{@link StampedLock}保护的缓存,使用读写乐观锁 + * + * @param 键类型 + * @param 值类型 + * @author looly + * @since 5.7.15 + */ +public abstract class StampedCache extends AbstractCache{ + private static final long serialVersionUID = 1L; + + // 乐观锁,此处使用乐观锁解决读多写少的场景 + // get时乐观读,再检查是否修改,修改则转入悲观读重新读一遍,可以有效解决在写时阻塞大量读操作的情况。 + // see: https://www.cnblogs.com/jiagoushijuzi/p/13721319.html + protected final StampedLock lock = new StampedLock(); + + @Override + public void put(K key, V object, long timeout) { + final long stamp = lock.writeLock(); + try { + putWithoutLock(key, object, timeout); + } finally { + lock.unlockWrite(stamp); + } + } + + @Override + public boolean containsKey(K key) { + final long stamp = lock.readLock(); + try { + // 不存在或已移除 + final CacheObj co = cacheMap.get(key); + if (co == null) { + return false; + } + + if (false == co.isExpired()) { + // 命中 + return true; + } + } finally { + lock.unlockRead(stamp); + } + + // 过期 + remove(key, true); + return false; + } + + @Override + public V get(K key, boolean isUpdateLastAccess) { + // 尝试读取缓存,使用乐观读锁 + long stamp = lock.tryOptimisticRead(); + CacheObj co = cacheMap.get(key); + if(false == lock.validate(stamp)){ + // 有写线程修改了此对象,悲观读 + stamp = lock.readLock(); + try { + co = cacheMap.get(key); + } finally { + lock.unlockRead(stamp); + } + } + + // 未命中 + if (null == co) { + missCount.increment(); + return null; + } else if (false == co.isExpired()) { + hitCount.increment(); + return co.get(isUpdateLastAccess); + } + + // 过期,既不算命中也不算非命中 + remove(key, true); + return null; + } + + @Override + public Iterator> cacheObjIterator() { + CopiedIter> copiedIterator; + final long stamp = lock.readLock(); + try { + copiedIterator = CopiedIter.copyOf(this.cacheMap.values().iterator()); + } finally { + lock.unlockRead(stamp); + } + return new CacheObjIterator<>(copiedIterator); + } + + @Override + public final int prune() { + final long stamp = lock.writeLock(); + try { + return pruneCache(); + } finally { + lock.unlockWrite(stamp); + } + } + + @Override + public void remove(K key) { + remove(key, false); + } + + @Override + public void clear() { + final long stamp = lock.writeLock(); + try { + cacheMap.clear(); + } finally { + lock.unlockWrite(stamp); + } + } + + /** + * 移除key对应的对象 + * + * @param key 键 + * @param withMissCount 是否计数丢失数 + */ + private void remove(K key, boolean withMissCount) { + final long stamp = lock.writeLock(); + CacheObj co; + try { + co = removeWithoutLock(key, withMissCount); + } finally { + lock.unlockWrite(stamp); + } + if (null != co) { + onRemove(co.key, co.obj); + } + } +} diff --git a/hutool-cache/src/main/java/cn/hutool/cache/impl/TimedCache.java b/hutool-cache/src/main/java/cn/hutool/cache/impl/TimedCache.java index 5e094875d3..e03ede728e 100644 --- a/hutool-cache/src/main/java/cn/hutool/cache/impl/TimedCache.java +++ b/hutool-cache/src/main/java/cn/hutool/cache/impl/TimedCache.java @@ -16,7 +16,7 @@ * @param 键类型 * @param 值类型 */ -public class TimedCache extends AbstractCache { +public class TimedCache extends StampedCache { private static final long serialVersionUID = 1L; /** 正在执行的定时任务 */ diff --git a/hutool-cache/src/test/java/cn/hutool/cache/LRUCacheTest.java b/hutool-cache/src/test/java/cn/hutool/cache/LRUCacheTest.java new file mode 100644 index 0000000000..6ccc2db2f5 --- /dev/null +++ b/hutool-cache/src/test/java/cn/hutool/cache/LRUCacheTest.java @@ -0,0 +1,52 @@ +package cn.hutool.cache; + +import cn.hutool.cache.impl.LRUCache; +import org.junit.Assert; +import org.junit.Test; + +import java.util.concurrent.CountDownLatch; + +/** + * 见:https://github.com/dromara/hutool/issues/1895
+ * 并发问题测试,在5.7.15前,LRUCache存在并发问题,多线程get后,map结构变更,导致null的位置不确定, + * 并可能引起死锁。 + */ +public class LRUCacheTest { + + @Test + public void readWriteTest() throws InterruptedException { + LRUCache cache = CacheUtil.newLRUCache(10); + for (int i = 0; i < 10; i++) { + cache.put(i, i); + } + + CountDownLatch countDownLatch = new CountDownLatch(10); + // 10个线程分别读0-9 10000次 + for (int i = 0; i < 10; i++) { + int finalI = i; + new Thread(() -> { + for (int j = 0; j < 10000; j++) { + cache.get(finalI); + } + countDownLatch.countDown(); + }).start(); + } + // 等待读线程结束 + countDownLatch.await(); + // 按顺序读0-9 + StringBuilder sb1 = new StringBuilder(); + for (int i = 0; i < 10; i++) { + sb1.append(cache.get(i)); + } + Assert.assertEquals("0123456789", sb1.toString()); + + // 新加11,此时0最久未使用,应该淘汰0 + cache.put(11, 11); + + StringBuilder sb2 = new StringBuilder(); + for (int i = 0; i < 10; i++) { + sb2.append(cache.get(i)); + } + Assert.assertEquals("null123456789", sb2.toString()); + } +} From e258fc1eb9ff4a5c52c4508fee5404cebdd3a3a2 Mon Sep 17 00:00:00 2001 From: Looly Date: Tue, 19 Oct 2021 13:27:16 +0800 Subject: [PATCH 32/51] fix bug --- CHANGELOG.md | 1 + hutool-crypto/src/main/java/cn/hutool/crypto/KeyUtil.java | 4 ++-- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8cde66ed3a..af022c29ed 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -19,6 +19,7 @@ * 【poi 】 修复TemporalAccessorUtil.toInstant使用DateTimeFormatter导致问题(issue#1891@Github) * 【poi 】 修复sheet.getRow(y)为null导致的问题(issue#1893@Github) * 【cache 】 修复LRUCache线程安全问题(issue#1895@Github) +* 【crypto 】 修复KeyUtil异常信息参数丢失问题(issue#1902@Github) ------------------------------------------------------------------------------------------------------------- diff --git a/hutool-crypto/src/main/java/cn/hutool/crypto/KeyUtil.java b/hutool-crypto/src/main/java/cn/hutool/crypto/KeyUtil.java index b9df3e891c..90ef705408 100644 --- a/hutool-crypto/src/main/java/cn/hutool/crypto/KeyUtil.java +++ b/hutool-crypto/src/main/java/cn/hutool/crypto/KeyUtil.java @@ -180,7 +180,7 @@ public static SecretKey generateKey(String algorithm, byte[] key) { */ public static SecretKey generateDESKey(String algorithm, byte[] key) { if (StrUtil.isBlank(algorithm) || false == algorithm.startsWith("DES")) { - throw new CryptoException("Algorithm [{}] is not a DES algorithm!"); + throw new CryptoException("Algorithm [{}] is not a DES algorithm!", algorithm); } SecretKey secretKey; @@ -212,7 +212,7 @@ public static SecretKey generateDESKey(String algorithm, byte[] key) { */ public static SecretKey generatePBEKey(String algorithm, char[] key) { if (StrUtil.isBlank(algorithm) || false == algorithm.startsWith("PBE")) { - throw new CryptoException("Algorithm [{}] is not a PBE algorithm!"); + throw new CryptoException("Algorithm [{}] is not a PBE algorithm!", algorithm); } if (null == key) { From b2c5438692c5b56ae435e5c4a1858d603f1b1607 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=94=B3=E5=8A=AD=E6=98=8E?= <626546063@qq.com> Date: Wed, 20 Oct 2021 01:56:24 +0000 Subject: [PATCH 33/51] use CopyVisitor --- .../java/cn/hutool/core/util/ZipUtil.java | 51 ++++++------------- 1 file changed, 15 insertions(+), 36 deletions(-) diff --git a/hutool-core/src/main/java/cn/hutool/core/util/ZipUtil.java b/hutool-core/src/main/java/cn/hutool/core/util/ZipUtil.java index 064642772e..dc8d552308 100644 --- a/hutool-core/src/main/java/cn/hutool/core/util/ZipUtil.java +++ b/hutool-core/src/main/java/cn/hutool/core/util/ZipUtil.java @@ -10,13 +10,24 @@ import cn.hutool.core.io.IORuntimeException; import cn.hutool.core.io.IoUtil; import cn.hutool.core.io.file.FileSystemUtil; +import cn.hutool.core.io.file.visitor.CopyVisitor; import cn.hutool.core.io.resource.Resource; -import java.io.*; +import java.io.BufferedInputStream; +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.File; +import java.io.FileFilter; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; import java.nio.charset.Charset; +import java.nio.file.FileAlreadyExistsException; import java.nio.file.FileSystem; -import java.nio.file.*; -import java.nio.file.attribute.BasicFileAttributes; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.nio.file.StandardCopyOption; import java.util.ArrayList; import java.util.Collections; import java.util.List; @@ -88,39 +99,7 @@ public static void addFile(String zipFilePathStr, String appendFilePathStr) thro if (!Files.isDirectory(appendFilePath)) { Files.copy(appendFilePath, dest, StandardCopyOption.COPY_ATTRIBUTES); } else { - Files.walkFileTree(appendFilePath, new SimpleFileVisitor() { - /** - * 用于保证文件夹拷贝后的效果跟常见压缩软件的效果相同 - */ - private String dirRoot = null; - - @Override - public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException { - final Path dest; - if (dirRoot != null) { - dest = zipFileSystem.getPath(root.toString(), dirRoot, File.separator, StrUtil.subAfter(file.toString(), dirRoot, false)); - } else { - dest = zipFileSystem.getPath(root.toString(), file.toString()); - } - Files.copy(file, dest, StandardCopyOption.COPY_ATTRIBUTES); - return FileVisitResult.CONTINUE; - } - - @Override - public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) throws IOException { - final Path dirToCreate; - if (dirRoot == null) { - dirToCreate = zipFileSystem.getPath(root.toString(), dir.getFileName().toString()); - dirRoot = dir.getFileName().toString(); - } else { - dirToCreate = zipFileSystem.getPath(root.toString(), dirRoot, File.separator, StrUtil.subAfter(dir.toString(), dirRoot, false)); - } - if (Files.notExists(dirToCreate)) { - Files.createDirectories(dirToCreate); - } - return FileVisitResult.CONTINUE; - } - }); + Files.walkFileTree(appendFilePath, new CopyVisitor(appendFilePath, zipFileSystem.getPath(zipFilePathStr))); } } catch (FileAlreadyExistsException ignored) { // 文件已存在, 跳过 From fd1f330166cf6196a3407a6d980b8422092d2d88 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=94=B3=E5=8A=AD=E6=98=8E?= <42922512+shenshaoming@users.noreply.github.com> Date: Wed, 20 Oct 2021 10:43:51 +0800 Subject: [PATCH 34/51] add test --- .../java/cn/hutool/core/util/ZipUtilTest.java | 47 ++++++++++++++++++ .../src/test/resources/test-zip/addFile.txt | 2 + .../test/resources/test-zip/test-add/test.txt | 0 .../src/test/resources/test-zip/test.zip | Bin 0 -> 150 bytes 4 files changed, 49 insertions(+) create mode 100644 hutool-core/src/test/resources/test-zip/addFile.txt create mode 100644 hutool-core/src/test/resources/test-zip/test-add/test.txt create mode 100644 hutool-core/src/test/resources/test-zip/test.zip diff --git a/hutool-core/src/test/java/cn/hutool/core/util/ZipUtilTest.java b/hutool-core/src/test/java/cn/hutool/core/util/ZipUtilTest.java index 0246f66883..acd389ab15 100644 --- a/hutool-core/src/test/java/cn/hutool/core/util/ZipUtilTest.java +++ b/hutool-core/src/test/java/cn/hutool/core/util/ZipUtilTest.java @@ -1,5 +1,6 @@ package cn.hutool.core.util; +import cn.hutool.core.compress.ZipReader; import cn.hutool.core.io.FileUtil; import cn.hutool.core.io.IORuntimeException; import cn.hutool.core.lang.Console; @@ -12,6 +13,8 @@ import java.io.IOException; import java.io.OutputStream; import java.nio.charset.Charset; +import java.util.ArrayList; +import java.util.List; /** * {@link ZipUtil}单元测试 @@ -20,6 +23,50 @@ */ public class ZipUtilTest { + @Test + public void addFileTest() throws IOException { + File appendFile = FileUtil.file("test-zip/addFile.txt"); + File zipFile = FileUtil.file("test-zip/test.zip"); + + // 用于测试完成后将被测试文件恢复 + File tempZipFile = FileUtil.createTempFile(FileUtil.file("test-zip")); + tempZipFile.deleteOnExit(); + FileUtil.copy(zipFile, tempZipFile, true); + + // test file add + List beforeNames = zipEntryNames(zipFile); + ZipUtil.addFile(zipFile.getAbsolutePath(), appendFile.getAbsolutePath()); + List afterNames = zipEntryNames(zipFile); + Assert.assertTrue(afterNames.containsAll(beforeNames)); + Assert.assertTrue(afterNames.contains(appendFile.getName())); + + // test dir add + beforeNames = afterNames; + File addDirFile = FileUtil.file("test-zip/test-add"); + ZipUtil.addFile(zipFile.getAbsolutePath(), addDirFile.getAbsolutePath()); + afterNames = zipEntryNames(zipFile); + + Assert.assertTrue(afterNames.containsAll(beforeNames)); + Assert.assertTrue(afterNames.contains(appendFile.getName())); + + // rollback + FileUtil.copy(tempZipFile, zipFile, true); + Assert.assertTrue(String.format("delete temp file %s failed", tempZipFile.getCanonicalPath()), tempZipFile.delete()); + } + + /** + * 获取zip文件中所有一级文件/文件夹的name + * + * @param zipFile 待测试的zip文件 + * @return zip文件中一级目录下的所有文件/文件夹名 + */ + private List zipEntryNames(File zipFile) { + List fileNames = new ArrayList<>(); + ZipReader reader = ZipReader.of(zipFile, CharsetUtil.CHARSET_UTF_8); + reader.read(zipEntry -> fileNames.add(zipEntry.getName())); + reader.close(); + return fileNames; + } @Test @Ignore diff --git a/hutool-core/src/test/resources/test-zip/addFile.txt b/hutool-core/src/test/resources/test-zip/addFile.txt new file mode 100644 index 0000000000..8d1c2fee6c --- /dev/null +++ b/hutool-core/src/test/resources/test-zip/addFile.txt @@ -0,0 +1,2 @@ +this file will be used to add into the test.zip +before the add action, the test.zip won't have this file. diff --git a/hutool-core/src/test/resources/test-zip/test-add/test.txt b/hutool-core/src/test/resources/test-zip/test-add/test.txt new file mode 100644 index 0000000000..e69de29bb2 diff --git a/hutool-core/src/test/resources/test-zip/test.zip b/hutool-core/src/test/resources/test-zip/test.zip new file mode 100644 index 0000000000000000000000000000000000000000..86126d555dc839601f5165c532e08c4260fa115b GIT binary patch literal 150 zcmWIWW@h1H0D(~dkYF$aN^k(_lGNf7y^@NO0B=Sndj?$URG>N(pfpSu7Xw6ukwJo? dw0#d_*wO2Z=yZTLD;r1~BM?RbX*Cds0RRL47l{A> literal 0 HcmV?d00001 From b93796931379f4f2fa74ea54e0d02c387ef9396a Mon Sep 17 00:00:00 2001 From: Looly Date: Wed, 20 Oct 2021 12:34:26 +0800 Subject: [PATCH 35/51] fix bug --- CHANGELOG.md | 3 +- .../cn/hutool/core/text/CharSequenceUtil.java | 10 ++--- .../java/cn/hutool/core/text/StrSplitter.java | 39 ++++++++++--------- .../cn/hutool/core/text/split/SplitIter.java | 1 - .../java/cn/hutool/core/util/StrUtilTest.java | 10 +++++ 5 files changed, 37 insertions(+), 26 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index af022c29ed..61d3f25734 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,7 +3,7 @@ ------------------------------------------------------------------------------------------------------------- -# 5.7.15 (2021-10-19) +# 5.7.15 (2021-10-20) ### 🐣新特性 * 【db 】 Db.quietSetAutoCommit增加判空(issue#I4D75B@Gitee) @@ -20,6 +20,7 @@ * 【poi 】 修复sheet.getRow(y)为null导致的问题(issue#1893@Github) * 【cache 】 修复LRUCache线程安全问题(issue#1895@Github) * 【crypto 】 修复KeyUtil异常信息参数丢失问题(issue#1902@Github) +* 【core 】 修复StrUtil.split和splittoArray不一致问题(issue#I4ELU5@Github) ------------------------------------------------------------------------------------------------------------- diff --git a/hutool-core/src/main/java/cn/hutool/core/text/CharSequenceUtil.java b/hutool-core/src/main/java/cn/hutool/core/text/CharSequenceUtil.java index 8c39749156..1a41d72822 100644 --- a/hutool-core/src/main/java/cn/hutool/core/text/CharSequenceUtil.java +++ b/hutool-core/src/main/java/cn/hutool/core/text/CharSequenceUtil.java @@ -1729,16 +1729,14 @@ public static String[] splitToArray(CharSequence str, char separator) { /** * 切分字符串 * - * @param str 被切分的字符串 + * @param text 被切分的字符串 * @param separator 分隔符字符 * @param limit 限制分片数 * @return 切分后的数组 */ - public static String[] splitToArray(CharSequence str, char separator, int limit) { - if (null == str) { - return new String[]{}; - } - return StrSplitter.splitToArray(str.toString(), separator, limit, false, false); + public static String[] splitToArray(CharSequence text, char separator, int limit) { + Assert.notNull(text, "Text must be not null!"); + return StrSplitter.splitToArray(text.toString(), separator, limit, false, false); } /** diff --git a/hutool-core/src/main/java/cn/hutool/core/text/StrSplitter.java b/hutool-core/src/main/java/cn/hutool/core/text/StrSplitter.java index ec3d4aa45f..d9333e61ca 100644 --- a/hutool-core/src/main/java/cn/hutool/core/text/StrSplitter.java +++ b/hutool-core/src/main/java/cn/hutool/core/text/StrSplitter.java @@ -1,5 +1,6 @@ package cn.hutool.core.text; +import cn.hutool.core.lang.Assert; import cn.hutool.core.lang.PatternPool; import cn.hutool.core.text.finder.CharFinder; import cn.hutool.core.text.finder.CharMatcherFinder; @@ -146,7 +147,7 @@ public static List split(CharSequence str, char separator, int limit, boo /** * 切分字符串,忽略大小写 * - * @param str 被切分的字符串 + * @param text 被切分的字符串 * @param separator 分隔符字符 * @param limit 限制分片数,-1不限制 * @param isTrim 是否去除切分字符串后每个元素两边的空格 @@ -154,8 +155,8 @@ public static List split(CharSequence str, char separator, int limit, boo * @return 切分后的集合 * @since 3.2.1 */ - public static List splitIgnoreCase(CharSequence str, char separator, int limit, boolean isTrim, boolean ignoreEmpty) { - return split(str, separator, limit, isTrim, ignoreEmpty, true); + public static List splitIgnoreCase(CharSequence text, char separator, int limit, boolean isTrim, boolean ignoreEmpty) { + return split(text, separator, limit, isTrim, ignoreEmpty, true); } /** @@ -177,7 +178,7 @@ public static List split(CharSequence text, char separator, int limit, b /** * 切分字符串 * - * @param 切分后的元素类型 + * @param 切分后的元素类型 * @param text 被切分的字符串 * @param separator 分隔符字符 * @param limit 限制分片数,-1不限制 @@ -204,7 +205,7 @@ public static List split(CharSequence text, char separator, int limit, bo * @return 切分后的集合 * @since 3.0.8 */ - public static String[] splitToArray(String str, char separator, int limit, boolean isTrim, boolean ignoreEmpty) { + public static String[] splitToArray(CharSequence str, char separator, int limit, boolean isTrim, boolean ignoreEmpty) { return toArray(split(str, separator, limit, isTrim, ignoreEmpty)); } @@ -220,7 +221,7 @@ public static String[] splitToArray(String str, char separator, int limit, boole * @return 切分后的集合 * @since 3.0.8 */ - public static List split(String str, String separator, boolean isTrim, boolean ignoreEmpty) { + public static List split(CharSequence str, String separator, boolean isTrim, boolean ignoreEmpty) { return split(str, separator, -1, isTrim, ignoreEmpty, false); } @@ -233,7 +234,7 @@ public static List split(String str, String separator, boolean isTrim, b * @return 切分后的集合 * @since 3.2.1 */ - public static List splitTrim(String str, String separator, boolean ignoreEmpty) { + public static List splitTrim(CharSequence str, String separator, boolean ignoreEmpty) { return split(str, separator, true, ignoreEmpty); } @@ -248,7 +249,7 @@ public static List splitTrim(String str, String separator, boolean ignor * @return 切分后的集合 * @since 3.0.8 */ - public static List split(String str, String separator, int limit, boolean isTrim, boolean ignoreEmpty) { + public static List split(CharSequence str, String separator, int limit, boolean isTrim, boolean ignoreEmpty) { return split(str, separator, limit, isTrim, ignoreEmpty, false); } @@ -262,7 +263,7 @@ public static List split(String str, String separator, int limit, boolea * @return 切分后的集合 * @since 3.2.1 */ - public static List splitTrim(String str, String separator, int limit, boolean ignoreEmpty) { + public static List splitTrim(CharSequence str, String separator, int limit, boolean ignoreEmpty) { return split(str, separator, limit, true, ignoreEmpty); } @@ -277,7 +278,7 @@ public static List splitTrim(String str, String separator, int limit, bo * @return 切分后的集合 * @since 3.2.1 */ - public static List splitIgnoreCase(String str, String separator, int limit, boolean isTrim, boolean ignoreEmpty) { + public static List splitIgnoreCase(CharSequence str, String separator, int limit, boolean isTrim, boolean ignoreEmpty) { return split(str, separator, limit, isTrim, ignoreEmpty, true); } @@ -291,7 +292,7 @@ public static List splitIgnoreCase(String str, String separator, int lim * @return 切分后的集合 * @since 3.2.1 */ - public static List splitTrimIgnoreCase(String str, String separator, int limit, boolean ignoreEmpty) { + public static List splitTrimIgnoreCase(CharSequence str, String separator, int limit, boolean ignoreEmpty) { return split(str, separator, limit, true, ignoreEmpty, true); } @@ -307,7 +308,7 @@ public static List splitTrimIgnoreCase(String str, String separator, int * @return 切分后的集合 * @since 3.2.1 */ - public static List split(String text, String separator, int limit, boolean isTrim, boolean ignoreEmpty, boolean ignoreCase) { + public static List split(CharSequence text, String separator, int limit, boolean isTrim, boolean ignoreEmpty, boolean ignoreCase) { final SplitIter splitIter = new SplitIter(text, new StrFinder(separator, ignoreCase), limit, ignoreEmpty); return splitIter.toList(isTrim); } @@ -323,7 +324,7 @@ public static List split(String text, String separator, int limit, boole * @return 切分后的集合 * @since 3.0.8 */ - public static String[] splitToArray(String str, String separator, int limit, boolean isTrim, boolean ignoreEmpty) { + public static String[] splitToArray(CharSequence str, String separator, int limit, boolean isTrim, boolean ignoreEmpty) { return toArray(split(str, separator, limit, isTrim, ignoreEmpty)); } @@ -338,7 +339,8 @@ public static String[] splitToArray(String str, String separator, int limit, boo * @return 切分后的集合 * @since 3.0.8 */ - public static List split(String text, int limit) { + public static List split(CharSequence text, int limit) { + if (StrUtil.isEmpty(text)) { return new ArrayList<>(0); } @@ -362,7 +364,7 @@ public static String[] splitToArray(String str, int limit) { /** * 通过正则切分字符串 * - * @param str 字符串 + * @param text 字符串 * @param separatorRegex 分隔符正则 * @param limit 限制分片数 * @param isTrim 是否去除切分字符串后每个元素两边的空格 @@ -370,9 +372,9 @@ public static String[] splitToArray(String str, int limit) { * @return 切分后的集合 * @since 3.0.8 */ - public static List splitByRegex(String str, String separatorRegex, int limit, boolean isTrim, boolean ignoreEmpty) { + public static List splitByRegex(String text, String separatorRegex, int limit, boolean isTrim, boolean ignoreEmpty) { final Pattern pattern = PatternPool.get(separatorRegex); - return split(str, pattern, limit, isTrim, ignoreEmpty); + return split(text, pattern, limit, isTrim, ignoreEmpty); } /** @@ -387,7 +389,8 @@ public static List splitByRegex(String str, String separatorRegex, int l * @since 3.0.8 */ public static List split(String text, Pattern separatorPattern, int limit, boolean isTrim, boolean ignoreEmpty) { - if (StrUtil.isEmpty(text)) { + Assert.notNull(text, "Text must be not null!"); + if (text.length() < 1) { return new ArrayList<>(0); } final SplitIter splitIter = new SplitIter(text, new PatternFinder(separatorPattern), limit, ignoreEmpty); diff --git a/hutool-core/src/main/java/cn/hutool/core/text/split/SplitIter.java b/hutool-core/src/main/java/cn/hutool/core/text/split/SplitIter.java index c6d9776969..847a23ce32 100644 --- a/hutool-core/src/main/java/cn/hutool/core/text/split/SplitIter.java +++ b/hutool-core/src/main/java/cn/hutool/core/text/split/SplitIter.java @@ -53,7 +53,6 @@ public SplitIter(CharSequence text, TextFinder separatorFinder, int limit, boole @Override protected String computeNext() { - Assert.notNull(this.text, "Text to find must be not null!"); // 达到数量上限或末尾,结束 if (count >= limit || offset > text.length()) { return null; 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 73d8e6e7f5..b0aca478c9 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 @@ -81,6 +81,16 @@ public void splitTest2() { Assert.assertEquals("", split.get(2)); } + @Test(expected = IllegalArgumentException.class) + public void splitNullTest() { + StrUtil.split(null, '.'); + } + + @Test(expected = IllegalArgumentException.class) + public void splitToArrayNullTest() { + StrUtil.splitToArray(null, '.'); + } + @Test public void splitToLongTest() { String str = "1,2,3,4, 5"; From 96356cff62e5aff1f59a84d1c168f10fe47477aa Mon Sep 17 00:00:00 2001 From: Looly Date: Wed, 20 Oct 2021 22:58:36 +0800 Subject: [PATCH 36/51] support scope=import --- CHANGELOG.md | 3 +- hutool-bom/pom.xml | 119 +++++++++++++++++++++++++++++++++++++-------- 2 files changed, 102 insertions(+), 20 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 61d3f25734..65a3e85e45 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,7 +12,8 @@ * 【core 】 ReUtil增加命名分组重载(pr#439@Gitee) * 【json 】 toString和writer增加Filter(issue#I4DQNQ@Gitee) * 【core 】 ContentType增加build重载(pr#1898@Github) -* +* 【bom 】 支持scope=import方式引入(issue#1561@Github) + ### 🐞Bug修复 * 【core 】 修复CollUtil.isEqualList两个null返回错误问题(issue#1885@Github) * 【poi 】 修复ExcelWriter多余调试信息导致的问题(issue#1884@Github) diff --git a/hutool-bom/pom.xml b/hutool-bom/pom.xml index 5d56cbcc3a..5966acbff8 100644 --- a/hutool-bom/pom.xml +++ b/hutool-bom/pom.xml @@ -17,101 +17,182 @@ 提供丰富的Java工具方法,此模块为Hutool所有模块汇总,最终形式为拆分开的多个jar包,可以通过exclude方式排除不需要的模块 https://github.com/looly/hutool + + + + cn.hutool + hutool-core + ${project.parent.version} + + + cn.hutool + hutool-aop + ${project.parent.version} + + + cn.hutool + hutool-bloomFilter + ${project.parent.version} + + + cn.hutool + hutool-cache + ${project.parent.version} + + + cn.hutool + hutool-crypto + ${project.parent.version} + + + cn.hutool + hutool-db + ${project.parent.version} + + + cn.hutool + hutool-dfa + ${project.parent.version} + + + cn.hutool + hutool-extra + ${project.parent.version} + + + cn.hutool + hutool-http + ${project.parent.version} + + + cn.hutool + hutool-log + ${project.parent.version} + + + cn.hutool + hutool-script + ${project.parent.version} + + + cn.hutool + hutool-setting + ${project.parent.version} + + + cn.hutool + hutool-system + ${project.parent.version} + + + cn.hutool + hutool-cron + ${project.parent.version} + + + cn.hutool + hutool-json + ${project.parent.version} + + + cn.hutool + hutool-poi + ${project.parent.version} + + + cn.hutool + hutool-captcha + ${project.parent.version} + + + cn.hutool + hutool-socket + ${project.parent.version} + + + cn.hutool + hutool-jwt + ${project.parent.version} + + + + cn.hutool hutool-core - ${project.parent.version} cn.hutool hutool-aop - ${project.parent.version} cn.hutool hutool-bloomFilter - ${project.parent.version} cn.hutool hutool-cache - ${project.parent.version} cn.hutool hutool-crypto - ${project.parent.version} cn.hutool hutool-db - ${project.parent.version} cn.hutool hutool-dfa - ${project.parent.version} cn.hutool hutool-extra - ${project.parent.version} cn.hutool hutool-http - ${project.parent.version} cn.hutool hutool-log - ${project.parent.version} cn.hutool hutool-script - ${project.parent.version} cn.hutool hutool-setting - ${project.parent.version} cn.hutool hutool-system - ${project.parent.version} cn.hutool hutool-cron - ${project.parent.version} cn.hutool hutool-json - ${project.parent.version} cn.hutool hutool-poi - ${project.parent.version} cn.hutool hutool-captcha - ${project.parent.version} cn.hutool hutool-socket - ${project.parent.version} cn.hutool hutool-jwt - ${project.parent.version} From d690f4a2c4e8905096c15b91ba5ddcdc8a82c3af Mon Sep 17 00:00:00 2001 From: Looly Date: Wed, 20 Oct 2021 23:56:39 +0800 Subject: [PATCH 37/51] add methods --- .../cn/hutool/bloomfilter/BloomFilter.java | 6 ++--- .../main/java/cn/hutool/core/io/IoUtil.java | 2 +- .../cn/hutool/core/lang/hash/CityHash.java | 6 ++--- .../java/cn/hutool/core/lang/hash/Hash.java | 19 +++++++++++++++ .../cn/hutool/core/lang/hash/Hash128.java | 10 ++++++-- .../java/cn/hutool/core/lang/hash/Hash32.java | 9 ++++++-- .../java/cn/hutool/core/lang/hash/Hash64.java | 9 ++++++-- .../cn/hutool/core/lang/hash/Number128.java | 23 ++++++++++++++++++- 8 files changed, 70 insertions(+), 14 deletions(-) create mode 100644 hutool-core/src/main/java/cn/hutool/core/lang/hash/Hash.java diff --git a/hutool-bloomFilter/src/main/java/cn/hutool/bloomfilter/BloomFilter.java b/hutool-bloomFilter/src/main/java/cn/hutool/bloomfilter/BloomFilter.java index e7c9cc9fc4..9ab8278b37 100644 --- a/hutool-bloomFilter/src/main/java/cn/hutool/bloomfilter/BloomFilter.java +++ b/hutool-bloomFilter/src/main/java/cn/hutool/bloomfilter/BloomFilter.java @@ -20,10 +20,10 @@ public interface BloomFilter extends Serializable{ /** * 在boolean的bitMap中增加一个字符串
- * 如果存在就返回false .如果不存在.先增加这个字符串.再返回true + * 如果存在就返回{@code false} .如果不存在.先增加这个字符串.再返回{@code true} * * @param str 字符串 - * @return 是否加入成功,如果存在就返回false .如果不存在返回true + * @return 是否加入成功,如果存在就返回{@code false} .如果不存在返回{@code true} */ boolean add(String str); -} \ No newline at end of file +} diff --git a/hutool-core/src/main/java/cn/hutool/core/io/IoUtil.java b/hutool-core/src/main/java/cn/hutool/core/io/IoUtil.java index 3479c17b83..02699d4a47 100644 --- a/hutool-core/src/main/java/cn/hutool/core/io/IoUtil.java +++ b/hutool-core/src/main/java/cn/hutool/core/io/IoUtil.java @@ -684,7 +684,7 @@ public static > T readLines(InputStream in, Charset * @return 内容 * @throws IORuntimeException IO异常 */ - public static > T readLines(Reader reader, final T collection) throws IORuntimeException { + public static > T readLines(Reader reader, T collection) throws IORuntimeException { readLines(reader, (LineHandler) collection::add); return collection; } diff --git a/hutool-core/src/main/java/cn/hutool/core/lang/hash/CityHash.java b/hutool-core/src/main/java/cn/hutool/core/lang/hash/CityHash.java index 61c9f9ba6b..aad2da0568 100644 --- a/hutool-core/src/main/java/cn/hutool/core/lang/hash/CityHash.java +++ b/hutool-core/src/main/java/cn/hutool/core/lang/hash/CityHash.java @@ -318,7 +318,7 @@ private static int hash32Len13to24(byte[] byteArray) { private static long hashLen0to16(byte[] byteArray) { int len = byteArray.length; if (len >= 8) { - long mul = k2 + len * 2; + long mul = k2 + len * 2L; long a = fetch64(byteArray, 0) + k2; long b = fetch64(byteArray, len - 8); long c = rotate(b, 37) * mul + a; @@ -344,7 +344,7 @@ private static long hashLen0to16(byte[] byteArray) { // This probably works well for 16-byte strings as well, but it may be overkill in that case. private static long hashLen17to32(byte[] byteArray) { int len = byteArray.length; - long mul = k2 + len * 2; + long mul = k2 + len * 2L; long a = fetch64(byteArray, 0) * k1; long b = fetch64(byteArray, 8); long c = fetch64(byteArray, len - 8) * mul; @@ -355,7 +355,7 @@ private static long hashLen17to32(byte[] byteArray) { private static long hashLen33to64(byte[] byteArray) { int len = byteArray.length; - long mul = k2 + len * 2; + long mul = k2 + len * 2L; long a = fetch64(byteArray, 0) * k2; long b = fetch64(byteArray, 8); long c = fetch64(byteArray, len - 24); diff --git a/hutool-core/src/main/java/cn/hutool/core/lang/hash/Hash.java b/hutool-core/src/main/java/cn/hutool/core/lang/hash/Hash.java new file mode 100644 index 0000000000..8ad0a9e356 --- /dev/null +++ b/hutool-core/src/main/java/cn/hutool/core/lang/hash/Hash.java @@ -0,0 +1,19 @@ +package cn.hutool.core.lang.hash; + +/** + * Hash计算接口 + * + * @param 被计算hash的对象类型 + * @author looly + * @since 5.7.15 + */ +@FunctionalInterface +public interface Hash { + /** + * 计算Hash值 + * + * @param t 对象 + * @return hash + */ + Number hash(T t); +} diff --git a/hutool-core/src/main/java/cn/hutool/core/lang/hash/Hash128.java b/hutool-core/src/main/java/cn/hutool/core/lang/hash/Hash128.java index af5dfc4965..b16b4c9cee 100644 --- a/hutool-core/src/main/java/cn/hutool/core/lang/hash/Hash128.java +++ b/hutool-core/src/main/java/cn/hutool/core/lang/hash/Hash128.java @@ -8,7 +8,8 @@ * @since 5.2.5 */ @FunctionalInterface -public interface Hash128 { +public interface Hash128 extends Hash{ + /** * 计算Hash值 * @@ -16,4 +17,9 @@ public interface Hash128 { * @return hash */ Number128 hash128(T t); -} \ No newline at end of file + + @Override + default Number hash(T t){ + return hash128(t); + } +} diff --git a/hutool-core/src/main/java/cn/hutool/core/lang/hash/Hash32.java b/hutool-core/src/main/java/cn/hutool/core/lang/hash/Hash32.java index b0aaa8cedf..7a90166971 100644 --- a/hutool-core/src/main/java/cn/hutool/core/lang/hash/Hash32.java +++ b/hutool-core/src/main/java/cn/hutool/core/lang/hash/Hash32.java @@ -8,7 +8,7 @@ * @since 5.2.5 */ @FunctionalInterface -public interface Hash32 { +public interface Hash32 extends Hash{ /** * 计算Hash值 * @@ -16,4 +16,9 @@ public interface Hash32 { * @return hash */ int hash32(T t); -} \ No newline at end of file + + @Override + default Number hash(T t){ + return hash32(t); + } +} diff --git a/hutool-core/src/main/java/cn/hutool/core/lang/hash/Hash64.java b/hutool-core/src/main/java/cn/hutool/core/lang/hash/Hash64.java index 9feac2969e..61a50e4bd9 100644 --- a/hutool-core/src/main/java/cn/hutool/core/lang/hash/Hash64.java +++ b/hutool-core/src/main/java/cn/hutool/core/lang/hash/Hash64.java @@ -8,7 +8,7 @@ * @since 5.2.5 */ @FunctionalInterface -public interface Hash64 { +public interface Hash64 extends Hash{ /** * 计算Hash值 * @@ -16,4 +16,9 @@ public interface Hash64 { * @return hash */ long hash64(T t); -} \ No newline at end of file + + @Override + default Number hash(T t){ + return hash64(t); + } +} diff --git a/hutool-core/src/main/java/cn/hutool/core/lang/hash/Number128.java b/hutool-core/src/main/java/cn/hutool/core/lang/hash/Number128.java index 9af5bad4d1..4f63010955 100644 --- a/hutool-core/src/main/java/cn/hutool/core/lang/hash/Number128.java +++ b/hutool-core/src/main/java/cn/hutool/core/lang/hash/Number128.java @@ -6,7 +6,8 @@ * @author hexiufeng * @since 5.2.5 */ -public class Number128 { +public class Number128 extends Number{ + private static final long serialVersionUID = 1L; private long lowValue; private long highValue; @@ -41,4 +42,24 @@ public void setHighValue(long hiValue) { public long[] getLongArray() { return new long[]{lowValue, highValue}; } + + @Override + public int intValue() { + return (int) longValue(); + } + + @Override + public long longValue() { + return this.lowValue; + } + + @Override + public float floatValue() { + return longValue(); + } + + @Override + public double doubleValue() { + return longValue(); + } } From b66ed3d7c8d42259042b5d507b28d27b46747bf1 Mon Sep 17 00:00:00 2001 From: Looly Date: Wed, 20 Oct 2021 23:57:14 +0800 Subject: [PATCH 38/51] add methods --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 65a3e85e45..55fc99699a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,6 +13,7 @@ * 【json 】 toString和writer增加Filter(issue#I4DQNQ@Gitee) * 【core 】 ContentType增加build重载(pr#1898@Github) * 【bom 】 支持scope=import方式引入(issue#1561@Github) +* 【core 】 新增Hash接口,HashXXX继承此接口 ### 🐞Bug修复 * 【core 】 修复CollUtil.isEqualList两个null返回错误问题(issue#1885@Github) From 25118070a3210e08665f4e1bed5195190743e469 Mon Sep 17 00:00:00 2001 From: Looly Date: Thu, 21 Oct 2021 02:52:14 +0800 Subject: [PATCH 39/51] add zip append --- CHANGELOG.md | 3 +- .../hutool/core/compress/ZipCopyVisitor.java | 84 +++++++++++++++++++ .../java/cn/hutool/core/io/file/PathUtil.java | 14 ++++ .../core/io/file/visitor/CopyVisitor.java | 83 +++++++++--------- .../java/cn/hutool/core/util/ZipUtil.java | 56 +++++++------ .../java/cn/hutool/core/util/ZipUtilTest.java | 20 +++-- .../test/resources/test-zip/test-add/test.txt | 1 + 7 files changed, 184 insertions(+), 77 deletions(-) create mode 100644 hutool-core/src/main/java/cn/hutool/core/compress/ZipCopyVisitor.java diff --git a/CHANGELOG.md b/CHANGELOG.md index 55fc99699a..688101d0d5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,7 +3,7 @@ ------------------------------------------------------------------------------------------------------------- -# 5.7.15 (2021-10-20) +# 5.7.15 (2021-10-21) ### 🐣新特性 * 【db 】 Db.quietSetAutoCommit增加判空(issue#I4D75B@Gitee) @@ -14,6 +14,7 @@ * 【core 】 ContentType增加build重载(pr#1898@Github) * 【bom 】 支持scope=import方式引入(issue#1561@Github) * 【core 】 新增Hash接口,HashXXX继承此接口 +* 【core 】 ZipUtil增加append方法(pr#441@Gitee) ### 🐞Bug修复 * 【core 】 修复CollUtil.isEqualList两个null返回错误问题(issue#1885@Github) diff --git a/hutool-core/src/main/java/cn/hutool/core/compress/ZipCopyVisitor.java b/hutool-core/src/main/java/cn/hutool/core/compress/ZipCopyVisitor.java new file mode 100644 index 0000000000..3423097bea --- /dev/null +++ b/hutool-core/src/main/java/cn/hutool/core/compress/ZipCopyVisitor.java @@ -0,0 +1,84 @@ +package cn.hutool.core.compress; + +import cn.hutool.core.util.StrUtil; + +import java.io.IOException; +import java.nio.file.CopyOption; +import java.nio.file.FileAlreadyExistsException; +import java.nio.file.FileSystem; +import java.nio.file.FileVisitResult; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.SimpleFileVisitor; +import java.nio.file.attribute.BasicFileAttributes; + +/** + * Zip文件拷贝的FileVisitor实现,zip中追加文件,此类非线程安全
+ * 此类在遍历源目录并复制过程中会自动创建目标目录中不存在的上级目录。 + * + * @author looly + * @since 5.7.15 + */ +public class ZipCopyVisitor extends SimpleFileVisitor { + + /** + * 源Path,或基准路径,用于计算被拷贝文件的相对路径 + */ + private final Path source; + private final FileSystem fileSystem; + private final CopyOption[] copyOptions; + + /** + * 构造 + * + * @param source 源Path,或基准路径,用于计算被拷贝文件的相对路径 + * @param fileSystem 目标Zip文件 + * @param copyOptions 拷贝选项,如跳过已存在等 + */ + public ZipCopyVisitor(Path source, FileSystem fileSystem, CopyOption... copyOptions) { + this.source = source; + this.fileSystem = fileSystem; + this.copyOptions = copyOptions; + } + + @Override + public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) throws IOException { + final Path targetDir = resolveTarget(dir); + if(StrUtil.isNotEmpty(targetDir.toString())){ + // 在目标的Zip文件中的相对位置创建目录 + try { + Files.copy(dir, targetDir, copyOptions); + } catch (FileAlreadyExistsException e) { + if (false == Files.isDirectory(targetDir)) { + throw e; + } + } + } + + return FileVisitResult.CONTINUE; + } + + @Override + public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException { + // 如果目标存在,无论目录还是文件都抛出FileAlreadyExistsException异常,此处不做特别处理 + Files.copy(file, resolveTarget(file), copyOptions); + + return FileVisitResult.CONTINUE; + } + + /** + * 根据源文件或目录路径,拼接生成目标的文件或目录路径
+ * 原理是首先截取源路径,得到相对路径,再和目标路径拼接 + * + *

+ * 如:源路径是 /opt/test/,需要拷贝的文件是 /opt/test/a/a.txt,得到相对路径 a/a.txt
+ * 目标路径是/home/,则得到最终目标路径是 /home/a/a.txt + *

+ * + * @param file 需要拷贝的文件或目录Path + * @return 目标Path + */ + private Path resolveTarget(Path file) { + return fileSystem.getPath(source.relativize(file).toString()); + } +} diff --git a/hutool-core/src/main/java/cn/hutool/core/io/file/PathUtil.java b/hutool-core/src/main/java/cn/hutool/core/io/file/PathUtil.java index 95abe2c522..1c3ed2d92f 100644 --- a/hutool-core/src/main/java/cn/hutool/core/io/file/PathUtil.java +++ b/hutool-core/src/main/java/cn/hutool/core/io/file/PathUtil.java @@ -656,6 +656,20 @@ public static Path mkParentDirs(Path path) { return mkdir(path.getParent()); } + /** + * 获取{@link Path}文件名 + * + * @param path {@link Path} + * @return 文件名 + * @since 5.7.15 + */ + public static String getName(Path path) { + if (null == path) { + return null; + } + return path.getFileName().toString(); + } + /** * 删除文件或空目录,不追踪软链 * diff --git a/hutool-core/src/main/java/cn/hutool/core/io/file/visitor/CopyVisitor.java b/hutool-core/src/main/java/cn/hutool/core/io/file/visitor/CopyVisitor.java index e8bd619be7..b36e8c1935 100644 --- a/hutool-core/src/main/java/cn/hutool/core/io/file/visitor/CopyVisitor.java +++ b/hutool-core/src/main/java/cn/hutool/core/io/file/visitor/CopyVisitor.java @@ -1,11 +1,7 @@ package cn.hutool.core.io.file.visitor; import cn.hutool.core.io.file.PathUtil; -import cn.hutool.core.util.StrUtil; -import com.sun.nio.zipfs.ZipFileSystem; -import com.sun.nio.zipfs.ZipPath; -import java.io.File; import java.io.IOException; import java.nio.file.CopyOption; import java.nio.file.FileAlreadyExistsException; @@ -24,53 +20,48 @@ */ public class CopyVisitor extends SimpleFileVisitor { + /** + * 源Path,或基准路径,用于计算被拷贝文件的相对路径 + */ private final Path source; private final Path target; - private boolean isTargetCreated; - private final boolean isZipFile; - private String dirRoot = null; private final CopyOption[] copyOptions; + /** + * 标记目标目录是否创建,省略每次判断目标是否存在 + */ + private boolean isTargetCreated; + /** * 构造 * - * @param source 源Path - * @param target 目标Path + * @param source 源Path,或基准路径,用于计算被拷贝文件的相对路径 + * @param target 目标Path * @param copyOptions 拷贝选项,如跳过已存在等 */ public CopyVisitor(Path source, Path target, CopyOption... copyOptions) { - if(PathUtil.exists(target, false) && false == PathUtil.isDirectory(target)){ + if (PathUtil.exists(target, false) && false == PathUtil.isDirectory(target)) { throw new IllegalArgumentException("Target must be a directory"); } this.source = source; this.target = target; - this.isZipFile = target instanceof ZipPath; this.copyOptions = copyOptions; } @Override - public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) - throws IOException { - final Path targetDir; - if (isZipFile) { - ZipPath zipPath = (ZipPath) target; - ZipFileSystem fileSystem = zipPath.getFileSystem(); - if (dirRoot == null) { - targetDir = fileSystem.getPath(dir.getFileName().toString()); - dirRoot = dir.getFileName().toString() + File.separator; - } else { - targetDir = fileSystem.getPath(dirRoot, StrUtil.subAfter(dir.toString(), dirRoot, false)); - } - } else { - initTarget(); - // 将当前目录相对于源路径转换为相对于目标路径 - targetDir = target.resolve(source.relativize(dir)); - } + public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) throws IOException { + initTargetDir(); + // 将当前目录相对于源路径转换为相对于目标路径 + final Path targetDir = resolveTarget(dir); + + // 在目录不存在的情况下,copy方法会创建新目录 try { Files.copy(dir, targetDir, copyOptions); } catch (FileAlreadyExistsException e) { - if (false == Files.isDirectory(targetDir)) + if (false == Files.isDirectory(targetDir)) { + // 目标文件存在抛出异常,目录忽略 throw e; + } } return FileVisitResult.CONTINUE; } @@ -78,25 +69,33 @@ public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) @Override public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException { - if (isZipFile) { - if (dirRoot == null) { - Files.copy(file, target, copyOptions); - } else { - ZipPath zipPath = (ZipPath) target; - Files.copy(file, zipPath.getFileSystem().getPath(dirRoot, StrUtil.subAfter(file.toString(), dirRoot, false)), copyOptions); - } - } else { - initTarget(); - Files.copy(file, target.resolve(source.relativize(file)), copyOptions); - } + initTargetDir(); + // 如果目标存在,无论目录还是文件都抛出FileAlreadyExistsException异常,此处不做特别处理 + Files.copy(file, resolveTarget(file), copyOptions); return FileVisitResult.CONTINUE; } + /** + * 根据源文件或目录路径,拼接生成目标的文件或目录路径
+ * 原理是首先截取源路径,得到相对路径,再和目标路径拼接 + * + *

+ * 如:源路径是 /opt/test/,需要拷贝的文件是 /opt/test/a/a.txt,得到相对路径 a/a.txt
+ * 目标路径是/home/,则得到最终目标路径是 /home/a/a.txt + *

+ * + * @param file 需要拷贝的文件或目录Path + * @return 目标Path + */ + private Path resolveTarget(Path file) { + return target.resolve(source.relativize(file)); + } + /** * 初始化目标文件或目录 */ - private void initTarget(){ - if(false == this.isTargetCreated){ + private void initTargetDir() { + if (false == this.isTargetCreated) { PathUtil.mkdir(this.target); this.isTargetCreated = true; } diff --git a/hutool-core/src/main/java/cn/hutool/core/util/ZipUtil.java b/hutool-core/src/main/java/cn/hutool/core/util/ZipUtil.java index dc8d552308..a813242bfe 100644 --- a/hutool-core/src/main/java/cn/hutool/core/util/ZipUtil.java +++ b/hutool-core/src/main/java/cn/hutool/core/util/ZipUtil.java @@ -2,6 +2,7 @@ import cn.hutool.core.compress.Deflate; import cn.hutool.core.compress.Gzip; +import cn.hutool.core.compress.ZipCopyVisitor; import cn.hutool.core.compress.ZipReader; import cn.hutool.core.compress.ZipWriter; import cn.hutool.core.exceptions.UtilException; @@ -10,7 +11,7 @@ import cn.hutool.core.io.IORuntimeException; import cn.hutool.core.io.IoUtil; import cn.hutool.core.io.file.FileSystemUtil; -import cn.hutool.core.io.file.visitor.CopyVisitor; +import cn.hutool.core.io.file.PathUtil; import cn.hutool.core.io.resource.Resource; import java.io.BufferedInputStream; @@ -22,12 +23,11 @@ import java.io.InputStream; import java.io.OutputStream; import java.nio.charset.Charset; +import java.nio.file.CopyOption; import java.nio.file.FileAlreadyExistsException; import java.nio.file.FileSystem; import java.nio.file.Files; import java.nio.file.Path; -import java.nio.file.Paths; -import java.nio.file.StandardCopyOption; import java.util.ArrayList; import java.util.Collections; import java.util.List; @@ -40,8 +40,8 @@ /** * 压缩工具类 * - * @see cn.hutool.core.compress.ZipWriter * @author Looly + * @see cn.hutool.core.compress.ZipWriter */ public class ZipUtil { @@ -84,25 +84,29 @@ public static InputStream getStream(ZipFile zipFile, ZipEntry zipEntry) { } /** - * 在zip文件中添加新文件, 如果已经存在则不会有效果 + * 在zip文件中添加新文件或目录
+ * 新文件添加在zip根目录,文件夹包括其本身和内容
+ * 如果待添加文件夹是系统根路径(如/或c:/),则只复制文件夹下的内容 * - * @param zipFilePathStr zip文件存储路径 - * @param appendFilePathStr 待添加文件路径(可以是文件夹) + * @param zipPath zip文件的Path + * @param appendFilePath 待添加文件Path(可以是文件夹) + * @param options 拷贝选项,可选是否覆盖等 + * @since 5.7.15 */ - public static void addFile(String zipFilePathStr, String appendFilePathStr) throws IOException { - Path zipPath = Paths.get(zipFilePathStr); - Path appendFilePath = Paths.get(appendFilePathStr); - + public static void append(Path zipPath, Path appendFilePath, CopyOption... options) throws IOException { try (FileSystem zipFileSystem = FileSystemUtil.createZip(zipPath.toString())) { - Path root = zipFileSystem.getPath("/"); - Path dest = zipFileSystem.getPath(root.toString(), appendFilePath.getFileName().toString()); - if (!Files.isDirectory(appendFilePath)) { - Files.copy(appendFilePath, dest, StandardCopyOption.COPY_ATTRIBUTES); + if (Files.isDirectory(appendFilePath)) { + Path source = appendFilePath.getParent(); + if (null == source) { + // 如果用户提供的是根路径,则不复制目录,直接复制目录下的内容 + source = appendFilePath; + } + Files.walkFileTree(appendFilePath, new ZipCopyVisitor(source, zipFileSystem, options)); } else { - Files.walkFileTree(appendFilePath, new CopyVisitor(appendFilePath, zipFileSystem.getPath(zipFilePathStr))); + Files.copy(appendFilePath, zipFileSystem.getPath(PathUtil.getName(appendFilePath)), options); } } catch (FileAlreadyExistsException ignored) { - // 文件已存在, 跳过 + // 不覆盖情况下,文件已存在, 跳过 } } @@ -271,7 +275,7 @@ public static void zip(OutputStream out, Charset charset, boolean withSrcDir, Fi */ @Deprecated public static void zip(ZipOutputStream zipOutputStream, boolean withSrcDir, FileFilter filter, File... srcFiles) throws IORuntimeException { - try(final ZipWriter zipWriter = new ZipWriter(zipOutputStream)){ + try (final ZipWriter zipWriter = new ZipWriter(zipOutputStream)) { zipWriter.add(withSrcDir, filter, srcFiles); } } @@ -370,7 +374,7 @@ public static File zip(File zipFile, String[] paths, InputStream[] ins, Charset throw new IllegalArgumentException("Paths length is not equals to ins length !"); } - try(final ZipWriter zipWriter = ZipWriter.of(zipFile, charset)){ + try (final ZipWriter zipWriter = ZipWriter.of(zipFile, charset)) { for (int i = 0; i < paths.length; i++) { zipWriter.add(paths[i], ins[i]); } @@ -395,7 +399,7 @@ public static void zip(OutputStream out, String[] paths, InputStream[] ins) { throw new IllegalArgumentException("Paths length is not equals to ins length !"); } - try(final ZipWriter zipWriter = ZipWriter.of(out, DEFAULT_CHARSET)){ + try (final ZipWriter zipWriter = ZipWriter.of(out, DEFAULT_CHARSET)) { for (int i = 0; i < paths.length; i++) { zipWriter.add(paths[i], ins[i]); } @@ -419,7 +423,7 @@ public static void zip(ZipOutputStream zipOutputStream, String[] paths, InputStr throw new IllegalArgumentException("Paths length is not equals to ins length !"); } - try(final ZipWriter zipWriter = new ZipWriter(zipOutputStream)){ + try (final ZipWriter zipWriter = new ZipWriter(zipOutputStream)) { for (int i = 0; i < paths.length; i++) { zipWriter.add(paths[i], ins[i]); } @@ -559,7 +563,7 @@ public static File unzip(ZipFile zipFile, File outFile) throws IORuntimeExceptio StrUtil.format("Target path [{}] exist!", outFile.getAbsolutePath())); } - try(final ZipReader reader = new ZipReader(zipFile)){ + try (final ZipReader reader = new ZipReader(zipFile)) { reader.readTo(outFile); } return outFile; @@ -602,7 +606,7 @@ public static InputStream get(ZipFile zipFile, String path) { * @since 5.5.2 */ public static void read(ZipFile zipFile, Consumer consumer) { - try(final ZipReader reader = new ZipReader(zipFile)){ + try (final ZipReader reader = new ZipReader(zipFile)) { reader.read(consumer); } } @@ -636,7 +640,7 @@ public static File unzip(InputStream in, File outFile, Charset charset) throws U * @since 4.5.8 */ public static File unzip(ZipInputStream zipStream, File outFile) throws UtilException { - try(final ZipReader reader = new ZipReader(zipStream)){ + try (final ZipReader reader = new ZipReader(zipStream)) { reader.readTo(outFile); } return outFile; @@ -650,7 +654,7 @@ public static File unzip(ZipInputStream zipStream, File outFile) throws UtilExce * @since 5.5.2 */ public static void read(ZipInputStream zipStream, Consumer consumer) { - try(final ZipReader reader = new ZipReader(zipStream)){ + try (final ZipReader reader = new ZipReader(zipStream)) { reader.read(consumer); } } @@ -702,7 +706,7 @@ public static byte[] unzipFileBytes(File zipFile, String name) { * @since 4.1.8 */ public static byte[] unzipFileBytes(File zipFile, Charset charset, String name) { - try(final ZipReader reader = ZipReader.of(zipFile, charset)){ + try (final ZipReader reader = ZipReader.of(zipFile, charset)) { return IoUtil.readBytes(reader.get(name)); } } diff --git a/hutool-core/src/test/java/cn/hutool/core/util/ZipUtilTest.java b/hutool-core/src/test/java/cn/hutool/core/util/ZipUtilTest.java index acd389ab15..e224e359a6 100644 --- a/hutool-core/src/test/java/cn/hutool/core/util/ZipUtilTest.java +++ b/hutool-core/src/test/java/cn/hutool/core/util/ZipUtilTest.java @@ -24,7 +24,7 @@ public class ZipUtilTest { @Test - public void addFileTest() throws IOException { + public void appendTest() throws IOException { File appendFile = FileUtil.file("test-zip/addFile.txt"); File zipFile = FileUtil.file("test-zip/test.zip"); @@ -34,23 +34,27 @@ public void addFileTest() throws IOException { FileUtil.copy(zipFile, tempZipFile, true); // test file add - List beforeNames = zipEntryNames(zipFile); - ZipUtil.addFile(zipFile.getAbsolutePath(), appendFile.getAbsolutePath()); - List afterNames = zipEntryNames(zipFile); + List beforeNames = zipEntryNames(tempZipFile); + ZipUtil.append(tempZipFile.toPath(), appendFile.toPath()); + List afterNames = zipEntryNames(tempZipFile); + + // 确认增加了文件 + Assert.assertEquals(beforeNames.size() + 1, afterNames.size()); Assert.assertTrue(afterNames.containsAll(beforeNames)); Assert.assertTrue(afterNames.contains(appendFile.getName())); // test dir add - beforeNames = afterNames; + beforeNames = zipEntryNames(tempZipFile); File addDirFile = FileUtil.file("test-zip/test-add"); - ZipUtil.addFile(zipFile.getAbsolutePath(), addDirFile.getAbsolutePath()); - afterNames = zipEntryNames(zipFile); + ZipUtil.append(tempZipFile.toPath(), addDirFile.toPath()); + afterNames = zipEntryNames(tempZipFile); + // 确认增加了文件和目录,增加目录和目录下一个文件,故此处+2 + Assert.assertEquals(beforeNames.size() + 2, afterNames.size()); Assert.assertTrue(afterNames.containsAll(beforeNames)); Assert.assertTrue(afterNames.contains(appendFile.getName())); // rollback - FileUtil.copy(tempZipFile, zipFile, true); Assert.assertTrue(String.format("delete temp file %s failed", tempZipFile.getCanonicalPath()), tempZipFile.delete()); } diff --git a/hutool-core/src/test/resources/test-zip/test-add/test.txt b/hutool-core/src/test/resources/test-zip/test-add/test.txt index e69de29bb2..56a6051ca2 100644 --- a/hutool-core/src/test/resources/test-zip/test-add/test.txt +++ b/hutool-core/src/test/resources/test-zip/test-add/test.txt @@ -0,0 +1 @@ +1 \ No newline at end of file From 64729aad63a82cef5be33acbe4df5ba4be6f5dc9 Mon Sep 17 00:00:00 2001 From: Looly Date: Thu, 21 Oct 2021 03:13:52 +0800 Subject: [PATCH 40/51] add method --- CHANGELOG.md | 1 + .../cn/hutool/core/collection/CollUtil.java | 33 ++++++++++++++----- 2 files changed, 25 insertions(+), 9 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 688101d0d5..cd6f792717 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -15,6 +15,7 @@ * 【bom 】 支持scope=import方式引入(issue#1561@Github) * 【core 】 新增Hash接口,HashXXX继承此接口 * 【core 】 ZipUtil增加append方法(pr#441@Gitee) +* 【core 】 CollUtil增加重载(issue#I4E9FS@Gitee) ### 🐞Bug修复 * 【core 】 修复CollUtil.isEqualList两个null返回错误问题(issue#1885@Github) 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 7ab4a9c23a..94e992aa1f 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 @@ -53,6 +53,7 @@ import java.util.concurrent.LinkedBlockingDeque; import java.util.function.Function; import java.util.function.Predicate; +import java.util.function.Supplier; /** * 集合相关工具类 @@ -1050,8 +1051,8 @@ public static List sub(List list, int start, int end) { * @param end 结束位置(不包含) * @param step 步进 * @return 截取后的数组,当开始位置超过最大时,返回空的List - * @since 4.0.6 * @see ListUtil#sub(List, int, int, int) + * @since 4.0.6 */ public static List sub(List list, int start, int end, int step) { return ListUtil.sub(list, start, end, step); @@ -1073,11 +1074,11 @@ public static List sub(Collection collection, int start, int end) { /** * 截取集合的部分 * - * @param 集合元素类型 - * @param collection 被截取的数组 - * @param start 开始位置(包含) - * @param end 结束位置(不包含) - * @param step 步进 + * @param 集合元素类型 + * @param collection 被截取的数组 + * @param start 开始位置(包含) + * @param end 结束位置(不包含) + * @param step 步进 * @return 截取后的数组,当开始位置超过最大时,返回空集合 * @since 4.0.6 */ @@ -1086,7 +1087,7 @@ public static List sub(Collection collection, int start, int end, int return ListUtil.empty(); } - final List list = collection instanceof List ? (List)collection : ListUtil.toList(collection); + final List list = collection instanceof List ? (List) collection : ListUtil.toList(collection); return sub(list, start, end, step); } @@ -1575,6 +1576,20 @@ public static , E> T defaultIfEmpty(T collection, T defa return isEmpty(collection) ? defaultCollection : collection; } + /** + * 如果给定集合为空,返回默认集合 + * + * @param 集合类型 + * @param 集合元素类型 + * @param collection 集合 + * @param supplier 默认值懒加载函数 + * @return 非空(empty)的原集合或默认集合 + * @since 5.7.15 + */ + public static , E> T defaultIfEmpty(T collection, Supplier supplier) { + return isEmpty(collection) ? supplier.get() : collection; + } + /** * Iterable是否为空 * @@ -2948,8 +2963,8 @@ public static int size(final Object object) { * @since 5.6.0 */ public static boolean isEqualList(final Collection list1, final Collection list2) { - if (list1 == list2){ - return true; + if (list1 == list2) { + return true; } if (list1 == null || list2 == null || list1.size() != list2.size()) { return false; From 04032f1094e979e5d9835f044bd8a1fcdc892f86 Mon Sep 17 00:00:00 2001 From: Looly Date: Thu, 21 Oct 2021 03:27:11 +0800 Subject: [PATCH 41/51] fix comment --- .../main/java/cn/hutool/extra/ftp/Ftp.java | 24 +++++++++---------- 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/hutool-extra/src/main/java/cn/hutool/extra/ftp/Ftp.java b/hutool-extra/src/main/java/cn/hutool/extra/ftp/Ftp.java index a74af70734..81f74df288 100644 --- a/hutool-extra/src/main/java/cn/hutool/extra/ftp/Ftp.java +++ b/hutool-extra/src/main/java/cn/hutool/extra/ftp/Ftp.java @@ -470,9 +470,9 @@ public boolean delDir(String dirPath) throws IORuntimeException { * 上传文件到指定目录,可选: * *
-	 * 1. path为null或""上传到当前路径
-	 * 2. path为相对路径则相对于当前路径的子路径
-	 * 3. path为绝对路径则上传到此路径
+	 * 1. destPath为null或""上传到当前路径
+	 * 2. destPath为相对路径则相对于当前路径的子路径
+	 * 3. destPath为绝对路径则上传到此路径
 	 * 
* * @param destPath 服务端路径,可以为{@code null} 或者相对路径或绝对路径 @@ -495,14 +495,14 @@ public boolean upload(String destPath, File file) { * * * @param file 文件 - * @param path 服务端路径,可以为{@code null} 或者相对路径或绝对路径 + * @param destPath 服务端路径,可以为{@code null} 或者相对路径或绝对路径 * @param fileName 自定义在服务端保存的文件名 * @return 是否上传成功 * @throws IORuntimeException IO异常 */ - public boolean upload(String path, String fileName, File file) throws IORuntimeException { + public boolean upload(String destPath, String fileName, File file) throws IORuntimeException { try (InputStream in = FileUtil.getInputStream(file)) { - return upload(path, fileName, in); + return upload(destPath, fileName, in); } catch (IOException e) { throw new IORuntimeException(e); } @@ -517,13 +517,13 @@ public boolean upload(String path, String fileName, File file) throws IORuntimeE * 3. path为绝对路径则上传到此路径 * * - * @param path 服务端路径,可以为{@code null} 或者相对路径或绝对路径 + * @param destPath 服务端路径,可以为{@code null} 或者相对路径或绝对路径 * @param fileName 文件名 * @param fileStream 文件流 * @return 是否上传成功 * @throws IORuntimeException IO异常 */ - public boolean upload(String path, String fileName, InputStream fileStream) throws IORuntimeException { + public boolean upload(String destPath, String fileName, InputStream fileStream) throws IORuntimeException { try { client.setFileType(FTPClient.BINARY_FILE_TYPE); } catch (IOException e) { @@ -535,10 +535,10 @@ public boolean upload(String path, String fileName, InputStream fileStream) thro pwd = pwd(); } - if (StrUtil.isNotBlank(path)) { - mkDirs(path); - if (false == isDir(path)) { - throw new FtpException("Change dir to [{}] error, maybe dir not exist!", path); + if (StrUtil.isNotBlank(destPath)) { + mkDirs(destPath); + if (false == isDir(destPath)) { + throw new FtpException("Change dir to [{}] error, maybe dir not exist!", destPath); } } From 98512fa1f2c319de6af835ec8b5ce2f78b12fa00 Mon Sep 17 00:00:00 2001 From: Looly Date: Thu, 21 Oct 2021 13:18:53 +0800 Subject: [PATCH 42/51] fix bug --- CHANGELOG.md | 1 + README-EN.md | 6 +-- README.md | 12 ++--- .../java/cn/hutool/core/codec/Base64.java | 2 +- .../crypto/symmetric/SymmetricCrypto.java | 8 ++- .../java/cn/hutool/crypto/test/SmTest.java | 2 + .../crypto/test/symmetric/Sm4StreamTest.java | 51 +++++++++++++++++++ 7 files changed, 68 insertions(+), 14 deletions(-) create mode 100644 hutool-crypto/src/test/java/cn/hutool/crypto/test/symmetric/Sm4StreamTest.java diff --git a/CHANGELOG.md b/CHANGELOG.md index cd6f792717..5ea43ec0bc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -25,6 +25,7 @@ * 【cache 】 修复LRUCache线程安全问题(issue#1895@Github) * 【crypto 】 修复KeyUtil异常信息参数丢失问题(issue#1902@Github) * 【core 】 修复StrUtil.split和splittoArray不一致问题(issue#I4ELU5@Github) +* 【core 】 修复SymmetricCrypto未关闭CipherOutputStream导致的问题(issue#I4EMST@Gitee) ------------------------------------------------------------------------------------------------------------- diff --git a/README-EN.md b/README-EN.md index 9783a1a9f4..5576237e4b 100644 --- a/README-EN.md +++ b/README-EN.md @@ -201,13 +201,11 @@ If you think Hutool is good, you can donate to buy tshe author a pack of chili~, ## 👕shop -We provide the T-Shirt with Hutool Logo, please visit the shop: +We provide the T-Shirt and Sweater with Hutool Logo, please visit the shop: [Hutool T-Shirt](https://m.tb.cn/h.f47W8zc?sm=7d2b95) -
- -
+[Hutool Sweater](https://m.tb.cn/h.fUM4d6B?sm=4c0a5f) ## 📌WeChat Official Account diff --git a/README.md b/README.md index f9ea97fd92..99967c3f01 100644 --- a/README.md +++ b/README.md @@ -202,21 +202,19 @@ Hutool欢迎任何人为Hutool添砖加瓦,贡献代码,不过维护者是 如果你觉得Hutool不错,可以捐赠请维护者吃包辣条~,在此表示感谢^_^。 -点击以下链接,将页面拉到最下方点击“捐赠”即可。 - [Gitee上捐赠](https://gitee.com/dromara/hutool) [捐赠给Dromara组织](https://dromara.gitee.io/donate.html) ## 👕周边 -我们提供了印有Hutool Logo的主题T恤,欢迎点击购买: +你也可以通过购买Hutool的周边商品来支持Hutool维护哦! -[HutoolT恤商店](https://m.tb.cn/h.f47W8zc?sm=7d2b95) +我们提供了印有Hutool Logo的周边商品,欢迎点击购买支持: -
- -
+[Hutool周边商店-T恤](https://m.tb.cn/h.f47W8zc?sm=7d2b95) + +[Hutool周边商店-卫衣](https://m.tb.cn/h.fUM4d6B?sm=4c0a5f) ## 📌公众号 diff --git a/hutool-core/src/main/java/cn/hutool/core/codec/Base64.java b/hutool-core/src/main/java/cn/hutool/core/codec/Base64.java index 1b77c2ba67..ec5b018f63 100644 --- a/hutool-core/src/main/java/cn/hutool/core/codec/Base64.java +++ b/hutool-core/src/main/java/cn/hutool/core/codec/Base64.java @@ -299,7 +299,7 @@ public static void decodeToStream(CharSequence base64, OutputStream out, boolean * base64解码 * * @param base64 被解码的base64字符串 - * @return 被加密后的字符串 + * @return 解码后的bytes */ public static byte[] decode(CharSequence base64) { return Base64Decoder.decode(base64); diff --git a/hutool-crypto/src/main/java/cn/hutool/crypto/symmetric/SymmetricCrypto.java b/hutool-crypto/src/main/java/cn/hutool/crypto/symmetric/SymmetricCrypto.java index abc31d1823..ba92ab5539 100644 --- a/hutool-crypto/src/main/java/cn/hutool/crypto/symmetric/SymmetricCrypto.java +++ b/hutool-crypto/src/main/java/cn/hutool/crypto/symmetric/SymmetricCrypto.java @@ -300,9 +300,11 @@ public void encrypt(InputStream data, OutputStream out, boolean isClose) throws throw new CryptoException(e); } finally { lock.unlock(); + // issue#I4EMST@Gitee + // CipherOutputStream必须关闭,才能完全写出 + IoUtil.close(cipherOutputStream); if (isClose) { IoUtil.close(data); - IoUtil.close(cipherOutputStream); } } } @@ -351,9 +353,11 @@ public void decrypt(InputStream data, OutputStream out, boolean isClose) throws throw new CryptoException(e); } finally { lock.unlock(); + // issue#I4EMST@Gitee + // CipherOutputStream必须关闭,才能完全写出 + IoUtil.close(cipherInputStream); if (isClose) { IoUtil.close(data); - IoUtil.close(cipherInputStream); } } } diff --git a/hutool-crypto/src/test/java/cn/hutool/crypto/test/SmTest.java b/hutool-crypto/src/test/java/cn/hutool/crypto/test/SmTest.java index 80c7995fed..c98dd21eec 100644 --- a/hutool-crypto/src/test/java/cn/hutool/crypto/test/SmTest.java +++ b/hutool-crypto/src/test/java/cn/hutool/crypto/test/SmTest.java @@ -81,4 +81,6 @@ public void hmacSm3Test() { String digest = hMac.digestHex(content); Assert.assertEquals("493e3f9a1896b43075fbe54658076727960d69632ac6b6ed932195857a6840c6", digest); } + + } diff --git a/hutool-crypto/src/test/java/cn/hutool/crypto/test/symmetric/Sm4StreamTest.java b/hutool-crypto/src/test/java/cn/hutool/crypto/test/symmetric/Sm4StreamTest.java new file mode 100644 index 0000000000..87ec2675c2 --- /dev/null +++ b/hutool-crypto/src/test/java/cn/hutool/crypto/test/symmetric/Sm4StreamTest.java @@ -0,0 +1,51 @@ +package cn.hutool.crypto.test.symmetric; + +import cn.hutool.crypto.symmetric.SM4; +import org.junit.Ignore; +import org.junit.Test; + +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; + +/** + * https://gitee.com/dromara/hutool/issues/I4EMST + */ +public class Sm4StreamTest { + + private static final SM4 sm4 = new SM4(); + + private static final boolean IS_CLOSE = false; + + @Test + @Ignore + public void sm4Test(){ + String source = "d:/test/sm4_1.txt"; + String target = "d:/test/sm4_2.data"; + String target2 = "d:/test/sm4_3.txt"; + encrypt(source, target); + decrypt(target, target2); + } + + public static void encrypt(String source, String target) { + try (InputStream input = new FileInputStream(source); + OutputStream out = new FileOutputStream(target)) { + sm4.encrypt(input, out, IS_CLOSE); + System.out.println("============encrypt end"); + } catch (IOException e) { + e.printStackTrace(); + } + } + + public static void decrypt(String source, String target) { + try (InputStream input = new FileInputStream(source); + OutputStream out = new FileOutputStream(target)) { + sm4.decrypt(input, out, IS_CLOSE); + System.out.println("============decrypt end"); + } catch (IOException e) { + e.printStackTrace(); + } + } +} From 79a293320a1763c5fb848c3789b97fba058d1a16 Mon Sep 17 00:00:00 2001 From: Looly Date: Thu, 21 Oct 2021 13:42:20 +0800 Subject: [PATCH 43/51] fix test --- .../src/test/java/cn/hutool/extra/ftp/FtpTest.java | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/hutool-extra/src/test/java/cn/hutool/extra/ftp/FtpTest.java b/hutool-extra/src/test/java/cn/hutool/extra/ftp/FtpTest.java index 6265b6ca0e..1cba5c565e 100644 --- a/hutool-extra/src/test/java/cn/hutool/extra/ftp/FtpTest.java +++ b/hutool-extra/src/test/java/cn/hutool/extra/ftp/FtpTest.java @@ -7,8 +7,6 @@ import org.junit.Ignore; import org.junit.Test; -import java.util.List; - public class FtpTest { @Test @@ -25,12 +23,9 @@ public void cdTest() { @Test @Ignore public void uploadTest() { - Ftp ftp = new Ftp("looly.centos"); - - List ls = ftp.ls("/file"); - Console.log(ls); + Ftp ftp = new Ftp("localhost"); - boolean upload = ftp.upload("/file/aaa", FileUtil.file("E:/qrcodeWithLogo.jpg")); + boolean upload = ftp.upload("/temp", FileUtil.file("d:/test/test.zip")); Console.log(upload); IoUtil.close(ftp); From 6ad682fb0a1f28f899c1771e70b7d3024086d624 Mon Sep 17 00:00:00 2001 From: Looly Date: Thu, 21 Oct 2021 14:24:02 +0800 Subject: [PATCH 44/51] add method --- CHANGELOG.md | 1 + .../java/cn/hutool/core/bean/BeanUtil.java | 26 ++++++++++++ .../hutool/core/bean/copier/BeanCopier.java | 7 ++++ .../hutool/core/bean/copier/CopyOptions.java | 42 ++++++++++++++++--- .../cn/hutool/core/bean/BeanUtilTest.java | 13 ++++++ 5 files changed, 83 insertions(+), 6 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5ea43ec0bc..ad9219221f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -16,6 +16,7 @@ * 【core 】 新增Hash接口,HashXXX继承此接口 * 【core 】 ZipUtil增加append方法(pr#441@Gitee) * 【core 】 CollUtil增加重载(issue#I4E9FS@Gitee) +* 【core 】 CopyOptions新增setFieldValueEditor(issue#I4E08T@Gitee) ### 🐞Bug修复 * 【core 】 修复CollUtil.isEqualList两个null返回错误问题(issue#1885@Github) 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 84b99c239d..7961246489 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 @@ -664,6 +664,32 @@ public static Map beanToMap(Object bean, Map tar ).copy(); } + /** + * 对象转Map
+ * 通过自定义{@link CopyOptions} 完成抓换选项,以便实现: + * + *
+	 * 1. 字段筛选,可以去除不需要的字段
+	 * 2. 字段变换,例如实现驼峰转下划线
+	 * 3. 自定义字段前缀或后缀等等
+	 * 4. 字段值处理
+	 * ...
+	 * 
+ * + * @param bean bean对象 + * @param targetMap 目标的Map + * @param copyOptions 拷贝选项 + * @return Map + * @since 5.7.15 + */ + public static Map beanToMap(Object bean, Map targetMap, CopyOptions copyOptions) { + if (null == bean) { + return null; + } + + return BeanCopier.create(bean, targetMap, copyOptions).copy(); + } + // --------------------------------------------------------------------------------------------- copyProperties /** diff --git a/hutool-core/src/main/java/cn/hutool/core/bean/copier/BeanCopier.java b/hutool-core/src/main/java/cn/hutool/core/bean/copier/BeanCopier.java index fc6c3947dd..bcfae58a1d 100644 --- a/hutool-core/src/main/java/cn/hutool/core/bean/copier/BeanCopier.java +++ b/hutool-core/src/main/java/cn/hutool/core/bean/copier/BeanCopier.java @@ -191,6 +191,10 @@ private void beanToMap(Object bean, Map targetMap) { if(null != copyOptions.propertiesFilter && false == copyOptions.propertiesFilter.test(prop.getField(), value)) { return; } + + // since 5.7.15 + value = copyOptions.editFieldValue(key, value); + if ((null == value && copyOptions.ignoreNullValue) || bean == value) { // 当允许跳过空时,跳过 //值不能为bean本身,防止循环引用,此类也跳过 @@ -257,6 +261,9 @@ private void valueProviderToBean(ValueProvider valueProvider, Object bea return; } + // since 5.7.15 + value = copyOptions.editFieldValue(providerKey, value); + if ((null == value && copyOptions.ignoreNullValue) || bean == value) { // 当允许跳过空时,跳过 // 值不能为bean本身,防止循环引用 diff --git a/hutool-core/src/main/java/cn/hutool/core/bean/copier/CopyOptions.java b/hutool-core/src/main/java/cn/hutool/core/bean/copier/CopyOptions.java index 502ea4846b..4cab138096 100644 --- a/hutool-core/src/main/java/cn/hutool/core/bean/copier/CopyOptions.java +++ b/hutool-core/src/main/java/cn/hutool/core/bean/copier/CopyOptions.java @@ -7,6 +7,7 @@ import java.io.Serializable; import java.lang.reflect.Field; import java.util.Map; +import java.util.function.BiFunction; import java.util.function.BiPredicate; /** @@ -57,6 +58,10 @@ public class CopyOptions implements Serializable { * 字段属性编辑器,用于自定义属性转换规则,例如驼峰转下划线等 */ protected Editor fieldNameEditor; + /** + * 字段属性值编辑器,用于自定义属性值转换规则,例如null转""等 + */ + protected BiFunction fieldValueEditor; /** * 是否支持transient关键字修饰和@Transient注解,如果支持,被修饰的字段或方法对应的字段将被忽略。 */ @@ -224,6 +229,30 @@ public CopyOptions setFieldNameEditor(Editor fieldNameEditor) { return this; } + /** + * 设置字段属性值编辑器,用于自定义属性值转换规则,例如null转""等
+ * + * @param fieldValueEditor 字段属性值编辑器,用于自定义属性值转换规则,例如null转""等 + * @return CopyOptions + * @since 5.7.15 + */ + public CopyOptions setFieldValueEditor(BiFunction fieldValueEditor) { + this.fieldValueEditor = fieldValueEditor; + return this; + } + + /** + * 转换字段名为编辑后的字段名 + * + * @param fieldName 字段名 + * @return 编辑后的字段名 + * @since 5.7.15 + */ + protected Object editFieldValue(String fieldName, Object fieldValue) { + return (null != this.fieldValueEditor) ? + this.fieldValueEditor.apply(fieldName, fieldValue) : fieldValue; + } + /** * 是否支持transient关键字修饰和@Transient注解,如果支持,被修饰的字段或方法对应的字段将被忽略。 * @@ -251,12 +280,12 @@ public CopyOptions setTransientSupport(boolean transientSupport) { * 当非反向,则根据源字段名获取目标字段名,反之根据目标字段名获取源字段名。 * * @param fieldName 字段名 - * @param reversed 是否反向映射 + * @param reversed 是否反向映射 * @return 映射后的字段名 */ - protected String getMappedFieldName(String fieldName, boolean reversed){ + protected String getMappedFieldName(String fieldName, boolean reversed) { Map mapping = reversed ? getReversedMapping() : this.fieldMapping; - if(MapUtil.isEmpty(mapping)){ + if (MapUtil.isEmpty(mapping)) { return fieldName; } return ObjectUtil.defaultIfNull(mapping.get(fieldName), fieldName); @@ -264,11 +293,12 @@ protected String getMappedFieldName(String fieldName, boolean reversed){ /** * 转换字段名为编辑后的字段名 + * * @param fieldName 字段名 * @return 编辑后的字段名 * @since 5.4.2 */ - protected String editFieldName(String fieldName){ + protected String editFieldName(String fieldName) { return (null != this.fieldNameEditor) ? this.fieldNameEditor.edit(fieldName) : fieldName; } @@ -279,10 +309,10 @@ protected String editFieldName(String fieldName){ * @since 4.1.10 */ private Map getReversedMapping() { - if(null == this.fieldMapping){ + if (null == this.fieldMapping) { return null; } - if(null == this.reversedFieldMapping){ + if (null == this.reversedFieldMapping) { reversedFieldMapping = MapUtil.reverse(this.fieldMapping); } return reversedFieldMapping; 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 526e8bbcaa..d05bc13b00 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 @@ -197,6 +197,19 @@ public void beanToMapTest2() { Assert.assertEquals("sub名字", map.get("sub_name")); } + @Test + public void beanToMapWithValueEditTest() { + SubPerson person = new SubPerson(); + person.setAge(14); + person.setOpenid("11213232"); + person.setName("测试A11"); + person.setSubName("sub名字"); + + Map map = BeanUtil.beanToMap(person, new LinkedHashMap<>(), + CopyOptions.create().setFieldValueEditor((key, value) -> key + "_" + value)); + Assert.assertEquals("subName_sub名字", map.get("subName")); + } + @Test public void beanToMapWithAliasTest() { SubPersonWithAlias person = new SubPersonWithAlias(); From 39df3623e1bd64a3be68caa8ed9081eefdb91a67 Mon Sep 17 00:00:00 2001 From: Looly Date: Thu, 21 Oct 2021 21:36:22 +0800 Subject: [PATCH 45/51] fix bug --- CHANGELOG.md | 1 + .../main/java/cn/hutool/core/net/url/UrlBuilder.java | 2 +- .../src/main/java/cn/hutool/core/net/url/UrlQuery.java | 2 +- .../test/java/cn/hutool/core/net/UrlBuilderTest.java | 10 ++++++++++ 4 files changed, 13 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index ad9219221f..edb4031424 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -27,6 +27,7 @@ * 【crypto 】 修复KeyUtil异常信息参数丢失问题(issue#1902@Github) * 【core 】 修复StrUtil.split和splittoArray不一致问题(issue#I4ELU5@Github) * 【core 】 修复SymmetricCrypto未关闭CipherOutputStream导致的问题(issue#I4EMST@Gitee) +* 【core 】 修复QueryBuilder对/转义问题(issue#1904@Github) ------------------------------------------------------------------------------------------------------------- diff --git a/hutool-core/src/main/java/cn/hutool/core/net/url/UrlBuilder.java b/hutool-core/src/main/java/cn/hutool/core/net/url/UrlBuilder.java index 357a215b81..be6d5c94be 100644 --- a/hutool-core/src/main/java/cn/hutool/core/net/url/UrlBuilder.java +++ b/hutool-core/src/main/java/cn/hutool/core/net/url/UrlBuilder.java @@ -129,7 +129,7 @@ public static UrlBuilder of(String url) { */ public static UrlBuilder of(String url, Charset charset) { Assert.notBlank(url, "Url must be not blank!"); - return of(URLUtil.url(url.trim()), charset); + return of(URLUtil.url(StrUtil.trim(url)), charset); } /** diff --git a/hutool-core/src/main/java/cn/hutool/core/net/url/UrlQuery.java b/hutool-core/src/main/java/cn/hutool/core/net/url/UrlQuery.java index 3c297b9de0..8f882d9f58 100644 --- a/hutool-core/src/main/java/cn/hutool/core/net/url/UrlQuery.java +++ b/hutool-core/src/main/java/cn/hutool/core/net/url/UrlQuery.java @@ -312,7 +312,7 @@ private void addParam(String key, String value, Charset charset) { private static String toStr(CharSequence str, Charset charset, boolean isEncode) { String result = StrUtil.str(str); if (isEncode) { - result = URLUtil.encodeAll(result, charset); + result = URLUtil.encodeFragment(result, charset); } return result; } diff --git a/hutool-core/src/test/java/cn/hutool/core/net/UrlBuilderTest.java b/hutool-core/src/test/java/cn/hutool/core/net/UrlBuilderTest.java index f53085fc66..b88687dff5 100644 --- a/hutool-core/src/test/java/cn/hutool/core/net/UrlBuilderTest.java +++ b/hutool-core/src/test/java/cn/hutool/core/net/UrlBuilderTest.java @@ -271,4 +271,14 @@ public void fragmentEncodeTest(){ urlBuilder = UrlBuilder.ofHttp(urlBuilder.toString()); Assert.assertEquals(urlBuilder.toString(), urlBuilder.toString()); } + + @Test + public void slashEncodeTest(){ + // https://github.com/dromara/hutool/issues/1904 + // 在query中,"/"是不可转义字符 + // 见:https://www.rfc-editor.org/rfc/rfc3986.html#section-3.4 + String url = "https://invoice.maycur.com/2b27a802-8423-4d41-86f5-63a6b259f61e.xlsx?download/2b27a802-8423-4d41-86f5-63a6b259f61e.xlsx&e=1630491088"; + final UrlBuilder urlBuilder = UrlBuilder.ofHttp(url); + Assert.assertEquals(url, urlBuilder.toString()); + } } From ce9ee646d6746489945c927b96617ac865866759 Mon Sep 17 00:00:00 2001 From: Looly Date: Thu, 21 Oct 2021 22:24:14 +0800 Subject: [PATCH 46/51] add test --- .../java/cn/hutool/core/util/ZipUtilTest.java | 20 +++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/hutool-core/src/test/java/cn/hutool/core/util/ZipUtilTest.java b/hutool-core/src/test/java/cn/hutool/core/util/ZipUtilTest.java index e224e359a6..a98972e67f 100644 --- a/hutool-core/src/test/java/cn/hutool/core/util/ZipUtilTest.java +++ b/hutool-core/src/test/java/cn/hutool/core/util/ZipUtilTest.java @@ -160,4 +160,24 @@ public void zipStreamTest(){ } } + @Test + @Ignore + public void zipStreamTest2(){ + // https://github.com/dromara/hutool/issues/944 + String file1 = "d:/test/a.txt"; + String file2 = "d:/test/a.txt"; + String file3 = "d:/test/asn1.key"; + + String zip = "d:/test/test2.zip"; + try (OutputStream out = new FileOutputStream(zip)){ + //实际应用中, out 为 HttpServletResponse.getOutputStream + ZipUtil.zip(out, Charset.defaultCharset(), false, null, + new File(file1), + new File(file2), + new File(file3) + ); + } catch (IOException e) { + throw new IORuntimeException(e); + } + } } From 322992dba7c425a2664e98f505d09c825539e230 Mon Sep 17 00:00:00 2001 From: Looly Date: Thu, 21 Oct 2021 22:42:42 +0800 Subject: [PATCH 47/51] change readme --- README-EN.md | 34 ++++++++++++++++++---------------- README.md | 40 +++++++++++++++++++++------------------- 2 files changed, 39 insertions(+), 35 deletions(-) diff --git a/README-EN.md b/README-EN.md index 5576237e4b..d11d3dfaad 100644 --- a/README-EN.md +++ b/README-EN.md @@ -117,6 +117,24 @@ Each module can be introduced individually, or all modules can be introduced by ------------------------------------------------------------------------------- +## 🪙Support Hutool + +### 💳Donate + +If you think Hutool is good, you can donate to buy the author a pack of chili~, thanks in advance ^_^. + +[Gitee donate](https://gitee.com/dromara/hutool) + +[Dromara donate](https://dromara.gitee.io/donate.html) + +### 👕Shop about Hutool + +We provide the T-Shirt and Sweater with Hutool Logo, please visit the shop: + +👉 [Hutool Shop](https://m.tb.cn/h.fVxoBOm?sm=2756b2) 👈 + +------------------------------------------------------------------------------- + ## 📦Install ### 🍊Maven @@ -191,22 +209,6 @@ Hutool welcomes anyone to contribute code to Hutool, but the author suffers from [![Stargazers over time](https://starchart.cc/dromara/hutool.svg)](https://starchart.cc/dromara/hutool) -## 💳Donate - -If you think Hutool is good, you can donate to buy tshe author a pack of chili~, thanks in advance ^_^. - -[Gitee donate](https://gitee.com/dromara/hutool) - -[Dromara donate](https://dromara.gitee.io/donate.html) - -## 👕shop - -We provide the T-Shirt and Sweater with Hutool Logo, please visit the shop: - -[Hutool T-Shirt](https://m.tb.cn/h.f47W8zc?sm=7d2b95) - -[Hutool Sweater](https://m.tb.cn/h.fUM4d6B?sm=4c0a5f) - ## 📌WeChat Official Account
diff --git a/README.md b/README.md index 99967c3f01..4bfc648e6f 100644 --- a/README.md +++ b/README.md @@ -113,6 +113,26 @@ Hutool的存在就是为了减少代码搜索成本,避免网络上参差不 ------------------------------------------------------------------------------- +## 🪙支持Hutool + +### 💳捐赠 + +如果你觉得Hutool不错,可以捐赠请维护者吃包辣条~,在此表示感谢^_^。 + +[Gitee上捐赠](https://gitee.com/dromara/hutool) + +[捐赠给Dromara组织](https://dromara.gitee.io/donate.html) + +### 👕周边商店 + +你也可以通过购买Hutool的周边商品来支持Hutool维护哦! + +我们提供了印有Hutool Logo的周边商品,欢迎点击购买支持: + +👉 [Hutool 周边商店](https://m.tb.cn/h.fVxoBOm?sm=2756b2) 👈 + +------------------------------------------------------------------------------- + ## 📦安装 ### 🍊Maven @@ -139,7 +159,7 @@ implementation 'cn.hutool:hutool-all:5.7.15' > 🔔️注意 > Hutool 5.x支持JDK8+,对Android平台没有测试,不能保证所有工具类或工具方法可用。 -> 如果你的项目使用JDK7,请使用Hutool 4.x版本 +> 如果你的项目使用JDK7,请使用Hutool 4.x版本(不再更新) ### 🚽编译安装 @@ -198,24 +218,6 @@ Hutool欢迎任何人为Hutool添砖加瓦,贡献代码,不过维护者是 [![Stargazers over time](https://starchart.cc/dromara/hutool.svg)](https://starchart.cc/dromara/hutool) -## 💳捐赠 - -如果你觉得Hutool不错,可以捐赠请维护者吃包辣条~,在此表示感谢^_^。 - -[Gitee上捐赠](https://gitee.com/dromara/hutool) - -[捐赠给Dromara组织](https://dromara.gitee.io/donate.html) - -## 👕周边 - -你也可以通过购买Hutool的周边商品来支持Hutool维护哦! - -我们提供了印有Hutool Logo的周边商品,欢迎点击购买支持: - -[Hutool周边商店-T恤](https://m.tb.cn/h.f47W8zc?sm=7d2b95) - -[Hutool周边商店-卫衣](https://m.tb.cn/h.fUM4d6B?sm=4c0a5f) - ## 📌公众号
From fc96980b6815cf7681b167a0246295191ab4a371 Mon Sep 17 00:00:00 2001 From: Looly Date: Thu, 21 Oct 2021 22:59:15 +0800 Subject: [PATCH 48/51] change fix test --- .../src/test/java/cn/hutool/core/net/UrlBuilderTest.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/hutool-core/src/test/java/cn/hutool/core/net/UrlBuilderTest.java b/hutool-core/src/test/java/cn/hutool/core/net/UrlBuilderTest.java index b88687dff5..78e4d93eec 100644 --- a/hutool-core/src/test/java/cn/hutool/core/net/UrlBuilderTest.java +++ b/hutool-core/src/test/java/cn/hutool/core/net/UrlBuilderTest.java @@ -192,9 +192,9 @@ public void weixinUrlTest(){ "&sn=1044c0d19723f74f04f4c1da34eefa35" + "&chksm=6cbda3a25bca2ab4516410db6ce6e125badaac2f8c5548ea6e18eab6dc3c5422cb8cbe1095f7"; final UrlBuilder builder = UrlBuilder.ofHttp(urlStr, CharsetUtil.CHARSET_UTF_8); - // 原URL中的&替换为&,value中的=被编码为%3D + // 原URL中的&替换为& Assert.assertEquals("https://mp.weixin.qq.com/s?" + - "__biz=MzI5NjkyNTIxMg%3D%3D" + + "__biz=MzI5NjkyNTIxMg==" + "&mid=100000465&idx=1" + "&sn=1044c0d19723f74f04f4c1da34eefa35" + "&chksm=6cbda3a25bca2ab4516410db6ce6e125badaac2f8c5548ea6e18eab6dc3c5422cb8cbe1095f7", @@ -240,7 +240,7 @@ public void toURITest() throws URISyntaxException { public void testEncodeInQuery() { String webUrl = "http://exmple.com/patha/pathb?a=123&b=4?6&c=789"; // b=4?6 参数中有未编码的? final UrlBuilder urlBuilder = UrlBuilder.of(webUrl, StandardCharsets.UTF_8); - Assert.assertEquals("a=123&b=4%3F6&c=789", urlBuilder.getQueryStr()); + Assert.assertEquals("a=123&b=4?6&c=789", urlBuilder.getQueryStr()); } @Test From 30eec7a74de2cc9a41959a7c484449097732867a Mon Sep 17 00:00:00 2001 From: Looly Date: Thu, 21 Oct 2021 23:08:34 +0800 Subject: [PATCH 49/51] add test --- .../src/test/java/cn/hutool/json/JSONArrayTest.java | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/hutool-json/src/test/java/cn/hutool/json/JSONArrayTest.java b/hutool-json/src/test/java/cn/hutool/json/JSONArrayTest.java index 2b3522b9a4..b6e83e0d87 100644 --- a/hutool-json/src/test/java/cn/hutool/json/JSONArrayTest.java +++ b/hutool-json/src/test/java/cn/hutool/json/JSONArrayTest.java @@ -263,4 +263,12 @@ public void filterExcludeTest(){ final String s = json1.toJSONString(0, (pair) -> false == pair.getValue().equals("value2")); Assert.assertEquals("[\"value1\",\"value3\",true]", s); } + + @Test + public void putNullTest(){ + final JSONArray array = JSONUtil.createArray(JSONConfig.create().setIgnoreNullValue(false)); + array.set(null); + + Assert.assertEquals("[null]", array.toString()); + } } From 142f6f4ac75f0883423f8667dd04122208a5e970 Mon Sep 17 00:00:00 2001 From: Looly Date: Thu, 21 Oct 2021 23:22:33 +0800 Subject: [PATCH 50/51] update --- hutool-db/pom.xml | 4 ++-- hutool-extra/pom.xml | 10 +++++----- hutool-poi/pom.xml | 2 +- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/hutool-db/pom.xml b/hutool-db/pom.xml index b434f61d5b..2c43499b34 100644 --- a/hutool-db/pom.xml +++ b/hutool-db/pom.xml @@ -143,13 +143,13 @@ mysql mysql-connector-java - 8.0.26 + 8.0.27 test org.postgresql postgresql - 42.2.23.jre7 + 42.3.0 test diff --git a/hutool-extra/pom.xml b/hutool-extra/pom.xml index dee8ad2512..0c2d6dc116 100644 --- a/hutool-extra/pom.xml +++ b/hutool-extra/pom.xml @@ -30,7 +30,7 @@ 3.7.2 5.1.1 4.0.1 - 2.5.5 + 2.5.6 3.3.0 @@ -241,7 +241,7 @@ org.apache.lucene lucene-analyzers-smartcn - 8.10.0 + 8.10.1 true @@ -326,7 +326,7 @@ com.github.houbb pinyin - 0.2.1 + 0.2.2 true @@ -418,7 +418,7 @@ org.mvel mvel2 - 2.4.12.Final + 2.4.13.Final compile true @@ -432,7 +432,7 @@ org.springframework spring-expression - 5.3.10 + 5.3.12 compile true diff --git a/hutool-poi/pom.xml b/hutool-poi/pom.xml index a3e0cc10b5..7352973cf7 100644 --- a/hutool-poi/pom.xml +++ b/hutool-poi/pom.xml @@ -45,7 +45,7 @@ org.ofdrw ofdrw-full - 1.15.5 + 1.16.0 compile true From 31db52dfa2cbf700f6e7ca0dbec2e0d64007b6dc Mon Sep 17 00:00:00 2001 From: Looly Date: Thu, 21 Oct 2021 23:28:51 +0800 Subject: [PATCH 51/51] =?UTF-8?q?=F0=9F=A7=A3release=205.7.15?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- 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 +- 22 files changed, 22 insertions(+), 22 deletions(-) diff --git a/hutool-all/pom.xml b/hutool-all/pom.xml index 6d1004deb4..f4cab970a6 100644 --- a/hutool-all/pom.xml +++ b/hutool-all/pom.xml @@ -9,7 +9,7 @@ cn.hutool hutool-parent - 5.7.15-SNAPSHOT + 5.7.15 hutool-all diff --git a/hutool-aop/pom.xml b/hutool-aop/pom.xml index aa06006926..bc7be21a4c 100644 --- a/hutool-aop/pom.xml +++ b/hutool-aop/pom.xml @@ -9,7 +9,7 @@ cn.hutool hutool-parent - 5.7.15-SNAPSHOT + 5.7.15 hutool-aop diff --git a/hutool-bloomFilter/pom.xml b/hutool-bloomFilter/pom.xml index 8554dd99c2..b7ed8a4106 100644 --- a/hutool-bloomFilter/pom.xml +++ b/hutool-bloomFilter/pom.xml @@ -9,7 +9,7 @@ cn.hutool hutool-parent - 5.7.15-SNAPSHOT + 5.7.15 hutool-bloomFilter diff --git a/hutool-bom/pom.xml b/hutool-bom/pom.xml index 5966acbff8..402341d6cc 100644 --- a/hutool-bom/pom.xml +++ b/hutool-bom/pom.xml @@ -9,7 +9,7 @@ cn.hutool hutool-parent - 5.7.15-SNAPSHOT + 5.7.15 hutool-bom diff --git a/hutool-cache/pom.xml b/hutool-cache/pom.xml index c2081d8e28..5bfe068ca0 100644 --- a/hutool-cache/pom.xml +++ b/hutool-cache/pom.xml @@ -9,7 +9,7 @@ cn.hutool hutool-parent - 5.7.15-SNAPSHOT + 5.7.15 hutool-cache diff --git a/hutool-captcha/pom.xml b/hutool-captcha/pom.xml index 68df03a4a6..e1293b7d38 100644 --- a/hutool-captcha/pom.xml +++ b/hutool-captcha/pom.xml @@ -9,7 +9,7 @@ cn.hutool hutool-parent - 5.7.15-SNAPSHOT + 5.7.15 hutool-captcha diff --git a/hutool-core/pom.xml b/hutool-core/pom.xml index af165e71e8..5956c0bb28 100644 --- a/hutool-core/pom.xml +++ b/hutool-core/pom.xml @@ -9,7 +9,7 @@ cn.hutool hutool-parent - 5.7.15-SNAPSHOT + 5.7.15 hutool-core diff --git a/hutool-cron/pom.xml b/hutool-cron/pom.xml index 0b753dbd74..62dc9962c0 100644 --- a/hutool-cron/pom.xml +++ b/hutool-cron/pom.xml @@ -9,7 +9,7 @@ cn.hutool hutool-parent - 5.7.15-SNAPSHOT + 5.7.15 hutool-cron diff --git a/hutool-crypto/pom.xml b/hutool-crypto/pom.xml index 96c365b9e6..3a89f935d4 100644 --- a/hutool-crypto/pom.xml +++ b/hutool-crypto/pom.xml @@ -9,7 +9,7 @@ cn.hutool hutool-parent - 5.7.15-SNAPSHOT + 5.7.15 hutool-crypto diff --git a/hutool-db/pom.xml b/hutool-db/pom.xml index 2c43499b34..dcfd6d3327 100644 --- a/hutool-db/pom.xml +++ b/hutool-db/pom.xml @@ -9,7 +9,7 @@ cn.hutool hutool-parent - 5.7.15-SNAPSHOT + 5.7.15 hutool-db diff --git a/hutool-dfa/pom.xml b/hutool-dfa/pom.xml index 77f87ced8a..65d431f9e5 100644 --- a/hutool-dfa/pom.xml +++ b/hutool-dfa/pom.xml @@ -9,7 +9,7 @@ cn.hutool hutool-parent - 5.7.15-SNAPSHOT + 5.7.15 hutool-dfa diff --git a/hutool-extra/pom.xml b/hutool-extra/pom.xml index 0c2d6dc116..9d7626fcf5 100644 --- a/hutool-extra/pom.xml +++ b/hutool-extra/pom.xml @@ -9,7 +9,7 @@ cn.hutool hutool-parent - 5.7.15-SNAPSHOT + 5.7.15 hutool-extra diff --git a/hutool-http/pom.xml b/hutool-http/pom.xml index 5e8f4ad11b..d8c8d1f833 100644 --- a/hutool-http/pom.xml +++ b/hutool-http/pom.xml @@ -9,7 +9,7 @@ cn.hutool hutool-parent - 5.7.15-SNAPSHOT + 5.7.15 hutool-http diff --git a/hutool-json/pom.xml b/hutool-json/pom.xml index 53479ea030..96e3feb613 100644 --- a/hutool-json/pom.xml +++ b/hutool-json/pom.xml @@ -9,7 +9,7 @@ cn.hutool hutool-parent - 5.7.15-SNAPSHOT + 5.7.15 hutool-json diff --git a/hutool-jwt/pom.xml b/hutool-jwt/pom.xml index 94f10eb9dd..d50aaa75f8 100644 --- a/hutool-jwt/pom.xml +++ b/hutool-jwt/pom.xml @@ -9,7 +9,7 @@ cn.hutool hutool-parent - 5.7.15-SNAPSHOT + 5.7.15 hutool-jwt diff --git a/hutool-log/pom.xml b/hutool-log/pom.xml index e2e516e6b9..ba5ea9f7e5 100644 --- a/hutool-log/pom.xml +++ b/hutool-log/pom.xml @@ -9,7 +9,7 @@ cn.hutool hutool-parent - 5.7.15-SNAPSHOT + 5.7.15 hutool-log diff --git a/hutool-poi/pom.xml b/hutool-poi/pom.xml index 7352973cf7..e1cb315c74 100644 --- a/hutool-poi/pom.xml +++ b/hutool-poi/pom.xml @@ -9,7 +9,7 @@ cn.hutool hutool-parent - 5.7.15-SNAPSHOT + 5.7.15 hutool-poi diff --git a/hutool-script/pom.xml b/hutool-script/pom.xml index 334119e1ce..56c4429c69 100644 --- a/hutool-script/pom.xml +++ b/hutool-script/pom.xml @@ -9,7 +9,7 @@ cn.hutool hutool-parent - 5.7.15-SNAPSHOT + 5.7.15 hutool-script diff --git a/hutool-setting/pom.xml b/hutool-setting/pom.xml index 346588b87c..81491727d2 100644 --- a/hutool-setting/pom.xml +++ b/hutool-setting/pom.xml @@ -9,7 +9,7 @@ cn.hutool hutool-parent - 5.7.15-SNAPSHOT + 5.7.15 hutool-setting diff --git a/hutool-socket/pom.xml b/hutool-socket/pom.xml index 0f023e57bb..afc15da80c 100644 --- a/hutool-socket/pom.xml +++ b/hutool-socket/pom.xml @@ -9,7 +9,7 @@ cn.hutool hutool-parent - 5.7.15-SNAPSHOT + 5.7.15 hutool-socket diff --git a/hutool-system/pom.xml b/hutool-system/pom.xml index 0a2badeace..f89f373b97 100644 --- a/hutool-system/pom.xml +++ b/hutool-system/pom.xml @@ -9,7 +9,7 @@ cn.hutool hutool-parent - 5.7.15-SNAPSHOT + 5.7.15 hutool-system diff --git a/pom.xml b/pom.xml index 7341d1fa86..7ef4218319 100644 --- a/pom.xml +++ b/pom.xml @@ -8,7 +8,7 @@ cn.hutool hutool-parent - 5.7.15-SNAPSHOT + 5.7.15 hutool Hutool是一个小而全的Java工具类库,通过静态方法封装,降低相关API的学习成本,提高工作效率,使Java拥有函数式语言般的优雅,让Java语言也可以“甜甜的”。 https://github.com/dromara/hutool