From 1f054ca1b8c36177b092c5721f5bdd173ce94863 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=8E=8B=E8=89=AF?= <841369634@qq.com> Date: Fri, 8 Oct 2021 17:39:34 +0800 Subject: [PATCH 01/86] =?UTF-8?q?Base64.isBase64(base64)=E6=96=B9=E6=B3=95?= =?UTF-8?q?=EF=BC=8C=E5=A6=82=E6=9E=9C=E5=AD=98=E5=9C=A8=E5=8F=8C=E5=AD=97?= =?UTF-8?q?=E8=8A=82=E5=AD=97=E7=AC=A6=EF=BC=8C=E7=9B=B4=E6=8E=A5=E8=BF=94?= =?UTF-8?q?=E5=9B=9Efalse=E3=80=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/main/java/cn/hutool/core/codec/Base64.java | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) 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 976124c01..809e5f779 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 @@ -323,7 +323,18 @@ public class Base64 { * @since 5.7.5 */ public static boolean isBase64(CharSequence base64){ - return isBase64(StrUtil.utf8Bytes(base64)); + if (base64 == null || base64.length() < 2) { + return false; + } + + byte[] bytes = StrUtil.utf8Bytes(base64); + + if (bytes.length != base64.length()) { + // 如果长度不相等,说明存在双字节字符,肯定不是Base64,直接返回false + return false; + } + + return isBase64(bytes); } /** From adfe11ce37a02676012f5c4c322899532d346fd3 Mon Sep 17 00:00:00 2001 From: Looly Date: Sat, 9 Oct 2021 18:39:00 +0800 Subject: [PATCH 02/86] change test --- .../src/test/java/cn/hutool/core/thread/ThreadUtilTest.java | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/hutool-core/src/test/java/cn/hutool/core/thread/ThreadUtilTest.java b/hutool-core/src/test/java/cn/hutool/core/thread/ThreadUtilTest.java index 78aed5e29..42dc40316 100644 --- a/hutool-core/src/test/java/cn/hutool/core/thread/ThreadUtilTest.java +++ b/hutool-core/src/test/java/cn/hutool/core/thread/ThreadUtilTest.java @@ -4,11 +4,13 @@ import org.junit.Assert; import org.junit.Test; public class ThreadUtilTest { - + @Test public void executeTest() { final boolean isValid = true; - + ThreadUtil.execute(() -> Assert.assertTrue(isValid)); + + } } From 6e3bad82d031ec1c2ea53cd8b0514077d5431e4b Mon Sep 17 00:00:00 2001 From: Looly Date: Sat, 9 Oct 2021 18:39:22 +0800 Subject: [PATCH 03/86] change test --- .../src/test/java/cn/hutool/core/thread/ThreadUtilTest.java | 2 -- 1 file changed, 2 deletions(-) diff --git a/hutool-core/src/test/java/cn/hutool/core/thread/ThreadUtilTest.java b/hutool-core/src/test/java/cn/hutool/core/thread/ThreadUtilTest.java index 42dc40316..e0e951baf 100644 --- a/hutool-core/src/test/java/cn/hutool/core/thread/ThreadUtilTest.java +++ b/hutool-core/src/test/java/cn/hutool/core/thread/ThreadUtilTest.java @@ -10,7 +10,5 @@ public class ThreadUtilTest { final boolean isValid = true; ThreadUtil.execute(() -> Assert.assertTrue(isValid)); - - } } From 2129881efba6c5c174ac2972e977b6d0787f6da8 Mon Sep 17 00:00:00 2001 From: Looly Date: Sat, 9 Oct 2021 20:18:36 +0800 Subject: [PATCH 04/86] fix test --- .../java/cn/hutool/http/webservice/SoapClientTest.java | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/hutool-http/src/test/java/cn/hutool/http/webservice/SoapClientTest.java b/hutool-http/src/test/java/cn/hutool/http/webservice/SoapClientTest.java index ffd137bc9..52b46791b 100644 --- a/hutool-http/src/test/java/cn/hutool/http/webservice/SoapClientTest.java +++ b/hutool-http/src/test/java/cn/hutool/http/webservice/SoapClientTest.java @@ -10,7 +10,7 @@ import javax.xml.soap.SOAPMessage; /** * SOAP相关单元测试 - * + * * @author looly * */ @@ -23,19 +23,19 @@ public class SoapClientTest { .setMethod("web:getCountryCityByIp", "http://WebXml.com.cn/") .setCharset(CharsetUtil.CHARSET_GBK) .setParam("theIpAddress", "218.21.240.106"); - + Console.log(client.getMsgStr(true)); - + Console.log(client.send(true)); } - + @Test @Ignore public void requestForMessageTest() throws SOAPException { SoapClient client = SoapClient.create("http://www.webxml.com.cn/WebServices/IpAddressSearchWebService.asmx") .setMethod("web:getCountryCityByIp", "http://WebXml.com.cn/") .setParam("theIpAddress", "218.21.240.106"); - + SOAPMessage message = client.sendForMessage(); Console.log(message.getSOAPBody().getTextContent()); } From 0f96a95f2c563c436f0661e18777a4ad59e28c94 Mon Sep 17 00:00:00 2001 From: Looly Date: Sat, 9 Oct 2021 20:24:02 +0800 Subject: [PATCH 05/86] fix isBase64 --- CHANGELOG.md | 1 + .../java/cn/hutool/core/codec/Base64.java | 22 +++++++++---------- 2 files changed, 12 insertions(+), 11 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index b776605de..3af8d62ca 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -22,6 +22,7 @@ * 【core 】 Week增加of重载,支持DayOfWek(pr#1872@Github) * 【poi 】 优化read,避免多次创建CopyOptions(issue#1875@Github) * 【core 】 优化CsvReader,实现可控遍历(pr#1873@Github) +* 【core 】 优化Base64.isBase64判断(pr#1879@Github) ### 🐞Bug修复 * 【http 】 修复HttpCookie设置cookies的方法,不符合RFC6265规范问题(pr#418@Gitee) 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 809e5f779..1b77c2ba6 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 @@ -322,12 +322,12 @@ public class Base64 { * @return 是否为Base64 * @since 5.7.5 */ - public static boolean isBase64(CharSequence base64){ + public static boolean isBase64(CharSequence base64) { if (base64 == null || base64.length() < 2) { return false; } - byte[] bytes = StrUtil.utf8Bytes(base64); + final byte[] bytes = StrUtil.utf8Bytes(base64); if (bytes.length != base64.length()) { // 如果长度不相等,说明存在双字节字符,肯定不是Base64,直接返回false @@ -344,15 +344,15 @@ public class Base64 { * @return 是否为Base64 * @since 5.7.5 */ - public static boolean isBase64(byte[] base64Bytes){ + public static boolean isBase64(byte[] base64Bytes) { boolean hasPadding = false; for (byte base64Byte : base64Bytes) { - if(hasPadding){ - if('=' != base64Byte){ + if (hasPadding) { + if ('=' != base64Byte) { // 前一个字符是'=',则后边的字符都必须是'=',即'='只能都位于结尾 return false; } - } else if('=' == base64Byte){ + } else if ('=' == base64Byte) { // 发现'=' 标记之 hasPadding = true; } else if (false == (Base64Decoder.isBase64Code(base64Byte) || isWhiteSpace(base64Byte))) { @@ -364,12 +364,12 @@ public class Base64 { private static boolean isWhiteSpace(byte byteToCheck) { switch (byteToCheck) { - case ' ' : - case '\n' : - case '\r' : - case '\t' : + case ' ': + case '\n': + case '\r': + case '\t': return true; - default : + default: return false; } } From 412160ac53e6f0569c222f54222c517d0bbe2dc8 Mon Sep 17 00:00:00 2001 From: Looly Date: Sat, 9 Oct 2021 21:13:30 +0800 Subject: [PATCH 06/86] fix bug --- CHANGELOG.md | 1 + .../main/java/cn/hutool/core/io/FileUtil.java | 5 ++- .../hutool/core/collection/CollUtilTest.java | 17 +++++++++ .../java/cn/hutool/json/issues1881Test.java | 35 +++++++++++++++++++ 4 files changed, 57 insertions(+), 1 deletion(-) create mode 100644 hutool-json/src/test/java/cn/hutool/json/issues1881Test.java diff --git a/CHANGELOG.md b/CHANGELOG.md index 3af8d62ca..f653f3f09 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -31,6 +31,7 @@ * 【db 】 修复Condition没占位符的情况下sql没引号问题(issue#1846@Github) * 【cache 】 修复FIFOCache中remove回调无效问题(pr#1856@Github) * 【json 】 修复JSONArray.set中,index为0报错问题(issue#1858@Github) +* 【core 】 修复FileUtil.checkSlip中getCanonicalPath异常引起的问题(issue#1858@Github) ------------------------------------------------------------------------------------------------------------- diff --git a/hutool-core/src/main/java/cn/hutool/core/io/FileUtil.java b/hutool-core/src/main/java/cn/hutool/core/io/FileUtil.java index 7f5b4c28f..4c838288d 100644 --- a/hutool-core/src/main/java/cn/hutool/core/io/FileUtil.java +++ b/hutool-core/src/main/java/cn/hutool/core/io/FileUtil.java @@ -3278,7 +3278,10 @@ public class FileUtil extends PathUtil { parentCanonicalPath = parentFile.getCanonicalPath(); canonicalPath = file.getCanonicalPath(); } catch (IOException e) { - throw new IORuntimeException(e); + // issue#I4CWMO@Gitee + // getCanonicalPath有时会抛出奇怪的IO异常,此时忽略异常,使用AbsolutePath判断。 + parentCanonicalPath = parentFile.getAbsolutePath(); + canonicalPath = file.getAbsolutePath(); } if (false == canonicalPath.startsWith(parentCanonicalPath)) { throw new IllegalArgumentException("New file is outside of the parent dir: " + file.getName()); diff --git a/hutool-core/src/test/java/cn/hutool/core/collection/CollUtilTest.java b/hutool-core/src/test/java/cn/hutool/core/collection/CollUtilTest.java index 80c66af8f..c3be3c277 100644 --- a/hutool-core/src/test/java/cn/hutool/core/collection/CollUtilTest.java +++ b/hutool-core/src/test/java/cn/hutool/core/collection/CollUtilTest.java @@ -712,6 +712,23 @@ public class CollUtilTest { Assert.assertEquals("d", map.get("keyd")); } + @Test + public void mapToMapTest(){ + final HashMap oldMap = new HashMap<>(); + oldMap.put("a", "1"); + oldMap.put("b", "12"); + oldMap.put("c", "134"); + + final Map map = CollUtil.toMap(oldMap.entrySet(), + new HashMap<>(), + Map.Entry::getKey, + entry -> Long.parseLong(entry.getValue())); + + Assert.assertEquals(1L, (long)map.get("a")); + Assert.assertEquals(12L, (long)map.get("b")); + Assert.assertEquals(134L, (long)map.get("c")); + } + @Test public void countMapTest() { ArrayList list = CollUtil.newArrayList("a", "b", "c", "c", "a", "b", "d"); diff --git a/hutool-json/src/test/java/cn/hutool/json/issues1881Test.java b/hutool-json/src/test/java/cn/hutool/json/issues1881Test.java new file mode 100644 index 000000000..07cdee890 --- /dev/null +++ b/hutool-json/src/test/java/cn/hutool/json/issues1881Test.java @@ -0,0 +1,35 @@ +package cn.hutool.json; + +import cn.hutool.core.lang.Console; +import lombok.Data; +import lombok.experimental.Accessors; +import org.junit.Test; + +import java.io.Serializable; +import java.util.ArrayList; +import java.util.List; + +public class issues1881Test { + @Accessors(chain = true) + @Data + public class ThingsHolderContactVO implements Serializable { + + private static final long serialVersionUID = -8727337936070932370L; + private Long id; + private Integer type; + private String memberId; + private String name; + private String phone; + private String avatar; + private Long createTime; + } + + @Test + public void parseTest(){ + List holderContactVOList = new ArrayList<>(); + holderContactVOList.add(new ThingsHolderContactVO().setId(1L).setName("1")); + holderContactVOList.add(new ThingsHolderContactVO().setId(2L).setName("2")); + + Console.log(JSONUtil.parseArray(holderContactVOList)); + } +} From 3037cb222c2732b85ae6a40c790883534febcabe Mon Sep 17 00:00:00 2001 From: Looly Date: Sat, 9 Oct 2021 21:47:54 +0800 Subject: [PATCH 07/86] add test --- CHANGELOG.md | 4 +- .../cn/hutool/core/text/StrFormatter.java | 40 ++++++++++++++----- .../cn/hutool/core/lang/StrFormatterTest.java | 23 +++++++++-- 3 files changed, 50 insertions(+), 17 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index f653f3f09..dbc471984 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,7 +3,7 @@ ------------------------------------------------------------------------------------------------------------- -# 5.7.14 (2021-10-08) +# 5.7.14 (2021-10-09) ### 🐣新特性 * 【extra 】 修复HttpCookie设置cookies的方法,不符合RFC6265规范问题(issue#I4B70D@Gitee) @@ -16,13 +16,13 @@ * 【core 】 DateTime构造和DateUtil.parse可选是否宽松模式(issue#1849@Github) * 【core 】 TreeBuilder增加部分根节点set方法(issue#1848@Github) * 【core 】 优化Base64.isBase64方法:减少一次多余的判断(pr#1860@Github) -* 【core 】 优化Base64.isBase64方法:减少一次多余的判断(pr#1860@Github) * 【cache 】 优化FIFOCache未设置过期策略时,无需遍历判断过期对象(pr#425@Gitee) * 【core 】 增加Opt类(pr#426@Gitee) * 【core 】 Week增加of重载,支持DayOfWek(pr#1872@Github) * 【poi 】 优化read,避免多次创建CopyOptions(issue#1875@Github) * 【core 】 优化CsvReader,实现可控遍历(pr#1873@Github) * 【core 】 优化Base64.isBase64判断(pr#1879@Github) +* 【core 】 新增StrFormatter.formatWith(pr#430@Gitee) ### 🐞Bug修复 * 【http 】 修复HttpCookie设置cookies的方法,不符合RFC6265规范问题(pr#418@Gitee) diff --git a/hutool-core/src/main/java/cn/hutool/core/text/StrFormatter.java b/hutool-core/src/main/java/cn/hutool/core/text/StrFormatter.java index cc5ec7cc5..a70d5542f 100644 --- a/hutool-core/src/main/java/cn/hutool/core/text/StrFormatter.java +++ b/hutool-core/src/main/java/cn/hutool/core/text/StrFormatter.java @@ -9,7 +9,6 @@ import java.util.Map; * 字符串格式化工具 * * @author Looly - * */ public class StrFormatter { @@ -23,22 +22,42 @@ public class StrFormatter { * 转义\: format("this is \\\\{} for {}", "a", "b") =》 this is \a for b
* * @param strPattern 字符串模板 - * @param argArray 参数列表 + * @param argArray 参数列表 * @return 结果 */ - public static String format(final String strPattern, final Object... argArray) { - if (StrUtil.isBlank(strPattern) || ArrayUtil.isEmpty(argArray)) { + public static String format(String strPattern, Object... argArray) { + return formatWith(strPattern, StrUtil.EMPTY_JSON, argArray); + } + + /** + * 格式化字符串
+ * 此方法只是简单将指定占位符 按照顺序替换为参数
+ * 如果想输出占位符使用 \\转义即可,如果想输出占位符之前的 \ 使用双转义符 \\\\ 即可
+ * 例:
+ * 通常使用:format("this is {} for {}", "{}", "a", "b") =》 this is a for b
+ * 转义{}: format("this is \\{} for {}", "{}", "a", "b") =》 this is \{} for a
+ * 转义\: format("this is \\\\{} for {}", "{}", "a", "b") =》 this is \a for b
+ * + * @param strPattern 字符串模板 + * @param placeHolder 占位符,例如{} + * @param argArray 参数列表 + * @return 结果 + * @since 5.7.14 + */ + public static String formatWith(String strPattern, String placeHolder, Object... argArray) { + if (StrUtil.isBlank(strPattern) || StrUtil.isBlank(placeHolder) || ArrayUtil.isEmpty(argArray)) { return strPattern; } final int strPatternLength = strPattern.length(); + final int placeHolderLength = placeHolder.length(); // 初始化定义好的长度以获得更好的性能 - StringBuilder sbuf = new StringBuilder(strPatternLength + 50); + final StringBuilder sbuf = new StringBuilder(strPatternLength + 50); int handledPosition = 0;// 记录已经处理到的位置 int delimIndex;// 占位符所在位置 for (int argIndex = 0; argIndex < argArray.length; argIndex++) { - delimIndex = strPattern.indexOf(StrUtil.EMPTY_JSON, handledPosition); + delimIndex = strPattern.indexOf(placeHolder, handledPosition); if (delimIndex == -1) {// 剩余部分无占位符 if (handledPosition == 0) { // 不带占位符的模板直接返回 return strPattern; @@ -54,24 +73,23 @@ public class StrFormatter { // 转义符之前还有一个转义符,占位符依旧有效 sbuf.append(strPattern, handledPosition, delimIndex - 1); sbuf.append(StrUtil.utf8Str(argArray[argIndex])); - handledPosition = delimIndex + 2; + handledPosition = delimIndex + placeHolderLength; } else { // 占位符被转义 argIndex--; sbuf.append(strPattern, handledPosition, delimIndex - 1); - sbuf.append(StrUtil.C_DELIM_START); + sbuf.append(placeHolder.charAt(0)); handledPosition = delimIndex + 1; } } else {// 正常占位符 sbuf.append(strPattern, handledPosition, delimIndex); sbuf.append(StrUtil.utf8Str(argArray[argIndex])); - handledPosition = delimIndex + 2; + handledPosition = delimIndex + placeHolderLength; } } - // append the characters following the last {} pair. // 加入最后一个占位符后所有的字符 - sbuf.append(strPattern, handledPosition, strPattern.length()); + sbuf.append(strPattern, handledPosition, strPatternLength); return sbuf.toString(); } diff --git a/hutool-core/src/test/java/cn/hutool/core/lang/StrFormatterTest.java b/hutool-core/src/test/java/cn/hutool/core/lang/StrFormatterTest.java index 5803a6287..fd8ce5742 100644 --- a/hutool-core/src/test/java/cn/hutool/core/lang/StrFormatterTest.java +++ b/hutool-core/src/test/java/cn/hutool/core/lang/StrFormatterTest.java @@ -6,19 +6,34 @@ import org.junit.Test; import cn.hutool.core.text.StrFormatter; public class StrFormatterTest { - + @Test - public void formatTest(){ + public void formatTest() { //通常使用 String result1 = StrFormatter.format("this is {} for {}", "a", "b"); Assert.assertEquals("this is a for b", result1); - + //转义{} String result2 = StrFormatter.format("this is \\{} for {}", "a", "b"); Assert.assertEquals("this is {} for a", result2); - + //转义\ String result3 = StrFormatter.format("this is \\\\{} for {}", "a", "b"); Assert.assertEquals("this is \\a for b", result3); } + + @Test + public void formatWithTest() { + //通常使用 + String result1 = StrFormatter.formatWith("this is ? for ?", "?", "a", "b"); + Assert.assertEquals("this is a for b", result1); + + //转义? + String result2 = StrFormatter.formatWith("this is \\? for ?", "?", "a", "b"); + Assert.assertEquals("this is ? for a", result2); + + //转义\ + String result3 = StrFormatter.formatWith("this is \\\\? for ?", "?", "a", "b"); + Assert.assertEquals("this is \\a for b", result3); + } } From 68e5ced2bd581125314b703af28bd9dc443b71fb Mon Sep 17 00:00:00 2001 From: Looly Date: Sat, 9 Oct 2021 21:57:30 +0800 Subject: [PATCH 08/86] add test --- .../cn/hutool/core/lang/StrFormatterTest.java | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/hutool-core/src/test/java/cn/hutool/core/lang/StrFormatterTest.java b/hutool-core/src/test/java/cn/hutool/core/lang/StrFormatterTest.java index fd8ce5742..a2ad790c9 100644 --- a/hutool-core/src/test/java/cn/hutool/core/lang/StrFormatterTest.java +++ b/hutool-core/src/test/java/cn/hutool/core/lang/StrFormatterTest.java @@ -36,4 +36,19 @@ public class StrFormatterTest { String result3 = StrFormatter.formatWith("this is \\\\? for ?", "?", "a", "b"); Assert.assertEquals("this is \\a for b", result3); } + + @Test + public void formatWithTest2() { + //通常使用 + String result1 = StrFormatter.formatWith("this is $$$ for $$$", "$$$", "a", "b"); + Assert.assertEquals("this is a for b", result1); + + //转义? + String result2 = StrFormatter.formatWith("this is \\$$$ for $$$", "$$$", "a", "b"); + Assert.assertEquals("this is $$$ for a", result2); + + //转义\ + String result3 = StrFormatter.formatWith("this is \\\\$$$ for $$$", "$$$", "a", "b"); + Assert.assertEquals("this is \\a for b", result3); + } } From 27f444655abce4eb736e9f963b494fb8db10094e Mon Sep 17 00:00:00 2001 From: Looly Date: Sat, 9 Oct 2021 22:44:40 +0800 Subject: [PATCH 09/86] add shop --- README-EN.md | 6 ++++++ README.md | 6 ++++++ 2 files changed, 12 insertions(+) diff --git a/README-EN.md b/README-EN.md index 1a91bf2e4..e6c2f22ef 100644 --- a/README-EN.md +++ b/README-EN.md @@ -199,6 +199,12 @@ If you think Hutool is good, you can donate to buy tshe author a pack of chili~, [Dromara donate](https://dromara.gitee.io/donate.html) +## 👕shop + +We provide the T-Shirt with Hutool Logo, please visit the shop: + +[Hutool T-Shirt](https://m.tb.cn/h.f47W8zc?sm=7d2b95) + ## 📌WeChat Official Account #### 🐧Welcome to the official account of Hutool cooperation. diff --git a/README.md b/README.md index 27523f6ac..560430d08 100644 --- a/README.md +++ b/README.md @@ -208,6 +208,12 @@ Hutool欢迎任何人为Hutool添砖加瓦,贡献代码,不过维护者是 [捐赠给Dromara组织](https://dromara.gitee.io/donate.html) +## 👕周边 + +我们提供了印有Hutool Logo的主题T恤,欢迎点击购买: + +[HutoolT恤商店](https://m.tb.cn/h.f47W8zc?sm=7d2b95) + ## 📌公众号 #### 🧡欢迎关注Hutool合作的公众号 From 5c80f8728ebfecc633372b60bf5e3fffae7cd730 Mon Sep 17 00:00:00 2001 From: Looly Date: Sat, 9 Oct 2021 22:54:45 +0800 Subject: [PATCH 10/86] fix readme --- README-EN.md | 11 ++++------- README.md | 11 ++++------- 2 files changed, 8 insertions(+), 14 deletions(-) diff --git a/README-EN.md b/README-EN.md index e6c2f22ef..3b1cd41db 100644 --- a/README-EN.md +++ b/README-EN.md @@ -207,10 +207,7 @@ We provide the T-Shirt with Hutool Logo, please visit the shop: ## 📌WeChat Official Account -#### 🐧Welcome to the official account of Hutool cooperation. - -![Tuling](https://cdn.jsdelivr.net/gh/looly/hutool-site/images/qr_tuling.jpg) - -#### 🐧Welcome to organization Dromara - -![Dromara](https://dromara.org/img/qrcode/qrcode_1.png) \ No newline at end of file +
+ + +
\ No newline at end of file diff --git a/README.md b/README.md index 560430d08..87a2d75df 100644 --- a/README.md +++ b/README.md @@ -216,10 +216,7 @@ Hutool欢迎任何人为Hutool添砖加瓦,贡献代码,不过维护者是 ## 📌公众号 -#### 🧡欢迎关注Hutool合作的公众号 - -![图灵学院](https://cdn.jsdelivr.net/gh/looly/hutool-site/images/qr_tuling.jpg) - -#### 🧡Dromara开源组织公众号 - -![Dromara](https://dromara.org/img/qrcode/qrcode_1.png) \ No newline at end of file +
+ + +
\ No newline at end of file From 9c9a367959dd454e6d6c12ba636cce05d4c42169 Mon Sep 17 00:00:00 2001 From: Looly Date: Sat, 9 Oct 2021 22:57:42 +0800 Subject: [PATCH 11/86] update dependency --- hutool-extra/pom.xml | 6 +++--- hutool-poi/pom.xml | 2 +- pom.xml | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/hutool-extra/pom.xml b/hutool-extra/pom.xml index 5e0c1edfe..7cf5ccbf8 100644 --- a/hutool-extra/pom.xml +++ b/hutool-extra/pom.xml @@ -19,7 +19,7 @@ 2.3 - 3.6.1.RELEASE + 3.7.0.RELEASE 1.4.1 2.3.31 4.9.16 @@ -30,7 +30,7 @@ 3.7.2 5.1.1 4.0.1 - 2.5.4 + 2.5.5 3.3.0 @@ -241,7 +241,7 @@ org.apache.lucene lucene-analyzers-smartcn - 8.9.0 + 8.10.0 true diff --git a/hutool-poi/pom.xml b/hutool-poi/pom.xml index 82d2f4209..b8b40c92b 100644 --- a/hutool-poi/pom.xml +++ b/hutool-poi/pom.xml @@ -45,7 +45,7 @@ org.ofdrw ofdrw-full - 1.15.1 + 1.15.5 compile true diff --git a/pom.xml b/pom.xml index 75f618692..79aec1cba 100644 --- a/pom.xml +++ b/pom.xml @@ -44,7 +44,7 @@ 8 4.13.2 - 1.18.20 + 1.18.22 From 756b1ab38e6e397624f37c9fd95f6caa8c57c7d7 Mon Sep 17 00:00:00 2001 From: Looly Date: Sat, 9 Oct 2021 23:01:15 +0800 Subject: [PATCH 12/86] fix readme --- README-EN.md | 4 ++++ README.md | 4 ++++ 2 files changed, 8 insertions(+) diff --git a/README-EN.md b/README-EN.md index 3b1cd41db..55b14ae82 100644 --- a/README-EN.md +++ b/README-EN.md @@ -205,6 +205,10 @@ We provide the T-Shirt with Hutool Logo, please visit the shop: [Hutool T-Shirt](https://m.tb.cn/h.f47W8zc?sm=7d2b95) +
+ +
+ ## 📌WeChat Official Account
diff --git a/README.md b/README.md index 87a2d75df..fd0baac18 100644 --- a/README.md +++ b/README.md @@ -214,6 +214,10 @@ Hutool欢迎任何人为Hutool添砖加瓦,贡献代码,不过维护者是 [HutoolT恤商店](https://m.tb.cn/h.f47W8zc?sm=7d2b95) +
+ +
+ ## 📌公众号
From 463246496cf775c2dcbf82a649d9db8968c4eec9 Mon Sep 17 00:00:00 2001 From: Looly Date: Sat, 9 Oct 2021 23:29:27 +0800 Subject: [PATCH 13/86] =?UTF-8?q?=F0=9F=91=95release=205.7.14?= 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 15601f7d5..7466d7fbb 100644 --- a/hutool-all/pom.xml +++ b/hutool-all/pom.xml @@ -9,7 +9,7 @@ cn.hutool hutool-parent - 5.7.14-SNAPSHOT + 5.7.14 hutool-all diff --git a/hutool-aop/pom.xml b/hutool-aop/pom.xml index fe27b1a20..8aeb93af5 100644 --- a/hutool-aop/pom.xml +++ b/hutool-aop/pom.xml @@ -9,7 +9,7 @@ cn.hutool hutool-parent - 5.7.14-SNAPSHOT + 5.7.14 hutool-aop diff --git a/hutool-bloomFilter/pom.xml b/hutool-bloomFilter/pom.xml index 4caef1e5c..6d5aca57a 100644 --- a/hutool-bloomFilter/pom.xml +++ b/hutool-bloomFilter/pom.xml @@ -9,7 +9,7 @@ cn.hutool hutool-parent - 5.7.14-SNAPSHOT + 5.7.14 hutool-bloomFilter diff --git a/hutool-bom/pom.xml b/hutool-bom/pom.xml index f2d8e0a81..efa4b7d42 100644 --- a/hutool-bom/pom.xml +++ b/hutool-bom/pom.xml @@ -9,7 +9,7 @@ cn.hutool hutool-parent - 5.7.14-SNAPSHOT + 5.7.14 hutool-bom diff --git a/hutool-cache/pom.xml b/hutool-cache/pom.xml index 7f50b660e..42faadbad 100644 --- a/hutool-cache/pom.xml +++ b/hutool-cache/pom.xml @@ -9,7 +9,7 @@ cn.hutool hutool-parent - 5.7.14-SNAPSHOT + 5.7.14 hutool-cache diff --git a/hutool-captcha/pom.xml b/hutool-captcha/pom.xml index 2f8960c18..0636c9a62 100644 --- a/hutool-captcha/pom.xml +++ b/hutool-captcha/pom.xml @@ -9,7 +9,7 @@ cn.hutool hutool-parent - 5.7.14-SNAPSHOT + 5.7.14 hutool-captcha diff --git a/hutool-core/pom.xml b/hutool-core/pom.xml index 3ccf717a2..817ceed23 100644 --- a/hutool-core/pom.xml +++ b/hutool-core/pom.xml @@ -9,7 +9,7 @@ cn.hutool hutool-parent - 5.7.14-SNAPSHOT + 5.7.14 hutool-core diff --git a/hutool-cron/pom.xml b/hutool-cron/pom.xml index 76a5a1fcc..2f6d5502c 100644 --- a/hutool-cron/pom.xml +++ b/hutool-cron/pom.xml @@ -9,7 +9,7 @@ cn.hutool hutool-parent - 5.7.14-SNAPSHOT + 5.7.14 hutool-cron diff --git a/hutool-crypto/pom.xml b/hutool-crypto/pom.xml index 37cc747eb..b35241605 100644 --- a/hutool-crypto/pom.xml +++ b/hutool-crypto/pom.xml @@ -9,7 +9,7 @@ cn.hutool hutool-parent - 5.7.14-SNAPSHOT + 5.7.14 hutool-crypto diff --git a/hutool-db/pom.xml b/hutool-db/pom.xml index e5f1f2414..2d0c46b64 100644 --- a/hutool-db/pom.xml +++ b/hutool-db/pom.xml @@ -9,7 +9,7 @@ cn.hutool hutool-parent - 5.7.14-SNAPSHOT + 5.7.14 hutool-db diff --git a/hutool-dfa/pom.xml b/hutool-dfa/pom.xml index 87ea5c8e1..a2a5ad72d 100644 --- a/hutool-dfa/pom.xml +++ b/hutool-dfa/pom.xml @@ -9,7 +9,7 @@ cn.hutool hutool-parent - 5.7.14-SNAPSHOT + 5.7.14 hutool-dfa diff --git a/hutool-extra/pom.xml b/hutool-extra/pom.xml index 7cf5ccbf8..141c2fd16 100644 --- a/hutool-extra/pom.xml +++ b/hutool-extra/pom.xml @@ -9,7 +9,7 @@ cn.hutool hutool-parent - 5.7.14-SNAPSHOT + 5.7.14 hutool-extra diff --git a/hutool-http/pom.xml b/hutool-http/pom.xml index a6906caff..5778eff37 100644 --- a/hutool-http/pom.xml +++ b/hutool-http/pom.xml @@ -9,7 +9,7 @@ cn.hutool hutool-parent - 5.7.14-SNAPSHOT + 5.7.14 hutool-http diff --git a/hutool-json/pom.xml b/hutool-json/pom.xml index 8ea5bd302..ed5f98168 100644 --- a/hutool-json/pom.xml +++ b/hutool-json/pom.xml @@ -9,7 +9,7 @@ cn.hutool hutool-parent - 5.7.14-SNAPSHOT + 5.7.14 hutool-json diff --git a/hutool-jwt/pom.xml b/hutool-jwt/pom.xml index 02f05e5a4..c3c8e1a66 100644 --- a/hutool-jwt/pom.xml +++ b/hutool-jwt/pom.xml @@ -9,7 +9,7 @@ cn.hutool hutool-parent - 5.7.14-SNAPSHOT + 5.7.14 hutool-jwt diff --git a/hutool-log/pom.xml b/hutool-log/pom.xml index bce009c70..6f0a6fb6a 100644 --- a/hutool-log/pom.xml +++ b/hutool-log/pom.xml @@ -9,7 +9,7 @@ cn.hutool hutool-parent - 5.7.14-SNAPSHOT + 5.7.14 hutool-log diff --git a/hutool-poi/pom.xml b/hutool-poi/pom.xml index b8b40c92b..931bc7d42 100644 --- a/hutool-poi/pom.xml +++ b/hutool-poi/pom.xml @@ -9,7 +9,7 @@ cn.hutool hutool-parent - 5.7.14-SNAPSHOT + 5.7.14 hutool-poi diff --git a/hutool-script/pom.xml b/hutool-script/pom.xml index 338165c67..d76f4aa4c 100644 --- a/hutool-script/pom.xml +++ b/hutool-script/pom.xml @@ -9,7 +9,7 @@ cn.hutool hutool-parent - 5.7.14-SNAPSHOT + 5.7.14 hutool-script diff --git a/hutool-setting/pom.xml b/hutool-setting/pom.xml index e706ad1b4..a79515e51 100644 --- a/hutool-setting/pom.xml +++ b/hutool-setting/pom.xml @@ -9,7 +9,7 @@ cn.hutool hutool-parent - 5.7.14-SNAPSHOT + 5.7.14 hutool-setting diff --git a/hutool-socket/pom.xml b/hutool-socket/pom.xml index 198d7f30c..dce985061 100644 --- a/hutool-socket/pom.xml +++ b/hutool-socket/pom.xml @@ -9,7 +9,7 @@ cn.hutool hutool-parent - 5.7.14-SNAPSHOT + 5.7.14 hutool-socket diff --git a/hutool-system/pom.xml b/hutool-system/pom.xml index fa2548dbf..9d9645c60 100644 --- a/hutool-system/pom.xml +++ b/hutool-system/pom.xml @@ -9,7 +9,7 @@ cn.hutool hutool-parent - 5.7.14-SNAPSHOT + 5.7.14 hutool-system diff --git a/pom.xml b/pom.xml index 79aec1cba..e73b78ebf 100644 --- a/pom.xml +++ b/pom.xml @@ -8,7 +8,7 @@ cn.hutool hutool-parent - 5.7.14-SNAPSHOT + 5.7.14 hutool Hutool是一个小而全的Java工具类库,通过静态方法封装,降低相关API的学习成本,提高工作效率,使Java拥有函数式语言般的优雅,让Java语言也可以“甜甜的”。 https://github.com/dromara/hutool From b1e8eed3087130e5da3beb797916b54c3359a27f Mon Sep 17 00:00:00 2001 From: Looly Date: Sat, 9 Oct 2021 23:32:56 +0800 Subject: [PATCH 14/86] change readme --- README-EN.md | 2 +- README.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/README-EN.md b/README-EN.md index 55b14ae82..817081b8c 100644 --- a/README-EN.md +++ b/README-EN.md @@ -206,7 +206,7 @@ We provide the T-Shirt with Hutool Logo, please visit the shop: [Hutool T-Shirt](https://m.tb.cn/h.f47W8zc?sm=7d2b95)
- +
## 📌WeChat Official Account diff --git a/README.md b/README.md index fd0baac18..62b43a57a 100644 --- a/README.md +++ b/README.md @@ -215,7 +215,7 @@ Hutool欢迎任何人为Hutool添砖加瓦,贡献代码,不过维护者是 [HutoolT恤商店](https://m.tb.cn/h.f47W8zc?sm=7d2b95)
- +
## 📌公众号 From 0e9893d365434cecba0eee8b8babcf273130e676 Mon Sep 17 00:00:00 2001 From: Looly Date: Sat, 9 Oct 2021 23:56:44 +0800 Subject: [PATCH 15/86] 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 817081b8c..9783a1a9f 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 62b43a57a..f9ea97fd9 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 2ad2244f6..7b9e0e8bb 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 71f2db4e9..36f2fde5c 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 7466d7fbb..6d1004deb 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 8aeb93af5..aa0600692 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 6d5aca57a..8554dd99c 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 efa4b7d42..5d56cbcc3 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 42faadbad..c2081d8e2 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 0636c9a62..68df03a4a 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 817ceed23..af165e71e 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 2f6d5502c..0b753dbd7 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 b35241605..96c365b9e 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 2d0c46b64..b434f61d5 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 a2a5ad72d..77f87ced8 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 141c2fd16..dee8ad251 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 5778eff37..5e8f4ad11 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 ed5f98168..53479ea03 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 c3c8e1a66..94f10eb9d 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 6f0a6fb6a..e2e516e6b 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 931bc7d42..a3e0cc10b 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 d76f4aa4c..334119e1c 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 a79515e51..346588b87 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 dce985061..0f023e57b 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 9d9645c60..0a2badeac 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 e73b78ebf..7341d1fa8 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 16/86] prepare 5.7.15 --- CHANGELOG.md | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index dbc471984..3957c6130 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 17/86] prepare 5.7.15 --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3957c6130..7ee4ac3f2 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 18/86] 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 1a5e2df3e..6da47a54a 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.CollUtil; 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 class StrJoinerTest { 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 19/86] 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 7ee4ac3f2..4c45814e5 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 4af1b1dcb..f1b3f23aa 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.sql.Wrapper; 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 class Db extends AbstractDb { /** * 创建Db
* 使用默认数据源,自动探测数据库连接池 - * + * * @param group 数据源分组 * @return Db */ @@ -47,7 +46,7 @@ public class Db extends AbstractDb { /** * 创建Db
* 会根据数据源连接的元信息识别目标数据库类型,进而使用合适的数据源 - * + * * @param ds 数据源 * @return Db */ @@ -57,7 +56,7 @@ public class Db extends AbstractDb { /** * 创建Db - * + * * @param ds 数据源 * @param dialect 方言 * @return Db @@ -68,7 +67,7 @@ public class Db extends AbstractDb { /** * 创建Db - * + * * @param ds 数据源 * @param driverClassName 数据库连接驱动类名 * @return Db @@ -80,7 +79,7 @@ public class Db extends AbstractDb { // ---------------------------------------------------------------------------- Constructor start /** * 构造,从DataSource中识别方言 - * + * * @param ds 数据源 */ public Db(DataSource ds) { @@ -89,7 +88,7 @@ public class Db extends AbstractDb { /** * 构造 - * + * * @param ds 数据源 * @param driverClassName 数据库连接驱动类名,用于识别方言 */ @@ -99,7 +98,7 @@ public class Db extends AbstractDb { /** * 构造 - * + * * @param ds 数据源 * @param dialect 方言 */ @@ -118,7 +117,7 @@ public class Db extends AbstractDb { public Db setWrapper(Wrapper wrapper) { return (Db) super.setWrapper(wrapper); } - + @Override public Db disableWrapper() { return (Db)super.disableWrapper(); @@ -147,7 +146,7 @@ public class Db extends AbstractDb { /** * 执行事务,使用默认的事务级别
* 在同一事务中,所有对数据库操作都是原子的,同时提交或者同时回滚 - * + * * @param func 事务函数,所有操作应在同一函数下执行,确保在同一事务中 * @return this * @throws SQLException SQL异常 @@ -159,7 +158,7 @@ public class Db extends AbstractDb { /** * 执行事务
* 在同一事务中,所有对数据库操作都是原子的,同时提交或者同时回滚 - * + * * @param transactionLevel 事务级别枚举,null表示使用JDBC默认事务 * @param func 事务函数,所有操作应在同一函数下执行,确保在同一事务中 * @return this @@ -208,7 +207,7 @@ public class Db extends AbstractDb { // ---------------------------------------------------------------------------- Private method start /** * 静默回滚事务 - * + * * @param conn Connection */ private void quietRollback(Connection conn) { @@ -223,12 +222,12 @@ public class Db extends AbstractDb { /** * 静默设置自动提交 - * + * * @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 @@ public class Db extends AbstractDb { } } // ---------------------------------------------------------------------------- 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 20/86] 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 6da47a54a..2ce2eee8c 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.CollUtil; 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 class StrJoinerTest { 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 21/86] 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 ed6e57b2d..358a8e272 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 22/86] 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 4c45814e5..1d752a394 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 2083ced3d..7ab4a9c23 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 class CollUtil { * @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 ef0991fc8..c538e317c 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 class IterUtil { * @return Iterable对象的元素数量 * @since 5.5.0 */ - public static int size(final Iterable iterable) { + public static int size(Iterable iterable) { if (null == iterable) { return 0; } @@ -838,7 +838,7 @@ public class IterUtil { * @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 class IterUtil { * @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 23/86] 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 1d752a394..6c772c812 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 e6a11c2d8..02663d60b 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.FileUtil; 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 class ExcelWriter extends ExcelBase { 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 ad5c3c5fc..d33fc890b 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 class ExcelWriteTest { 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 24/86] 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 000000000..d0c2c5589 --- /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 48962fe5f..b27d86338 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 @@ import java.util.zip.ZipOutputStream; 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 97bb6af52..c2a2f5bd4 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 25/86] 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 000000000..1f58c7a8f --- /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 26/86] 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 f2aa714e8..b3df4faff 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 class JWTValidatorTest { 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 27/86] =?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 1efdd5aef..e97735163 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 @@ import java.util.function.Supplier; */ 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 class Assert { 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 class Assert { * @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 class Assert { * @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 class Assert { * @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 class Assert { 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 28/86] =?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 000000000..31c9ebc7a --- /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 000000000..7ea1df65e --- /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 29/86] 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 6c772c812..905612610 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 31c9ebc7a..b997b1428 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 @@ import java.util.concurrent.atomic.AtomicLong; */ 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 class RingIndexUtil { 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 class RingIndexUtil { 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 class RingIndexUtil { } } } - } 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 7ea1df65e..4ff70cdd1 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 class RingIndexUtilTest { 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 30/86] 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 905612610..f854a8372 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 e97735163..fd4751d6a 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 class Assert { /** * 检查值是否在指定范围内 * - * @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 class Assert { /** * 检查值是否在指定范围内 * - * @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 class Assert { /** * 检查值是否在指定范围内 * - * @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 aae07f8be..8d77384e7 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 class RandomUtil { 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 42ba46299..978fcd2ed 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 class RandomUtilTest { 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 31/86] =?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 fcad035a5..a5f28313c 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.RegexPool; 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 class ReUtil { 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 4c392edc0..583274fd8 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 org.junit.Test; 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 class ReUtilTest { "(.+?)省(.+?)市(.+?)区", "广东省深圳市南山区"); 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 32/86] 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 f854a8372..7a583c895 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 f9b37927f..6758c3768 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 class DateTime extends Date { * @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 class DateTime extends Date { * @since 5.0.0 */ public DateTime(TemporalAccessor temporalAccessor) { - this(DateUtil.toInstant(temporalAccessor)); + this(TemporalAccessorUtil.toInstant(temporalAccessor)); } /** @@ -276,7 +276,7 @@ public class DateTime extends Date { * @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 f2918c55e..1d9baef32 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 class TemporalAccessorUtil extends TemporalUtil{ // 指定本地时间转换 为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 000000000..1b12788da --- /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 05c674cf5..738995533 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 class DateUtilTest { 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 33/86] 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 000000000..65dc37a5f --- /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 34/86] 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 7a583c895..a1df0b9d1 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 a5f28313c..19bced433 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.PatternPool; 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 class ReUtil { 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 class ReUtil { /** * 获得匹配的字符串 * - * @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 class ReUtil { 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 class ReUtil { 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 class ReUtil { /** * 替换匹配的第一个内容 * - * @param pattern 正则 - * @param content 被匹配的内容 + * @param pattern 正则 + * @param content 被匹配的内容 * @param replacement 替换的内容 * @return 替换后剩余的内容 * @since 5.6.5 @@ -399,7 +407,7 @@ public class ReUtil { 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 class ReUtil { /** * 找到指定正则匹配到字符串的开始位置 * - * @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 class ReUtil { * @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 class ReUtil { /** * 找到指定正则匹配到第一个字符串的位置 * - * @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 class ReUtil { * @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 583274fd8..0b26e6241 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 class ReUtilTest { 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 class ReUtilTest { 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 35/86] 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 2edf65727..1afbfbd18 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 @@ package cn.hutool.core.lang.mutable; import java.io.Serializable; /** - * 可变 boolean 类型 + * 可变 {@code boolean} 类型 * * @see Boolean * @since 3.0.1 @@ -59,12 +59,12 @@ public class MutableBool implements Comparable, Mutable, S * 相等需同时满足如下条件: *
    *
  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 class MutableBool implements Comparable, Mutable, S /** * 比较 * - * @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 0d0755bf8..55d539fc4 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 @@ package cn.hutool.core.lang.mutable; import cn.hutool.core.util.NumberUtil; /** - * 可变 byte 类型 + * 可变 {@code byte} 类型 * * @see Byte * @since 3.0.1 @@ -157,12 +157,12 @@ public class MutableByte extends Number implements Comparable, Muta * 相等需同时满足如下条件: *
    *
  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 class MutableByte extends Number implements Comparable, Muta /** * 比较 * - * @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 b516c454e..72a490366 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 @@ package cn.hutool.core.lang.mutable; 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 19bced433..e9212367f 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 @@ package cn.hutool.core.util; 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.List; 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 class ReUtil { 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 class ReUtil { 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 class ReUtil { 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 class ReUtil { 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 class ReUtil { 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 class ReUtil { 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 class ReUtil { 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 class ReUtil { 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 class ReUtil { 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 class ReUtil { 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 class ReUtil { 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 36/86] 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 f08548ee5..c57ef2488 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 37/86] 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 a1df0b9d1..99d34efd7 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 fdfafabc7..3cdea5a83 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 @@ package cn.hutool.json; 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.GlobalSerializeMapping; 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 class JSONArray implements JSON, JSONGetter, List, Rando 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 841d043c3..7358cedec 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.BeanCopier; 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.JSONObjectSerializer; 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 class JSONObject implements JSON, JSONGetter, Map 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 c4f8c6b6a..2b3522b9a 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 @@ public class JSONArrayTest { 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 03ce35541..f2983b859 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 class JSONObjectTest { 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 38/86] 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 65dc37a5f..502bf09be 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 @@ package cn.hutool.core.io.file; 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 class FileSystemUtil { } } + /** + * 创建 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 d9423d126..95abe2c52 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 class PathUtil { } } + /** + * 递归遍历目录以及子目录中的所有文件
+ * 如果提供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 000000000..130b400f8 --- /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 39/86] 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 c40ded1cd..064642772 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.FastByteArrayOutputStream; 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 class ZipUtil { } } + /** + * 在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 40/86] 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 000000000..9046324f4 --- /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 41/86] 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 99d34efd7..ad080878e 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 80dfb4e4d..301d39309 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 class ExcelReader extends ExcelBase { 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 4326a8608..ebe04bf0f 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.io.resource.ResourceUtil; 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 class ExcelReadTest { 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 42/86] 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 ad080878e..baa866a4b 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 d6d998267..29064428c 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 enum ContentType { 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 43/86] 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 842dbff1e..e8bd619be 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 class CopyVisitor extends SimpleFileVisitor { } 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 class CopyVisitor extends SimpleFileVisitor { @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 44/86] =?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 000000000..1d03c44eb --- /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 45/86] 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 baa866a4b..8cde66ed3 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 6999922ee..6bb549689 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 @@ package cn.hutool.cache.impl; 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.ConcurrentHashMap; 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 abstract class AbstractCache implements Cache { 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 abstract class AbstractCache implements Cache { * @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 @@ public abstract class AbstractCache implements Cache { // ---------------------------------------------------------------- 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 abstract class AbstractCache implements Cache { } 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 abstract class AbstractCache implements Cache { 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 abstract class AbstractCache implements Cache { * @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 abstract class AbstractCache implements Cache { 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 @@ public abstract class AbstractCache implements Cache { } } - /** - * 移除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 @@ public abstract class AbstractCache implements Cache { * @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 c403ae10c..558fed75f 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 @@ import java.util.LinkedHashMap; * @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 32709ed6b..4b1e2608a 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 @@ import java.util.Iterator; * @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 b385b57fe..29f83f02f 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 @@ import java.util.Iterator; * @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 000000000..180bc7785 --- /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 000000000..79534f2bf --- /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 5e094875d..e03ede728 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 @@ import java.util.concurrent.ScheduledFuture; * @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 000000000..6ccc2db2f --- /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 46/86] 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 8cde66ed3..af022c29e 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 b9df3e891..90ef70540 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 class KeyUtil { */ 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 class KeyUtil { */ 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 47/86] 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 064642772..dc8d55230 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.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.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 class ZipUtil { 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 48/86] 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 0246f6688..acd389ab1 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.FileOutputStream; 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 @@ import java.nio.charset.Charset; */ 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 000000000..8d1c2fee6 --- /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 000000000..e69de29bb 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 49/86] 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 af022c29e..61d3f2573 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 8c3974915..1a41d7282 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 class CharSequenceUtil { /** * 切分字符串 * - * @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 ec3d4aa45..d9333e61c 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 class StrSplitter { /** * 切分字符串,忽略大小写 * - * @param str 被切分的字符串 + * @param text 被切分的字符串 * @param separator 分隔符字符 * @param limit 限制分片数,-1不限制 * @param isTrim 是否去除切分字符串后每个元素两边的空格 @@ -154,8 +155,8 @@ public class StrSplitter { * @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 class StrSplitter { /** * 切分字符串 * - * @param 切分后的元素类型 + * @param 切分后的元素类型 * @param text 被切分的字符串 * @param separator 分隔符字符 * @param limit 限制分片数,-1不限制 @@ -204,7 +205,7 @@ public class StrSplitter { * @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 class StrSplitter { * @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 class StrSplitter { * @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 class StrSplitter { * @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 class StrSplitter { * @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 class StrSplitter { * @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 class StrSplitter { * @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 class StrSplitter { * @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 class StrSplitter { * @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 class StrSplitter { * @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 class StrSplitter { /** * 通过正则切分字符串 * - * @param str 字符串 + * @param text 字符串 * @param separatorRegex 分隔符正则 * @param limit 限制分片数 * @param isTrim 是否去除切分字符串后每个元素两边的空格 @@ -370,9 +372,9 @@ public class StrSplitter { * @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 class StrSplitter { * @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 c6d977696..847a23ce3 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 class SplitIter extends ComputeIter implements Serializable { @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 73d8e6e7f..b0aca478c 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 class StrUtilTest { 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 50/86] 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 61d3f2573..65a3e85e4 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 5d56cbcc3..5966acbff 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 51/86] 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 e7c9cc9fc..9ab8278b3 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 3479c17b8..02699d4a4 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 class IoUtil extends NioUtil { * @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 61c9f9ba6..aad2da056 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 @@ public class CityHash { 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 @@ public class CityHash { // 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 @@ public class CityHash { 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 000000000..8ad0a9e35 --- /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 af5dfc496..b16b4c9ce 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 @@ package cn.hutool.core.lang.hash; * @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 b0aaa8ced..7a9016697 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 @@ package cn.hutool.core.lang.hash; * @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 9feac2969..61a50e4bd 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 @@ package cn.hutool.core.lang.hash; * @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 9af5bad4d..4f6301095 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 @@ package cn.hutool.core.lang.hash; * @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 class Number128 { 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 52/86] add methods --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 65a3e85e4..55fc99699 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 53/86] 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 55fc99699..688101d0d 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 000000000..3423097be --- /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 95abe2c52..1c3ed2d92 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 class PathUtil { 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 e8bd619be..b36e8c193 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 @@ import java.nio.file.attribute.BasicFileAttributes; */ 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 class CopyVisitor extends SimpleFileVisitor { @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 dc8d55230..a813242bf 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 @@ package cn.hutool.core.util; 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.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.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.IOException; 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 @@ import java.util.zip.ZipOutputStream; /** * 压缩工具类 * - * @see cn.hutool.core.compress.ZipWriter * @author Looly + * @see cn.hutool.core.compress.ZipWriter */ public class ZipUtil { @@ -84,25 +84,29 @@ public class ZipUtil { } /** - * 在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 class ZipUtil { */ @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 class ZipUtil { 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 class ZipUtil { 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 class ZipUtil { 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 class ZipUtil { 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 class ZipUtil { * @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 class ZipUtil { * @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 class ZipUtil { * @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 class ZipUtil { * @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 acd389ab1..e224e359a 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 @@ import java.util.List; 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 class ZipUtilTest { 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 e69de29bb..56a6051ca 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 54/86] 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 688101d0d..cd6f79271 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 7ab4a9c23..94e992aa1 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.CopyOnWriteArrayList; 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 class CollUtil { * @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 class CollUtil { /** * 截取集合的部分 * - * @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 class CollUtil { 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 class CollUtil { 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 class CollUtil { * @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 55/86] 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 a74af7073..81f74df28 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 class Ftp extends AbstractFtp { * 上传文件到指定目录,可选: * *
-	 * 1. path为null或""上传到当前路径
-	 * 2. path为相对路径则相对于当前路径的子路径
-	 * 3. path为绝对路径则上传到此路径
+	 * 1. destPath为null或""上传到当前路径
+	 * 2. destPath为相对路径则相对于当前路径的子路径
+	 * 3. destPath为绝对路径则上传到此路径
 	 * 
* * @param destPath 服务端路径,可以为{@code null} 或者相对路径或绝对路径 @@ -495,14 +495,14 @@ public class Ftp extends AbstractFtp { * * * @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 class Ftp extends AbstractFtp { * 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 class Ftp extends AbstractFtp { 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 56/86] 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 cd6f79271..5ea43ec0b 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 9783a1a9f..5576237e4 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 f9ea97fd9..99967c3f0 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 1b77c2ba6..ec5b018f6 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 class Base64 { * 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 abc31d182..ba92ab553 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 class SymmetricCrypto implements SymmetricEncryptor, SymmetricDecryptor, 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 class SymmetricCrypto implements SymmetricEncryptor, SymmetricDecryptor, 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 80c7995fe..c98dd21ee 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 class SmTest { 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 000000000..87ec2675c --- /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 57/86] 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 6265b6ca0..1cba5c565 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 cn.hutool.extra.ssh.Sftp; import org.junit.Ignore; import org.junit.Test; -import java.util.List; - public class FtpTest { @Test @@ -25,12 +23,9 @@ public class FtpTest { @Test @Ignore public void uploadTest() { - Ftp ftp = new Ftp("looly.centos"); + Ftp ftp = new Ftp("localhost"); - List ls = ftp.ls("/file"); - Console.log(ls); - - 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 58/86] 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 5ea43ec0b..ad9219221 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 84b99c239..796124648 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 class BeanUtil { ).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 fc6c3947d..bcfae58a1 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 @@ public class BeanCopier implements Copier, Serializable { 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 @@ public class BeanCopier implements Copier, Serializable { 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 502ea4846..4cab13809 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 cn.hutool.core.util.ObjectUtil; 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 class CopyOptions implements Serializable { 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 class CopyOptions implements Serializable { * 当非反向,则根据源字段名获取目标字段名,反之根据目标字段名获取源字段名。 * * @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 @@ public class CopyOptions implements Serializable { /** * 转换字段名为编辑后的字段名 + * * @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 @@ public class CopyOptions implements Serializable { * @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 526e8bbca..d05bc13b0 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 class BeanUtilTest { 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 59/86] 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 ad9219221..edb403142 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 357a215b8..be6d5c94b 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 final class UrlBuilder implements Serializable { */ 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 3c297b9de..8f882d9f5 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 @@ public class UrlQuery { 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 f53085fc6..b88687dff 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 class UrlBuilderTest { 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 60/86] 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 e224e359a..a98972e67 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 class ZipUtilTest { } } + @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 61/86] 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 5576237e4..d11d3dfaa 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 99967c3f0..4bfc648e6 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 62/86] 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 b88687dff..78e4d93ee 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 class UrlBuilderTest { "&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 class UrlBuilderTest { 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 63/86] 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 2b3522b9a..b6e83e0d8 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 class JSONArrayTest { 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 64/86] 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 b434f61d5..2c43499b3 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 dee8ad251..0c2d6dc11 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 a3e0cc10b..7352973cf 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 65/86] =?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 6d1004deb..f4cab970a 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 aa0600692..bc7be21a4 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 8554dd99c..b7ed8a410 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 5966acbff..402341d6c 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 c2081d8e2..5bfe068ca 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 68df03a4a..e1293b7d3 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 af165e71e..5956c0bb2 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 0b753dbd7..62dc9962c 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 96c365b9e..3a89f935d 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 2c43499b3..dcfd6d332 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 77f87ced8..65d431f9e 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 0c2d6dc11..9d7626fcf 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 5e8f4ad11..d8c8d1f83 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 53479ea03..96e3feb61 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 94f10eb9d..d50aaa75f 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 e2e516e6b..ba5ea9f7e 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 7352973cf..e1cb315c7 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 334119e1c..56c4429c6 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 346588b87..81491727d 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 0f023e57b..afc15da80 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 0a2badeac..f89f373b9 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 7341d1fa8..7ef421831 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 From 41e488a36be90f5ca47969f1d901df9e2571d3bc Mon Sep 17 00:00:00 2001 From: Looly Date: Thu, 21 Oct 2021 23:40:53 +0800 Subject: [PATCH 66/86] prepare 5.7.16 --- CHANGELOG.md | 8 ++++++++ README-EN.md | 6 +++--- README.md | 6 +++--- bin/version.txt | 2 +- docs/js/version.js | 2 +- hutool-all/pom.xml | 2 +- hutool-aop/pom.xml | 2 +- hutool-bloomFilter/pom.xml | 2 +- hutool-bom/pom.xml | 2 +- hutool-cache/pom.xml | 2 +- hutool-captcha/pom.xml | 2 +- hutool-core/pom.xml | 2 +- hutool-cron/pom.xml | 2 +- hutool-crypto/pom.xml | 2 +- hutool-db/pom.xml | 2 +- hutool-dfa/pom.xml | 2 +- hutool-extra/pom.xml | 2 +- hutool-http/pom.xml | 2 +- hutool-json/pom.xml | 2 +- hutool-jwt/pom.xml | 2 +- hutool-log/pom.xml | 2 +- hutool-poi/pom.xml | 2 +- hutool-script/pom.xml | 2 +- hutool-setting/pom.xml | 2 +- hutool-socket/pom.xml | 2 +- hutool-system/pom.xml | 2 +- pom.xml | 2 +- 27 files changed, 38 insertions(+), 30 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index edb403142..bda7041ee 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,14 @@ ------------------------------------------------------------------------------------------------------------- +# 5.7.16 (2021-10-21) + +### 🐣新特性 + +### 🐞Bug修复 + +------------------------------------------------------------------------------------------------------------- + # 5.7.15 (2021-10-21) ### 🐣新特性 diff --git a/README-EN.md b/README-EN.md index d11d3dfaa..f73d4c304 100644 --- a/README-EN.md +++ b/README-EN.md @@ -142,18 +142,18 @@ We provide the T-Shirt and Sweater with Hutool Logo, please visit the shop: cn.hutool hutool-all - 5.7.15 + 5.7.16 ``` ### 🍐Gradle ``` -implementation 'cn.hutool:hutool-all:5.7.15' +implementation 'cn.hutool:hutool-all:5.7.16' ``` ## 📥Download -- [Maven Repo](https://repo1.maven.org/maven2/cn/hutool/hutool-all/5.7.15/) +- [Maven Repo](https://repo1.maven.org/maven2/cn/hutool/hutool-all/5.7.16/) > 🔔️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 4bfc648e6..ae1aea6e8 100644 --- a/README.md +++ b/README.md @@ -142,20 +142,20 @@ Hutool的存在就是为了减少代码搜索成本,避免网络上参差不 cn.hutool hutool-all - 5.7.15 + 5.7.16 ``` ### 🍐Gradle ``` -implementation 'cn.hutool:hutool-all:5.7.15' +implementation 'cn.hutool:hutool-all:5.7.16' ``` ### 📥下载jar 点击以下链接,下载`hutool-all-X.X.X.jar`即可: -- [Maven中央库](https://repo1.maven.org/maven2/cn/hutool/hutool-all/5.7.15/) +- [Maven中央库](https://repo1.maven.org/maven2/cn/hutool/hutool-all/5.7.16/) > 🔔️注意 > Hutool 5.x支持JDK8+,对Android平台没有测试,不能保证所有工具类或工具方法可用。 diff --git a/bin/version.txt b/bin/version.txt index 7b9e0e8bb..e8406ce30 100755 --- a/bin/version.txt +++ b/bin/version.txt @@ -1 +1 @@ -5.7.15 +5.7.16 diff --git a/docs/js/version.js b/docs/js/version.js index 36f2fde5c..3e6f04e99 100644 --- a/docs/js/version.js +++ b/docs/js/version.js @@ -1 +1 @@ -var version = '5.7.15' \ No newline at end of file +var version = '5.7.16' \ No newline at end of file diff --git a/hutool-all/pom.xml b/hutool-all/pom.xml index f4cab970a..b97625af4 100644 --- a/hutool-all/pom.xml +++ b/hutool-all/pom.xml @@ -9,7 +9,7 @@ cn.hutool hutool-parent - 5.7.15 + 5.7.16-SNAPSHOT hutool-all diff --git a/hutool-aop/pom.xml b/hutool-aop/pom.xml index bc7be21a4..5590a0b53 100644 --- a/hutool-aop/pom.xml +++ b/hutool-aop/pom.xml @@ -9,7 +9,7 @@ cn.hutool hutool-parent - 5.7.15 + 5.7.16-SNAPSHOT hutool-aop diff --git a/hutool-bloomFilter/pom.xml b/hutool-bloomFilter/pom.xml index b7ed8a410..96f02bf66 100644 --- a/hutool-bloomFilter/pom.xml +++ b/hutool-bloomFilter/pom.xml @@ -9,7 +9,7 @@ cn.hutool hutool-parent - 5.7.15 + 5.7.16-SNAPSHOT hutool-bloomFilter diff --git a/hutool-bom/pom.xml b/hutool-bom/pom.xml index 402341d6c..a484343e3 100644 --- a/hutool-bom/pom.xml +++ b/hutool-bom/pom.xml @@ -9,7 +9,7 @@ cn.hutool hutool-parent - 5.7.15 + 5.7.16-SNAPSHOT hutool-bom diff --git a/hutool-cache/pom.xml b/hutool-cache/pom.xml index 5bfe068ca..7e84b9cea 100644 --- a/hutool-cache/pom.xml +++ b/hutool-cache/pom.xml @@ -9,7 +9,7 @@ cn.hutool hutool-parent - 5.7.15 + 5.7.16-SNAPSHOT hutool-cache diff --git a/hutool-captcha/pom.xml b/hutool-captcha/pom.xml index e1293b7d3..b22ac7ab2 100644 --- a/hutool-captcha/pom.xml +++ b/hutool-captcha/pom.xml @@ -9,7 +9,7 @@ cn.hutool hutool-parent - 5.7.15 + 5.7.16-SNAPSHOT hutool-captcha diff --git a/hutool-core/pom.xml b/hutool-core/pom.xml index 5956c0bb2..ed9ac8a21 100644 --- a/hutool-core/pom.xml +++ b/hutool-core/pom.xml @@ -9,7 +9,7 @@ cn.hutool hutool-parent - 5.7.15 + 5.7.16-SNAPSHOT hutool-core diff --git a/hutool-cron/pom.xml b/hutool-cron/pom.xml index 62dc9962c..4d3a77ef2 100644 --- a/hutool-cron/pom.xml +++ b/hutool-cron/pom.xml @@ -9,7 +9,7 @@ cn.hutool hutool-parent - 5.7.15 + 5.7.16-SNAPSHOT hutool-cron diff --git a/hutool-crypto/pom.xml b/hutool-crypto/pom.xml index 3a89f935d..b58ed2229 100644 --- a/hutool-crypto/pom.xml +++ b/hutool-crypto/pom.xml @@ -9,7 +9,7 @@ cn.hutool hutool-parent - 5.7.15 + 5.7.16-SNAPSHOT hutool-crypto diff --git a/hutool-db/pom.xml b/hutool-db/pom.xml index dcfd6d332..99574b86d 100644 --- a/hutool-db/pom.xml +++ b/hutool-db/pom.xml @@ -9,7 +9,7 @@ cn.hutool hutool-parent - 5.7.15 + 5.7.16-SNAPSHOT hutool-db diff --git a/hutool-dfa/pom.xml b/hutool-dfa/pom.xml index 65d431f9e..2e73f8080 100644 --- a/hutool-dfa/pom.xml +++ b/hutool-dfa/pom.xml @@ -9,7 +9,7 @@ cn.hutool hutool-parent - 5.7.15 + 5.7.16-SNAPSHOT hutool-dfa diff --git a/hutool-extra/pom.xml b/hutool-extra/pom.xml index 9d7626fcf..aef8a9bbd 100644 --- a/hutool-extra/pom.xml +++ b/hutool-extra/pom.xml @@ -9,7 +9,7 @@ cn.hutool hutool-parent - 5.7.15 + 5.7.16-SNAPSHOT hutool-extra diff --git a/hutool-http/pom.xml b/hutool-http/pom.xml index d8c8d1f83..b86a35819 100644 --- a/hutool-http/pom.xml +++ b/hutool-http/pom.xml @@ -9,7 +9,7 @@ cn.hutool hutool-parent - 5.7.15 + 5.7.16-SNAPSHOT hutool-http diff --git a/hutool-json/pom.xml b/hutool-json/pom.xml index 96e3feb61..7a94609b1 100644 --- a/hutool-json/pom.xml +++ b/hutool-json/pom.xml @@ -9,7 +9,7 @@ cn.hutool hutool-parent - 5.7.15 + 5.7.16-SNAPSHOT hutool-json diff --git a/hutool-jwt/pom.xml b/hutool-jwt/pom.xml index d50aaa75f..efd206d24 100644 --- a/hutool-jwt/pom.xml +++ b/hutool-jwt/pom.xml @@ -9,7 +9,7 @@ cn.hutool hutool-parent - 5.7.15 + 5.7.16-SNAPSHOT hutool-jwt diff --git a/hutool-log/pom.xml b/hutool-log/pom.xml index ba5ea9f7e..f0175f355 100644 --- a/hutool-log/pom.xml +++ b/hutool-log/pom.xml @@ -9,7 +9,7 @@ cn.hutool hutool-parent - 5.7.15 + 5.7.16-SNAPSHOT hutool-log diff --git a/hutool-poi/pom.xml b/hutool-poi/pom.xml index e1cb315c7..90a2094c1 100644 --- a/hutool-poi/pom.xml +++ b/hutool-poi/pom.xml @@ -9,7 +9,7 @@ cn.hutool hutool-parent - 5.7.15 + 5.7.16-SNAPSHOT hutool-poi diff --git a/hutool-script/pom.xml b/hutool-script/pom.xml index 56c4429c6..9661de356 100644 --- a/hutool-script/pom.xml +++ b/hutool-script/pom.xml @@ -9,7 +9,7 @@ cn.hutool hutool-parent - 5.7.15 + 5.7.16-SNAPSHOT hutool-script diff --git a/hutool-setting/pom.xml b/hutool-setting/pom.xml index 81491727d..f70bafd70 100644 --- a/hutool-setting/pom.xml +++ b/hutool-setting/pom.xml @@ -9,7 +9,7 @@ cn.hutool hutool-parent - 5.7.15 + 5.7.16-SNAPSHOT hutool-setting diff --git a/hutool-socket/pom.xml b/hutool-socket/pom.xml index afc15da80..cb37a29e1 100644 --- a/hutool-socket/pom.xml +++ b/hutool-socket/pom.xml @@ -9,7 +9,7 @@ cn.hutool hutool-parent - 5.7.15 + 5.7.16-SNAPSHOT hutool-socket diff --git a/hutool-system/pom.xml b/hutool-system/pom.xml index f89f373b9..916d12017 100644 --- a/hutool-system/pom.xml +++ b/hutool-system/pom.xml @@ -9,7 +9,7 @@ cn.hutool hutool-parent - 5.7.15 + 5.7.16-SNAPSHOT hutool-system diff --git a/pom.xml b/pom.xml index 7ef421831..3ea2cdd16 100644 --- a/pom.xml +++ b/pom.xml @@ -8,7 +8,7 @@ cn.hutool hutool-parent - 5.7.15 + 5.7.16-SNAPSHOT hutool Hutool是一个小而全的Java工具类库,通过静态方法封装,降低相关API的学习成本,提高工作效率,使Java拥有函数式语言般的优雅,让Java语言也可以“甜甜的”。 https://github.com/dromara/hutool From d571567c52c110d7d0ec381259ea8d57b6e91864 Mon Sep 17 00:00:00 2001 From: Looly Date: Sat, 23 Oct 2021 23:19:50 +0800 Subject: [PATCH 67/86] add method --- CHANGELOG.md | 5 +++- .../java/cn/hutool/core/date/DateTime.java | 11 ++++++++ .../cn/hutool/core/text/CharSequenceUtil.java | 19 +++++++++++-- .../core/text/CharSequenceUtilTest.java | 14 ++++++++++ .../main/java/cn/hutool/extra/mail/Mail.java | 6 +++- .../cn/hutool/extra/mail/MailAccount.java | 28 ++++++++++++++++++- .../src/test/resources/config/mail.setting | 4 +++ .../java/cn/hutool/http/HttpUtilTest.java | 1 + 8 files changed, 83 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index bda7041ee..7c76c83e7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,9 +3,12 @@ ------------------------------------------------------------------------------------------------------------- -# 5.7.16 (2021-10-21) +# 5.7.16 (2021-10-23) ### 🐣新特性 +* 【core 】 增加DateTime.toLocalDateTime +* 【core 】 CharSequenceUtil增加normalize方法(pr#444@Gitee) +* 【core 】 MailAccount增加setEncodefilename()方法,可选是否编码附件的文件名(issue#I4F160@Gitee) ### 🐞Bug修复 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 6758c3768..d577399a7 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 @@ -12,6 +12,7 @@ import java.sql.Timestamp; import java.text.DateFormat; import java.text.SimpleDateFormat; import java.time.Instant; +import java.time.LocalDateTime; import java.time.ZoneId; import java.time.ZonedDateTime; import java.time.format.DateTimeFormatter; @@ -701,6 +702,16 @@ public class DateTime extends Date { return new java.sql.Date(getTime()); } + /** + * 转换为 {@link LocalDateTime} + * + * @return {@link LocalDateTime} + * @since 5.7.16 + */ + public LocalDateTime toLocalDateTime() { + return LocalDateTimeUtil.of(this); + } + /** * 计算相差时长 * 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 1a41d7282..6ca7f6c32 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 @@ -18,6 +18,7 @@ import cn.hutool.core.util.StrUtil; import java.nio.ByteBuffer; import java.nio.charset.Charset; import java.text.MessageFormat; +import java.text.Normalizer; import java.util.ArrayList; import java.util.HashSet; import java.util.LinkedList; @@ -4338,7 +4339,21 @@ public class CharSequenceUtil { * @return 给定字符串的所有字符是否都一样 * @since 5.7.3 */ - public static boolean isCharEquals(String str) { - return isBlank(str.replace(str.charAt(0), CharUtil.SPACE)); + public static boolean isCharEquals(CharSequence str) { + Assert.notEmpty(str, "Str to check must be not empty!"); + return count(str, str.charAt(0)) == str.length(); + } + + /** + * 对字符串归一化处理,如 "Á" 可以使用 "u00C1"或 "u0041u0301"表示,实际测试中两个字符串并不equals
+ * 因此使用此方法归一为一种表示形式,默认按照W3C通常建议的,在NFC中交换文本。 + * + * @param str 归一化的字符串 + * @return 归一化后的字符串 + * @see Normalizer#normalize(CharSequence, Normalizer.Form) + * @since 5.7.16 + */ + public static String normalize(CharSequence str) { + return Normalizer.normalize(str, Normalizer.Form.NFC); } } diff --git a/hutool-core/src/test/java/cn/hutool/core/text/CharSequenceUtilTest.java b/hutool-core/src/test/java/cn/hutool/core/text/CharSequenceUtilTest.java index bec53ad4d..85a35a18c 100644 --- a/hutool-core/src/test/java/cn/hutool/core/text/CharSequenceUtilTest.java +++ b/hutool-core/src/test/java/cn/hutool/core/text/CharSequenceUtilTest.java @@ -33,5 +33,19 @@ public class CharSequenceUtilTest { Assert.assertEquals( str + " is Good", result); } + @Test + public void normalizeTest(){ + // https://blog.csdn.net/oscar999/article/details/105326270 + + String str1 = "\u00C1"; + String str2 = "\u0041\u0301"; + + Assert.assertNotEquals(str1, str2); + + str1 = CharSequenceUtil.normalize(str1); + str2 = CharSequenceUtil.normalize(str2); + Assert.assertEquals(str1, str2); + } + // ------------------------------------------------------------------------ remove } diff --git a/hutool-extra/src/main/java/cn/hutool/extra/mail/Mail.java b/hutool-extra/src/main/java/cn/hutool/extra/mail/Mail.java index 1f28d16a9..e4ce322c9 100644 --- a/hutool-extra/src/main/java/cn/hutool/extra/mail/Mail.java +++ b/hutool-extra/src/main/java/cn/hutool/extra/mail/Mail.java @@ -36,6 +36,7 @@ import java.util.Date; * @since 3.2.0 */ public class Mail implements Builder { + private static final long serialVersionUID = 1L; /** * 邮箱帐户信息以及一些客户端配置信息 @@ -262,7 +263,10 @@ public class Mail implements Builder { for (DataSource attachment : attachments) { bodyPart = new MimeBodyPart(); bodyPart.setDataHandler(new DataHandler(attachment)); - nameEncoded = InternalMailUtil.encodeText(attachment.getName(), charset); + nameEncoded = attachment.getName(); + if(this.mailAccount.isEncodefilename()){ + nameEncoded = InternalMailUtil.encodeText(nameEncoded, charset); + } // 普通附件文件名 bodyPart.setFileName(nameEncoded); if (StrUtil.startWith(attachment.getContentType(), "image/")) { diff --git a/hutool-extra/src/main/java/cn/hutool/extra/mail/MailAccount.java b/hutool-extra/src/main/java/cn/hutool/extra/mail/MailAccount.java index 925550241..8ea8aed62 100644 --- a/hutool-extra/src/main/java/cn/hutool/extra/mail/MailAccount.java +++ b/hutool-extra/src/main/java/cn/hutool/extra/mail/MailAccount.java @@ -34,8 +34,10 @@ public class MailAccount implements Serializable { private static final String SOCKET_FACTORY_FALLBACK = "mail.smtp.socketFactory.fallback"; private static final String SOCKET_FACTORY_PORT = "smtp.socketFactory.port"; + // 其他 private static final String MAIL_DEBUG = "mail.debug"; private static final String SPLIT_LONG_PARAMS = "mail.mime.splitlongparameters"; + // private static final String ENCODE_FILE_NAME = "mail.mime.encodefilename"; public static final String[] MAIL_SETTING_PATHS = new String[]{"config/mail.setting", "config/mailAccount.setting", "mail.setting"}; @@ -75,7 +77,11 @@ public class MailAccount implements Serializable { /** * 对于超长参数是否切分为多份,默认为false(国内邮箱附件不支持切分的附件名) */ - private boolean splitlongparameters; + private boolean splitlongparameters = false; + /** + * 对于文件名是否使用{@link #charset}编码,默认为 {@code true} + */ + private boolean encodefilename = true; /** * 使用 STARTTLS安全连接,STARTTLS是对纯文本通信协议的扩展。它将纯文本连接升级为加密连接(TLS或SSL), 而不是使用一个单独的加密通信端口。 @@ -332,6 +338,26 @@ public class MailAccount implements Serializable { this.splitlongparameters = splitlongparameters; } + /** + * 对于文件名是否使用{@link #charset}编码,默认为 {@code true} + * + * @return 对于文件名是否使用{@link #charset}编码,默认为 {@code true} + * @since 5.7.16 + */ + public boolean isEncodefilename() { + return encodefilename; + } + + /** + * 设置对于文件名是否使用{@link #charset}编码 + * + * @param encodefilename 对于文件名是否使用{@link #charset}编码 + * @since 5.7.16 + */ + public void setEncodefilename(boolean encodefilename) { + this.encodefilename = encodefilename; + } + /** * 是否使用 STARTTLS安全连接,STARTTLS是对纯文本通信协议的扩展。它将纯文本连接升级为加密连接(TLS或SSL), 而不是使用一个单独的加密通信端口。 * diff --git a/hutool-extra/src/test/resources/config/mail.setting b/hutool-extra/src/test/resources/config/mail.setting index a7aa08342..f9a9cf720 100644 --- a/hutool-extra/src/test/resources/config/mail.setting +++ b/hutool-extra/src/test/resources/config/mail.setting @@ -20,3 +20,7 @@ starttlsEnable = true sslEnable = true # 调试模式 debug = true +# 对于超长参数是否切分为多份,默认为false(国内邮箱附件不支持切分的附件名) +splitlongparameters = false +# 是否编码附件文件名(默认true) +encodefilename = true diff --git a/hutool-http/src/test/java/cn/hutool/http/HttpUtilTest.java b/hutool-http/src/test/java/cn/hutool/http/HttpUtilTest.java index e829d7752..b89e7ea99 100644 --- a/hutool-http/src/test/java/cn/hutool/http/HttpUtilTest.java +++ b/hutool-http/src/test/java/cn/hutool/http/HttpUtilTest.java @@ -346,4 +346,5 @@ public class HttpUtilTest { .execute().body(); Console.log(body); } + } From 7abb318e31666c8e4af1a50fdbe0b19bff011988 Mon Sep 17 00:00:00 2001 From: Looly Date: Sun, 24 Oct 2021 20:10:19 +0800 Subject: [PATCH 68/86] charset null use default --- CHANGELOG.md | 3 +- .../hutool/extra/mail/InternalMailUtil.java | 19 ++++++------ .../main/java/cn/hutool/extra/mail/Mail.java | 14 +++++---- .../cn/hutool/extra/mail/MailAccount.java | 31 ++++++++++++++----- 4 files changed, 44 insertions(+), 23 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7c76c83e7..364fd5545 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,12 +3,13 @@ ------------------------------------------------------------------------------------------------------------- -# 5.7.16 (2021-10-23) +# 5.7.16 (2021-10-24) ### 🐣新特性 * 【core 】 增加DateTime.toLocalDateTime * 【core 】 CharSequenceUtil增加normalize方法(pr#444@Gitee) * 【core 】 MailAccount增加setEncodefilename()方法,可选是否编码附件的文件名(issue#I4F160@Gitee) +* 【core 】 MailAccount中charset增加null时的默认规则 ### 🐞Bug修复 diff --git a/hutool-extra/src/main/java/cn/hutool/extra/mail/InternalMailUtil.java b/hutool-extra/src/main/java/cn/hutool/extra/mail/InternalMailUtil.java index a4e015cf3..64eddb20a 100644 --- a/hutool-extra/src/main/java/cn/hutool/extra/mail/InternalMailUtil.java +++ b/hutool-extra/src/main/java/cn/hutool/extra/mail/InternalMailUtil.java @@ -17,11 +17,11 @@ import java.util.List; * @since 3.2.3 */ public class InternalMailUtil { - + /** * 将多个字符串邮件地址转为{@link InternetAddress}列表
* 单个字符串地址可以是多个地址合并的字符串 - * + * * @param addrStrs 地址数组 * @param charset 编码(主要用于中文用户名的编码) * @return 地址数组 @@ -38,12 +38,12 @@ public class InternalMailUtil { } return resultList.toArray(new InternetAddress[0]); } - + /** * 解析第一个地址 - * + * * @param address 地址字符串 - * @param charset 编码 + * @param charset 编码,{@code null}表示使用系统属性定义的编码或系统编码 * @return 地址列表 */ public static InternetAddress parseFirstAddress(String address, Charset charset) { @@ -61,9 +61,9 @@ public class InternalMailUtil { /** * 将一个地址字符串解析为多个地址
* 地址间使用" "、","、";"分隔 - * + * * @param address 地址字符串 - * @param charset 编码 + * @param charset 编码,{@code null}表示使用系统属性定义的编码或系统编码 * @return 地址列表 */ public static InternetAddress[] parseAddress(String address, Charset charset) { @@ -75,9 +75,10 @@ public class InternalMailUtil { } //编码用户名 if (ArrayUtil.isNotEmpty(addresses)) { + final String charsetStr = null == charset ? null : charset.name(); for (InternetAddress internetAddress : addresses) { try { - internetAddress.setPersonal(internetAddress.getPersonal(), charset.name()); + internetAddress.setPersonal(internetAddress.getPersonal(), charsetStr); } catch (UnsupportedEncodingException e) { throw new MailException(e); } @@ -90,7 +91,7 @@ public class InternalMailUtil { /** * 编码中文字符
* 编码失败返回原字符串 - * + * * @param text 被编码的文本 * @param charset 编码 * @return 编码后的结果 diff --git a/hutool-extra/src/main/java/cn/hutool/extra/mail/Mail.java b/hutool-extra/src/main/java/cn/hutool/extra/mail/Mail.java index e4ce322c9..2d71b2dcc 100644 --- a/hutool-extra/src/main/java/cn/hutool/extra/mail/Mail.java +++ b/hutool-extra/src/main/java/cn/hutool/extra/mail/Mail.java @@ -21,6 +21,7 @@ import javax.mail.Transport; import javax.mail.internet.MimeBodyPart; import javax.mail.internet.MimeMessage; import javax.mail.internet.MimeMultipart; +import javax.mail.internet.MimeUtility; import javax.mail.util.ByteArrayDataSource; import java.io.File; import java.io.IOException; @@ -264,7 +265,7 @@ public class Mail implements Builder { bodyPart = new MimeBodyPart(); bodyPart.setDataHandler(new DataHandler(attachment)); nameEncoded = attachment.getName(); - if(this.mailAccount.isEncodefilename()){ + if (this.mailAccount.isEncodefilename()) { nameEncoded = InternalMailUtil.encodeText(nameEncoded, charset); } // 普通附件文件名 @@ -388,7 +389,7 @@ public class Mail implements Builder { try { return doSend(); } catch (MessagingException e) { - if(e instanceof SendFailedException){ + if (e instanceof SendFailedException) { // 当地址无效时,显示更加详细的无效地址信息 final Address[] invalidAddresses = ((SendFailedException) e).getInvalidAddresses(); final String msg = StrUtil.format("Invalid Addresses: {}", ArrayUtil.toString(invalidAddresses)); @@ -430,7 +431,7 @@ public class Mail implements Builder { msg.setFrom(InternalMailUtil.parseFirstAddress(from, charset)); } // 标题 - msg.setSubject(this.title, charset.name()); + msg.setSubject(this.title, (null == charset) ? null : charset.name()); // 发送时间 msg.setSentDate(new Date()); // 内容和附件 @@ -456,14 +457,15 @@ public class Mail implements Builder { /** * 构建邮件信息主体 * - * @param charset 编码 + * @param charset 编码,{@code null}则使用{@link MimeUtility#getDefaultJavaCharset()} * @return 邮件信息主体 * @throws MessagingException 消息异常 */ private Multipart buildContent(Charset charset) throws MessagingException { + final String charsetStr = null != charset ? charset.name() : MimeUtility.getDefaultJavaCharset(); // 正文 final MimeBodyPart body = new MimeBodyPart(); - body.setContent(content, StrUtil.format("text/{}; charset={}", isHtml ? "html" : "plain", charset)); + body.setContent(content, StrUtil.format("text/{}; charset={}", isHtml ? "html" : "plain", charsetStr)); this.multipart.addBodyPart(body); return this.multipart; @@ -478,7 +480,7 @@ public class Mail implements Builder { private Session getSession() { final Session session = MailUtil.getSession(this.mailAccount, this.useGlobalSession); - if(null != this.debugOutput){ + if (null != this.debugOutput) { session.setDebugOut(debugOutput); } diff --git a/hutool-extra/src/main/java/cn/hutool/extra/mail/MailAccount.java b/hutool-extra/src/main/java/cn/hutool/extra/mail/MailAccount.java index 8ea8aed62..6cff1777d 100644 --- a/hutool-extra/src/main/java/cn/hutool/extra/mail/MailAccount.java +++ b/hutool-extra/src/main/java/cn/hutool/extra/mail/MailAccount.java @@ -34,10 +34,13 @@ public class MailAccount implements Serializable { private static final String SOCKET_FACTORY_FALLBACK = "mail.smtp.socketFactory.fallback"; private static final String SOCKET_FACTORY_PORT = "smtp.socketFactory.port"; + // System Properties + private static final String SPLIT_LONG_PARAMS = "mail.mime.splitlongparameters"; + //private static final String ENCODE_FILE_NAME = "mail.mime.encodefilename"; + //private static final String CHARSET = "mail.mime.charset"; + // 其他 private static final String MAIL_DEBUG = "mail.debug"; - private static final String SPLIT_LONG_PARAMS = "mail.mime.splitlongparameters"; - // private static final String ENCODE_FILE_NAME = "mail.mime.encodefilename"; public static final String[] MAIL_SETTING_PATHS = new String[]{"config/mail.setting", "config/mailAccount.setting", "mail.setting"}; @@ -303,16 +306,19 @@ public class MailAccount implements Serializable { /** * 获取字符集编码 * - * @return 编码 + * @return 编码,可能为{@code null} */ public Charset getCharset() { return charset; } /** - * 设置字符集编码 + * 设置字符集编码,此选项不会修改全局配置,若修改全局配置,请设置此项为{@code null}并设置: + *
+	 * 	System.setProperty("mail.mime.charset", charset);
+	 * 
* - * @param charset 字符集编码 + * @param charset 字符集编码,{@code null} 则表示使用全局设置的默认编码,全局编码为mail.mime.charset系统属性 * @return this */ public MailAccount setCharset(Charset charset) { @@ -330,7 +336,11 @@ public class MailAccount implements Serializable { } /** - * 设置对于超长参数是否切分为多份,默认为false(国内邮箱附件不支持切分的附件名) + * 设置对于超长参数是否切分为多份,默认为false(国内邮箱附件不支持切分的附件名)
+ * 注意此项为全局设置,此项会调用 + *
+	 * System.setProperty("mail.mime.splitlongparameters", true)
+	 * 
* * @param splitlongparameters 对于超长参数是否切分为多份 */ @@ -345,11 +355,17 @@ public class MailAccount implements Serializable { * @since 5.7.16 */ public boolean isEncodefilename() { + return encodefilename; } /** - * 设置对于文件名是否使用{@link #charset}编码 + * 设置对于文件名是否使用{@link #charset}编码,此选项不会修改全局配置
+ * 如果此选项设置为{@code false},则是否编码取决于两个系统属性: + *
    + *
  • mail.mime.encodefilename 是否编码附件文件名
  • + *
  • mail.mime.charset 编码文件名的编码
  • + *
* * @param encodefilename 对于文件名是否使用{@link #charset}编码 * @since 5.7.16 @@ -400,6 +416,7 @@ public class MailAccount implements Serializable { /** * 获取SSL协议,多个协议用空格分隔 + * * @return SSL协议,多个协议用空格分隔 * @since 5.5.7 */ From f88e7ab2f327630000e59c918d35762893919846 Mon Sep 17 00:00:00 2001 From: Looly Date: Tue, 26 Oct 2021 01:12:32 +0800 Subject: [PATCH 69/86] fix commeent --- CHANGELOG.md | 1 + .../main/java/cn/hutool/core/util/NumberUtil.java | 14 +++++++------- 2 files changed, 8 insertions(+), 7 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 364fd5545..414746a45 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,7 @@ * 【core 】 CharSequenceUtil增加normalize方法(pr#444@Gitee) * 【core 】 MailAccount增加setEncodefilename()方法,可选是否编码附件的文件名(issue#I4F160@Gitee) * 【core 】 MailAccount中charset增加null时的默认规则 +* 【core 】 NumberUtil.compare修正注释说明(issue#I4FAJ1@Gitee) ### 🐞Bug修复 diff --git a/hutool-core/src/main/java/cn/hutool/core/util/NumberUtil.java b/hutool-core/src/main/java/cn/hutool/core/util/NumberUtil.java index c5bf25e57..08f30132a 100644 --- a/hutool-core/src/main/java/cn/hutool/core/util/NumberUtil.java +++ b/hutool-core/src/main/java/cn/hutool/core/util/NumberUtil.java @@ -1675,12 +1675,12 @@ public class NumberUtil { * * @param x 第一个值 * @param y 第二个值 - * @return x==y返回0,x<y返回-1,x>y返回1 + * @return x==y返回0,x<y返回小于0的数,x>y返回大于0的数 * @see Character#compare(char, char) * @since 3.0.1 */ public static int compare(char x, char y) { - return x - y; + return Character.compare(x, y); } /** @@ -1688,7 +1688,7 @@ public class NumberUtil { * * @param x 第一个值 * @param y 第二个值 - * @return x==y返回0,x<y返回-1,x>y返回1 + * @return x==y返回0,x<y返回小于0的数,x>y返回大于0的数 * @see Double#compare(double, double) * @since 3.0.1 */ @@ -1701,7 +1701,7 @@ public class NumberUtil { * * @param x 第一个值 * @param y 第二个值 - * @return x==y返回0,x<y返回-1,x>y返回1 + * @return x==y返回0,x<y返回小于0的数,x>y返回大于0的数 * @see Integer#compare(int, int) * @since 3.0.1 */ @@ -1714,7 +1714,7 @@ public class NumberUtil { * * @param x 第一个值 * @param y 第二个值 - * @return x==y返回0,x<y返回-1,x>y返回1 + * @return x==y返回0,x<y返回小于0的数,x>y返回大于0的数 * @see Long#compare(long, long) * @since 3.0.1 */ @@ -1727,7 +1727,7 @@ public class NumberUtil { * * @param x 第一个值 * @param y 第二个值 - * @return x==y返回0,x<y返回-1,x>y返回1 + * @return x==y返回0,x<y返回小于0的数,x>y返回大于0的数 * @see Short#compare(short, short) * @since 3.0.1 */ @@ -1754,7 +1754,7 @@ public class NumberUtil { * @param bigNum1 数字1 * @param bigNum2 数字2 * @return 是否大于 - * @since 3, 0.9 + * @since 3.0.9 */ public static boolean isGreater(BigDecimal bigNum1, BigDecimal bigNum2) { Assert.notNull(bigNum1); From 87b88b395eb20da6e44d8964a0ea58652e638bfc Mon Sep 17 00:00:00 2001 From: Looly Date: Tue, 26 Oct 2021 01:33:30 +0800 Subject: [PATCH 70/86] fix bug --- CHANGELOG.md | 3 +- .../cn/hutool/core/net/url/UrlBuilder.java | 34 +++++++++++-------- .../java/cn/hutool/core/net/url/UrlPath.java | 4 +-- .../cn/hutool/core/net/UrlBuilderTest.java | 25 ++++++++++++++ 4 files changed, 49 insertions(+), 17 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 414746a45..41c510b61 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,7 +3,7 @@ ------------------------------------------------------------------------------------------------------------- -# 5.7.16 (2021-10-24) +# 5.7.16 (2021-10-26) ### 🐣新特性 * 【core 】 增加DateTime.toLocalDateTime @@ -13,6 +13,7 @@ * 【core 】 NumberUtil.compare修正注释说明(issue#I4FAJ1@Gitee) ### 🐞Bug修复 +* 【core 】 修复UrlBuilder.addPath歧义问题(issue#1912@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 be6d5c94b..f6a7c266d 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 @@ -322,13 +322,25 @@ public final class UrlBuilder implements Serializable { } /** - * 增加路径节点 + * 增加路径,在现有路径基础上追加路径 + * + * @param path 路径,例如aaa/bbb/ccc + * @return this + */ + public UrlBuilder addPath(CharSequence path) { + UrlPath.of(path, this.charset).getSegments().forEach(this::addPathSegment); + return this; + } + + /** + * 增加路径节点,路径节点中的"/"会被转义为"%2F" * * @param segment 路径节点 * @return this + * @since 5.7.16 */ - public UrlBuilder addPath(String segment) { - if (StrUtil.isBlank(segment)) { + public UrlBuilder addPathSegment(CharSequence segment) { + if (StrUtil.isEmpty(segment)) { return this; } if (null == this.path) { @@ -341,19 +353,13 @@ public final class UrlBuilder implements Serializable { /** * 追加path节点 * - * @param segment path节点 + * @param path path节点 * @return this + * @deprecated 方法重复,请使用{@link #addPath(CharSequence)} */ - public UrlBuilder appendPath(CharSequence segment) { - if (StrUtil.isEmpty(segment)) { - return this; - } - - if (this.path == null) { - this.path = new UrlPath(); - } - this.path.add(segment); - return this; + @Deprecated + public UrlBuilder appendPath(CharSequence path) { + return addPath(path); } /** diff --git a/hutool-core/src/main/java/cn/hutool/core/net/url/UrlPath.java b/hutool-core/src/main/java/cn/hutool/core/net/url/UrlPath.java index 66fc70109..78e2c393e 100644 --- a/hutool-core/src/main/java/cn/hutool/core/net/url/UrlPath.java +++ b/hutool-core/src/main/java/cn/hutool/core/net/url/UrlPath.java @@ -29,7 +29,7 @@ public class UrlPath { * @param charset decode用的编码,null表示不做decode * @return UrlPath */ - public static UrlPath of(String pathStr, Charset charset) { + public static UrlPath of(CharSequence pathStr, Charset charset) { final UrlPath urlPath = new UrlPath(); urlPath.parse(pathStr, charset); return urlPath; @@ -97,7 +97,7 @@ public class UrlPath { * @param charset decode编码,null表示不解码 * @return this */ - public UrlPath parse(String path, Charset charset) { + public UrlPath parse(CharSequence path, Charset charset) { if (StrUtil.isNotEmpty(path)) { // 原URL中以/结尾,则这个规则需保留,issue#I1G44J@Gitee if(StrUtil.endWith(path, CharUtil.SLASH)){ 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 78e4d93ee..ea7604161 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 @@ -281,4 +281,29 @@ public class UrlBuilderTest { final UrlBuilder urlBuilder = UrlBuilder.ofHttp(url); Assert.assertEquals(url, urlBuilder.toString()); } + + @Test + public void addPathEncodeTest(){ + String url = UrlBuilder.create() + .setScheme("https") + .setHost("domain.cn") + .addPath("api") + .addPath("xxx") + .addPath("bbb") + .build(); + + Assert.assertEquals("https://domain.cn/api/xxx/bbb", url); + } + + @Test + public void addPathEncodeTest2(){ + // https://github.com/dromara/hutool/issues/1912 + String url = UrlBuilder.create() + .setScheme("https") + .setHost("domain.cn") + .addPath("/api/xxx/bbb") + .build(); + + Assert.assertEquals("https://domain.cn/api/xxx/bbb", url); + } } From 7248b65f40342369d01d7aa202c1378c546227a5 Mon Sep 17 00:00:00 2001 From: Looly Date: Tue, 26 Oct 2021 04:24:05 +0800 Subject: [PATCH 71/86] add EFC3986 --- CHANGELOG.md | 1 + .../cn/hutool/core/codec/PercentCodec.java | 188 ++++++++++++++++++ .../main/java/cn/hutool/core/net/RFC3986.java | 98 +++++++++ .../cn/hutool/core/net/url/UrlBuilder.java | 3 +- .../java/cn/hutool/core/net/url/UrlPath.java | 4 +- .../java/cn/hutool/core/net/url/UrlQuery.java | 37 ++-- .../java/cn/hutool/core/net/UrlQueryTest.java | 36 ++++ .../java/cn/hutool/http/HttpUtilTest.java | 1 - 8 files changed, 347 insertions(+), 21 deletions(-) create mode 100644 hutool-core/src/main/java/cn/hutool/core/codec/PercentCodec.java create mode 100644 hutool-core/src/main/java/cn/hutool/core/net/RFC3986.java diff --git a/CHANGELOG.md b/CHANGELOG.md index 41c510b61..9fab546fb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,7 @@ * 【core 】 MailAccount增加setEncodefilename()方法,可选是否编码附件的文件名(issue#I4F160@Gitee) * 【core 】 MailAccount中charset增加null时的默认规则 * 【core 】 NumberUtil.compare修正注释说明(issue#I4FAJ1@Gitee) +* 【core 】 增加RFC3986类 ### 🐞Bug修复 * 【core 】 修复UrlBuilder.addPath歧义问题(issue#1912@Github) diff --git a/hutool-core/src/main/java/cn/hutool/core/codec/PercentCodec.java b/hutool-core/src/main/java/cn/hutool/core/codec/PercentCodec.java new file mode 100644 index 000000000..91faa74f6 --- /dev/null +++ b/hutool-core/src/main/java/cn/hutool/core/codec/PercentCodec.java @@ -0,0 +1,188 @@ +package cn.hutool.core.codec; + +import cn.hutool.core.util.CharUtil; +import cn.hutool.core.util.HexUtil; +import cn.hutool.core.util.StrUtil; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.OutputStreamWriter; +import java.io.Serializable; +import java.nio.charset.Charset; +import java.util.BitSet; + +/** + * 百分号编码(Percent-encoding), 也称作URL编码(URL encoding)。
+ * 百分号编码可用于URI的编码,也可以用于"application/x-www-form-urlencoded"的MIME准备数据。 + * + *

+ * 百分号编码会对 URI 中不允许出现的字符或者其他特殊情况的允许的字符进行编码,对于被编码的字符,最终会转为以百分号"%“开头,后面跟着两位16进制数值的形式。 + * 举个例子,空格符(SP)是不允许的字符,在 ASCII 码对应的二进制值是"00100000”,最终转为"%20"。 + *

+ *

+ * 对于不同场景应遵循不同规范: + * + *

    + *
  • URI:遵循RFC 3986保留字规范
  • + *
  • application/x-www-form-urlencoded,遵循W3C HTML Form content types规范,如空格须转+
  • + *
+ * + * @author looly + * @since 5.7.16 + */ +public class PercentCodec implements Serializable { + private static final long serialVersionUID = 1L; + + /** + * 从已知PercentCodec创建PercentCodec,会复制给定PercentCodec的安全字符 + * + * @param codec PercentCodec + * @return PercentCodec + */ + public static PercentCodec of(PercentCodec codec) { + return new PercentCodec((BitSet) codec.safeCharacters.clone()); + } + + /** + * 创建PercentCodec,使用指定字符串中的字符作为安全字符 + * + * @param chars 安全字符合集 + * @return PercentCodec + */ + public static PercentCodec of(CharSequence chars) { + final PercentCodec codec = new PercentCodec(); + final int length = chars.length(); + for (int i = 0; i < length; i++) { + codec.addSafe(chars.charAt(i)); + } + return codec; + } + + /** + * 存放安全编码 + */ + private final BitSet safeCharacters; + /** + * 是否编码空格为+ + */ + private boolean encodeSpaceAsPlus = false; + + /** + * 构造
+ * [a-zA-Z0-9]默认不被编码 + */ + public PercentCodec() { + this(new BitSet(256)); + } + + /** + * 构造 + * + * @param safeCharacters 安全字符,安全字符不被编码 + */ + public PercentCodec(BitSet safeCharacters) { + this.safeCharacters = safeCharacters; + } + + /** + * 增加安全字符
+ * 安全字符不被编码 + * + * @param c 字符 + * @return this + */ + public PercentCodec addSafe(char c) { + safeCharacters.set(c); + return this; + } + + /** + * 移除安全字符
+ * 安全字符不被编码 + * + * @param c 字符 + * @return this + */ + public PercentCodec removeSafe(char c) { + safeCharacters.clear(c); + return this; + } + + /** + * 增加安全字符到挡墙的PercentCodec + * + * @param codec PercentCodec + * @return this + */ + public PercentCodec or(PercentCodec codec) { + this.safeCharacters.or(codec.safeCharacters); + return this; + } + + /** + * 组合当前PercentCodec和指定PercentCodec为一个新的PercentCodec,安全字符为并集 + * + * @param codec PercentCodec + * @return 新的PercentCodec + */ + public PercentCodec orNew(PercentCodec codec) { + return of(this).or(codec); + } + + /** + * 是否将空格编码为+ + * + * @param encodeSpaceAsPlus 是否将空格编码为+ + * @return this + */ + public PercentCodec setEncodeSpaceAsPlus(boolean encodeSpaceAsPlus) { + this.encodeSpaceAsPlus = encodeSpaceAsPlus; + return this; + } + + /** + * 将URL中的字符串编码为%形式 + * + * @param path 需要编码的字符串 + * @param charset 编码, {@code null}返回原字符串,表示不编码 + * @return 编码后的字符串 + */ + public String encode(CharSequence path, Charset charset) { + if (null == charset || StrUtil.isEmpty(path)) { + return StrUtil.str(path); + } + + final StringBuilder rewrittenPath = new StringBuilder(path.length()); + final ByteArrayOutputStream buf = new ByteArrayOutputStream(); + final OutputStreamWriter writer = new OutputStreamWriter(buf, charset); + + int c; + for (int i = 0; i < path.length(); i++) { + c = path.charAt(i); + if (safeCharacters.get(c)) { + rewrittenPath.append((char) c); + } else if (encodeSpaceAsPlus && c == CharUtil.SPACE) { + // 对于空格单独处理 + rewrittenPath.append('+'); + } else { + // convert to external encoding before hex conversion + try { + writer.write((char) c); + writer.flush(); + } catch (IOException e) { + buf.reset(); + continue; + } + + byte[] ba = buf.toByteArray(); + for (byte toEncode : ba) { + // Converting each byte in the buffer + rewrittenPath.append('%'); + HexUtil.appendHex(rewrittenPath, toEncode, false); + } + buf.reset(); + } + } + return rewrittenPath.toString(); + } +} diff --git a/hutool-core/src/main/java/cn/hutool/core/net/RFC3986.java b/hutool-core/src/main/java/cn/hutool/core/net/RFC3986.java new file mode 100644 index 000000000..b8ca1ad72 --- /dev/null +++ b/hutool-core/src/main/java/cn/hutool/core/net/RFC3986.java @@ -0,0 +1,98 @@ +package cn.hutool.core.net; + +import cn.hutool.core.codec.PercentCodec; + +/** + * rfc3986 : https://www.ietf.org/rfc/rfc3986.html 编码实现 + * + * @author looly + * @since 5.7.16 + */ +public class RFC3986 { + + /** + * gen-delims = ":" / "/" / "?" / "#" / "[" / "]" / "@" + */ + public static final PercentCodec GEN_DELIMS = PercentCodec.of(":/?#[]&"); + + /** + * sub-delims = "!" / "$" / "&" / "'" / "(" / ")" / "*" / "+" / "," / ";" / "=" + */ + public static final PercentCodec SUB_DELIMS = PercentCodec.of("!$&'()*+,;="); + + /** + * reserved = gen-delims / sub-delims + */ + public static final PercentCodec RESERVED = GEN_DELIMS.orNew(SUB_DELIMS); + + /** + * unreserved = ALPHA / DIGIT / "-" / "." / "_" / "~" + */ + public static final PercentCodec UNRESERVED = PercentCodec.of(unreservedChars()); + + /** + * pchar = unreserved / pct-encoded / sub-delims / ":" / "@" + */ + public static final PercentCodec PCHAR = UNRESERVED.orNew(SUB_DELIMS).or(PercentCodec.of(":@")); + + /** + * segment = pchar + */ + public static final PercentCodec SEGMENT = PCHAR; + /** + * segment-nz-nc = SEGMENT ; non-zero-length segment without any colon ":" + */ + public static final PercentCodec SEGMENT_NZ_NC = PercentCodec.of(SEGMENT).removeSafe(':'); + + /** + * path = segment / "/" + */ + public static final PercentCodec PATH = SEGMENT.orNew(PercentCodec.of("/")); + + /** + * query = pchar / "/" / "?" + */ + public static final PercentCodec QUERY = PCHAR.orNew(PercentCodec.of("/?")); + + /** + * fragment = pchar / "/" / "?" + */ + public static final PercentCodec FRAGMENT = QUERY; + + /** + * query中的key + */ + public static final PercentCodec QUERY_PARAM_NAME = PercentCodec.of(QUERY).removeSafe('&').removeSafe('='); + + /** + * query中的value + */ + public static final PercentCodec QUERY_PARAM_VALUE = PercentCodec.of(QUERY).removeSafe('&'); + + /** + * unreserved = ALPHA / DIGIT / "-" / "." / "_" / "~" + * + * @return unreserved字符 + */ + private static StringBuilder unreservedChars() { + StringBuilder sb = new StringBuilder(); + + // ALPHA + for (char c = 'A'; c <= 'Z'; c++) { + sb.append(c); + } + for (char c = 'a'; c <= 'z'; c++) { + sb.append(c); + } + + // DIGIT + for (char c = '0'; c <= '9'; c++) { + sb.append(c); + } + + // "-" / "." / "_" / "~" + sb.append("_.-~"); + + return sb; + } +} 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 f6a7c266d..708e9721a 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 @@ -1,6 +1,7 @@ package cn.hutool.core.net.url; import cn.hutool.core.lang.Assert; +import cn.hutool.core.net.RFC3986; import cn.hutool.core.util.CharsetUtil; import cn.hutool.core.util.StrUtil; import cn.hutool.core.util.URLUtil; @@ -425,7 +426,7 @@ public final class UrlBuilder implements Serializable { * @return 标识符,例如#后边的部分 */ public String getFragmentEncoded() { - return URLUtil.encodeFragment(this.fragment, this.charset); + return RFC3986.FRAGMENT.encode(this.fragment, this.charset); } /** diff --git a/hutool-core/src/main/java/cn/hutool/core/net/url/UrlPath.java b/hutool-core/src/main/java/cn/hutool/core/net/url/UrlPath.java index 78e2c393e..bc91edf9b 100644 --- a/hutool-core/src/main/java/cn/hutool/core/net/url/UrlPath.java +++ b/hutool-core/src/main/java/cn/hutool/core/net/url/UrlPath.java @@ -2,10 +2,10 @@ package cn.hutool.core.net.url; import cn.hutool.core.collection.CollUtil; import cn.hutool.core.lang.Assert; +import cn.hutool.core.net.RFC3986; import cn.hutool.core.net.URLDecoder; import cn.hutool.core.util.CharUtil; import cn.hutool.core.util.StrUtil; -import cn.hutool.core.util.URLUtil; import java.nio.charset.Charset; import java.util.LinkedList; @@ -127,7 +127,7 @@ public class UrlPath { final StringBuilder builder = new StringBuilder(); for (String segment : segments) { - builder.append(CharUtil.SLASH).append(URLUtil.encodePathSegment(segment, charset)); + builder.append(CharUtil.SLASH).append(RFC3986.SEGMENT_NZ_NC.encode(segment, charset)); } if (withEngTag || StrUtil.isEmpty(builder)) { builder.append(CharUtil.SLASH); 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 8f882d9f5..69d5ec641 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 @@ -5,6 +5,7 @@ import cn.hutool.core.collection.IterUtil; import cn.hutool.core.convert.Convert; import cn.hutool.core.map.MapUtil; import cn.hutool.core.map.TableMap; +import cn.hutool.core.net.RFC3986; import cn.hutool.core.util.StrUtil; import cn.hutool.core.util.URLUtil; @@ -220,10 +221,15 @@ public class UrlQuery { } /** - * 构建URL查询字符串,即将key-value键值对转换为key1=v1&key2=&key3=v3形式 + * 构建URL查询字符串,即将key-value键值对转换为{@code key1=v1&key2=v2&key3=v3}形式。
+ * 对于{@code null}处理规则如下: + *
    + *
  • 如果key为{@code null},则这个键值对忽略
  • + *
  • 如果value为{@code null},只保留key,如key1对应value为{@code null}生成类似于{@code key1&key2=v2}形式
  • + *
* * @param charset encode编码,null表示不做encode编码 - * @param isEncode 是否转义键和值 + * @param isEncode 是否转义键和值,转义遵循rfc3986规范 * @return URL查询字符串 * @since 5.7.13 */ @@ -233,21 +239,18 @@ public class UrlQuery { } final StringBuilder sb = new StringBuilder(); - boolean isFirst = true; - CharSequence key; + CharSequence name; CharSequence value; for (Map.Entry entry : this.query) { - if (isFirst) { - isFirst = false; - } else { - sb.append("&"); - } - key = entry.getKey(); - if (null != key) { - sb.append(toStr(key, charset, isEncode)); + name = entry.getKey(); + if (null != name) { + if(sb.length() >0){ + sb.append("&"); + } + sb.append(isEncode ? RFC3986.QUERY_PARAM_NAME.encode(name, charset) : name); value = entry.getValue(); if (null != value) { - sb.append("=").append(toStr(value, charset, isEncode)); + sb.append("=").append(isEncode ? RFC3986.QUERY_PARAM_VALUE.encode(value, charset) : value); } } } @@ -301,18 +304,18 @@ public class UrlQuery { } /** - * 键值对的{@link CharSequence}转换为String,可选是否转义 + * 键值对的name转换为 * * @param str 原字符串 * @param charset 编码,只用于encode中 - * @param isEncode 是否转义 + * @param isEncode 是否转义,转义遵循rfc3986规范 * @return 转换后的String * @since 5.7.13 */ - private static String toStr(CharSequence str, Charset charset, boolean isEncode) { + private static String nameToStr(CharSequence str, Charset charset, boolean isEncode) { String result = StrUtil.str(str); if (isEncode) { - result = URLUtil.encodeFragment(result, charset); + result = RFC3986.QUERY_PARAM_NAME.encode(result, charset); } return result; } diff --git a/hutool-core/src/test/java/cn/hutool/core/net/UrlQueryTest.java b/hutool-core/src/test/java/cn/hutool/core/net/UrlQueryTest.java index d4ab80682..85d11ec00 100644 --- a/hutool-core/src/test/java/cn/hutool/core/net/UrlQueryTest.java +++ b/hutool-core/src/test/java/cn/hutool/core/net/UrlQueryTest.java @@ -63,4 +63,40 @@ public class UrlQueryTest { query = URLUtil.buildQuery(map, StandardCharsets.UTF_8); Assert.assertEquals("password=123456&username=SSM", query); } + + @Test + public void buildHasNullTest() { + Map map = new LinkedHashMap<>(); + map.put(null, "SSM"); + map.put("password", "123456"); + String query = URLUtil.buildQuery(map, StandardCharsets.UTF_8); + Assert.assertEquals("password=123456", query); + + map = new TreeMap<>(); + map.put("username", "SSM"); + map.put("password", ""); + query = URLUtil.buildQuery(map, StandardCharsets.UTF_8); + Assert.assertEquals("password=&username=SSM", query); + + map = new TreeMap<>(); + map.put("username", "SSM"); + map.put("password", null); + query = URLUtil.buildQuery(map, StandardCharsets.UTF_8); + Assert.assertEquals("password&username=SSM", query); + } + + @Test + public void buildSpecialTest() { + Map map = new LinkedHashMap<>(); + map.put("key1&", "SSM"); + map.put("key2", "123456&"); + String query = URLUtil.buildQuery(map, StandardCharsets.UTF_8); + Assert.assertEquals("key1%26=SSM&key2=123456%26", query); + + map = new TreeMap<>(); + map.put("username=", "SSM"); + map.put("password", "="); + query = URLUtil.buildQuery(map, StandardCharsets.UTF_8); + Assert.assertEquals("password==&username%3D=SSM", query); + } } diff --git a/hutool-http/src/test/java/cn/hutool/http/HttpUtilTest.java b/hutool-http/src/test/java/cn/hutool/http/HttpUtilTest.java index b89e7ea99..e829d7752 100644 --- a/hutool-http/src/test/java/cn/hutool/http/HttpUtilTest.java +++ b/hutool-http/src/test/java/cn/hutool/http/HttpUtilTest.java @@ -346,5 +346,4 @@ public class HttpUtilTest { .execute().body(); Console.log(body); } - } From 5c7906c1b02fff5d0e2977a5bec38c42bcf154a8 Mon Sep 17 00:00:00 2001 From: Looly Date: Tue, 26 Oct 2021 04:31:50 +0800 Subject: [PATCH 72/86] fix comment --- .../hutool/core/bean/copier/CopyOptions.java | 7 ++--- .../main/java/cn/hutool/core/lang/Assert.java | 27 ++++++++++++------- .../main/java/cn/hutool/core/net/RFC3986.java | 2 +- .../java/cn/hutool/core/util/ZipUtil.java | 5 +++- 4 files changed, 27 insertions(+), 14 deletions(-) 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 4cab13809..b0e35315f 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 @@ -242,10 +242,11 @@ public class CopyOptions implements Serializable { } /** - * 转换字段名为编辑后的字段名 + * 编辑字段值 * - * @param fieldName 字段名 - * @return 编辑后的字段名 + * @param fieldName 字段名 + * @param fieldValue 字段值 + * @return 编辑后的字段值 * @since 5.7.15 */ protected Object editFieldValue(String fieldName, Object fieldValue) { 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 fd4751d6a..a33b2a4ed 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,6 +840,7 @@ public class Assert { /** * 检查值是否在指定范围内 * + * @param 异常类型 * @param value 值 * @param min 最小值(包含) * @param max 最大值(包含) @@ -859,9 +860,11 @@ public class Assert { /** * 检查值是否在指定范围内 * - * @param value 值 - * @param min 最小值(包含) - * @param max 最大值(包含) + * @param value 值 + * @param min 最小值(包含) + * @param max 最大值(包含) + * @param errorMsgTemplate 异常信息模板,类似于"aa{}bb{}cc" + * @param params 异常信息参数,用于替换"{}"占位符 * @return 经过检查后的值 * @since 5.7.15 */ @@ -885,6 +888,7 @@ public class Assert { /** * 检查值是否在指定范围内 * + * @param 异常类型 * @param value 值 * @param min 最小值(包含) * @param max 最大值(包含) @@ -904,9 +908,11 @@ public class Assert { /** * 检查值是否在指定范围内 * - * @param value 值 - * @param min 最小值(包含) - * @param max 最大值(包含) + * @param value 值 + * @param min 最小值(包含) + * @param max 最大值(包含) + * @param errorMsgTemplate 异常信息模板,类似于"aa{}bb{}cc" + * @param params 异常信息参数,用于替换"{}"占位符 * @return 经过检查后的值 * @since 5.7.15 */ @@ -930,6 +936,7 @@ public class Assert { /** * 检查值是否在指定范围内 * + * @param 异常类型 * @param value 值 * @param min 最小值(包含) * @param max 最大值(包含) @@ -949,9 +956,11 @@ public class Assert { /** * 检查值是否在指定范围内 * - * @param value 值 - * @param min 最小值(包含) - * @param max 最大值(包含) + * @param value 值 + * @param min 最小值(包含) + * @param max 最大值(包含) + * @param errorMsgTemplate 异常信息模板,类似于"aa{}bb{}cc" + * @param params 异常信息参数,用于替换"{}"占位符 * @return 经过检查后的值 * @since 5.7.15 */ diff --git a/hutool-core/src/main/java/cn/hutool/core/net/RFC3986.java b/hutool-core/src/main/java/cn/hutool/core/net/RFC3986.java index b8ca1ad72..713b082c2 100644 --- a/hutool-core/src/main/java/cn/hutool/core/net/RFC3986.java +++ b/hutool-core/src/main/java/cn/hutool/core/net/RFC3986.java @@ -16,7 +16,7 @@ public class RFC3986 { public static final PercentCodec GEN_DELIMS = PercentCodec.of(":/?#[]&"); /** - * sub-delims = "!" / "$" / "&" / "'" / "(" / ")" / "*" / "+" / "," / ";" / "=" + * sub-delims = "!" / "$" / "{@code &}" / "'" / "(" / ")" / "*" / "+" / "," / ";" / "=" */ public static final PercentCodec SUB_DELIMS = PercentCodec.of("!$&'()*+,;="); 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 a813242bf..37e77b5d5 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 @@ -91,9 +91,10 @@ public class ZipUtil { * @param zipPath zip文件的Path * @param appendFilePath 待添加文件Path(可以是文件夹) * @param options 拷贝选项,可选是否覆盖等 + * @throws IORuntimeException IO异常 * @since 5.7.15 */ - public static void append(Path zipPath, Path appendFilePath, CopyOption... options) throws IOException { + public static void append(Path zipPath, Path appendFilePath, CopyOption... options) throws IORuntimeException { try (FileSystem zipFileSystem = FileSystemUtil.createZip(zipPath.toString())) { if (Files.isDirectory(appendFilePath)) { Path source = appendFilePath.getParent(); @@ -107,6 +108,8 @@ public class ZipUtil { } } catch (FileAlreadyExistsException ignored) { // 不覆盖情况下,文件已存在, 跳过 + } catch (IOException e){ + throw new IORuntimeException(e); } } From 93a901d8642ba94dd7bf378a60cc101974ba4c72 Mon Sep 17 00:00:00 2001 From: Looly Date: Tue, 26 Oct 2021 04:37:35 +0800 Subject: [PATCH 73/86] fix comment --- hutool-http/src/main/java/cn/hutool/http/HttpResponse.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/hutool-http/src/main/java/cn/hutool/http/HttpResponse.java b/hutool-http/src/main/java/cn/hutool/http/HttpResponse.java index eda670ac5..f64f1ecd7 100644 --- a/hutool-http/src/main/java/cn/hutool/http/HttpResponse.java +++ b/hutool-http/src/main/java/cn/hutool/http/HttpResponse.java @@ -148,9 +148,9 @@ public class HttpResponse extends HttpBase implements Closeable { } /** - * 是否为zlib(Defalte)压缩过的内容 + * 是否为zlib(Deflate)压缩过的内容 * - * @return 是否为zlib(Defalte)压缩过的内容 + * @return 是否为zlib(Deflate)压缩过的内容 * @since 4.5.7 */ public boolean isDeflate() { From a77dce46cd07e6104e4fa5e2c1ffda3fd9ffccf6 Mon Sep 17 00:00:00 2001 From: Looly Date: Tue, 26 Oct 2021 22:19:59 +0800 Subject: [PATCH 74/86] fix qr test --- CHANGELOG.md | 1 + .../java/cn/hutool/core/text/StrBuilder.java | 2 +- .../cn/hutool/crypto/digest/DigestUtil.java | 11 +++++----- .../cn/hutool/extra/qrcode/QrCodeUtil.java | 3 ++- .../java/cn/hutool/extra/qrcode/QrConfig.java | 21 ++++++++++++++++++- .../hutool/extra/qrcode/QrCodeUtilTest.java | 7 +++++++ 6 files changed, 36 insertions(+), 9 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9fab546fb..c01e32018 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -15,6 +15,7 @@ ### 🐞Bug修复 * 【core 】 修复UrlBuilder.addPath歧义问题(issue#1912@Github) +* 【core 】 修复StrBuilder中总长度计算问题(issue#I4F9L7@Gitee) ------------------------------------------------------------------------------------------------------------- diff --git a/hutool-core/src/main/java/cn/hutool/core/text/StrBuilder.java b/hutool-core/src/main/java/cn/hutool/core/text/StrBuilder.java index 0de657e51..69bc75f04 100644 --- a/hutool-core/src/main/java/cn/hutool/core/text/StrBuilder.java +++ b/hutool-core/src/main/java/cn/hutool/core/text/StrBuilder.java @@ -579,7 +579,7 @@ public class StrBuilder implements CharSequence, Appendable, Serializable { private static int totalLength(CharSequence... strs) { int totalLength = 0; for (CharSequence str : strs) { - totalLength += (null == str ? 4 : str.length()); + totalLength += (null == str ? 0 : str.length()); } return totalLength; } diff --git a/hutool-crypto/src/main/java/cn/hutool/crypto/digest/DigestUtil.java b/hutool-crypto/src/main/java/cn/hutool/crypto/digest/DigestUtil.java index 775d6183c..fc6fd61ce 100644 --- a/hutool-crypto/src/main/java/cn/hutool/crypto/digest/DigestUtil.java +++ b/hutool-crypto/src/main/java/cn/hutool/crypto/digest/DigestUtil.java @@ -1,13 +1,12 @@ package cn.hutool.crypto.digest; +import cn.hutool.core.util.CharsetUtil; + +import javax.crypto.SecretKey; import java.io.File; import java.io.InputStream; import java.nio.charset.Charset; -import javax.crypto.SecretKey; - -import cn.hutool.core.util.CharsetUtil; - /** * 摘要算法工具类 * @@ -424,7 +423,7 @@ public class DigestUtil { * 创建HMac对象,调用digest方法可获得hmac值 * * @param algorithm {@link HmacAlgorithm} - * @param key 密钥,如果为null生成随机密钥 + * @param key 密钥,如果为{@code null}生成随机密钥 * @return {@link HMac} * @since 3.0.3 */ @@ -436,7 +435,7 @@ public class DigestUtil { * 创建HMac对象,调用digest方法可获得hmac值 * * @param algorithm {@link HmacAlgorithm} - * @param key 密钥{@link SecretKey},如果为null生成随机密钥 + * @param key 密钥{@link SecretKey},如果为{@code null}生成随机密钥 * @return {@link HMac} * @since 3.0.3 */ diff --git a/hutool-extra/src/main/java/cn/hutool/extra/qrcode/QrCodeUtil.java b/hutool-extra/src/main/java/cn/hutool/extra/qrcode/QrCodeUtil.java index f9b800282..36e048d2b 100644 --- a/hutool-extra/src/main/java/cn/hutool/extra/qrcode/QrCodeUtil.java +++ b/hutool-extra/src/main/java/cn/hutool/extra/qrcode/QrCodeUtil.java @@ -313,9 +313,10 @@ public class QrCodeUtil { // 默认配置 config = new QrConfig(); } + BitMatrix bitMatrix; try { - bitMatrix = multiFormatWriter.encode(content, format, config.width, config.height, config.toHints()); + bitMatrix = multiFormatWriter.encode(content, format, config.width, config.height, config.toHints(format)); } catch (WriterException e) { throw new QrCodeException(e); } diff --git a/hutool-extra/src/main/java/cn/hutool/extra/qrcode/QrConfig.java b/hutool-extra/src/main/java/cn/hutool/extra/qrcode/QrConfig.java index f9aae03e7..0c2789c87 100644 --- a/hutool-extra/src/main/java/cn/hutool/extra/qrcode/QrConfig.java +++ b/hutool-extra/src/main/java/cn/hutool/extra/qrcode/QrConfig.java @@ -3,6 +3,7 @@ package cn.hutool.extra.qrcode; import cn.hutool.core.img.ImgUtil; import cn.hutool.core.io.FileUtil; import cn.hutool.core.util.CharsetUtil; +import com.google.zxing.BarcodeFormat; import com.google.zxing.EncodeHintType; import com.google.zxing.qrcode.decoder.ErrorCorrectionLevel; @@ -331,13 +332,31 @@ public class QrConfig { * @return 配置 */ public HashMap toHints() { + return toHints(BarcodeFormat.QR_CODE); + } + + /** + * 转换为Zxing的二维码配置 + * + * @param format 格式,根据格式不同,{@link #errorCorrection}的值类型有所不同 + * @return 配置 + */ + public HashMap toHints(BarcodeFormat format) { // 配置 final HashMap hints = new HashMap<>(); if (null != this.charset) { hints.put(EncodeHintType.CHARACTER_SET, charset.toString().toLowerCase()); } if (null != this.errorCorrection) { - hints.put(EncodeHintType.ERROR_CORRECTION, this.errorCorrection); + Object value; + if(BarcodeFormat.AZTEC == format || BarcodeFormat.PDF_417 == format){ + // issue#I4FE3U@Gitee + value = this.errorCorrection.getBits(); + } else { + value = this.errorCorrection; + } + + hints.put(EncodeHintType.ERROR_CORRECTION, value); } if (null != this.margin) { hints.put(EncodeHintType.MARGIN, this.margin); diff --git a/hutool-extra/src/test/java/cn/hutool/extra/qrcode/QrCodeUtilTest.java b/hutool-extra/src/test/java/cn/hutool/extra/qrcode/QrCodeUtilTest.java index cfb5dd251..7f31d796f 100644 --- a/hutool-extra/src/test/java/cn/hutool/extra/qrcode/QrCodeUtilTest.java +++ b/hutool-extra/src/test/java/cn/hutool/extra/qrcode/QrCodeUtilTest.java @@ -4,6 +4,7 @@ import cn.hutool.core.codec.Base64; import cn.hutool.core.img.ImgUtil; import cn.hutool.core.io.FileUtil; import cn.hutool.core.lang.Console; +import com.google.zxing.BarcodeFormat; import com.google.zxing.qrcode.decoder.ErrorCorrectionLevel; import org.junit.Assert; import org.junit.Ignore; @@ -87,4 +88,10 @@ public class QrCodeUtilTest { final String decode = QrCodeUtil.decode(ImgUtil.read("d:/test/qr_a.png"), false, true); Console.log(decode); } + + @Test + public void pdf417Test(){ + final BufferedImage image = QrCodeUtil.generate("content111", BarcodeFormat.PDF_417, QrConfig.create()); + Assert.assertNotNull(image); + } } From 32684b2d5ccc705ab4d83a3f7eec4f714bab0627 Mon Sep 17 00:00:00 2001 From: Looly Date: Tue, 26 Oct 2021 22:35:04 +0800 Subject: [PATCH 75/86] add method --- CHANGELOG.md | 2 + .../cn/hutool/core/text/CharSequenceUtil.java | 4 +- .../main/java/cn/hutool/extra/ftp/Ftp.java | 12 +++--- .../main/java/cn/hutool/extra/ssh/Sftp.java | 43 ++++++++++++++++++- 4 files changed, 52 insertions(+), 9 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index c01e32018..46aa7655b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,10 +12,12 @@ * 【core 】 MailAccount中charset增加null时的默认规则 * 【core 】 NumberUtil.compare修正注释说明(issue#I4FAJ1@Gitee) * 【core 】 增加RFC3986类 +* 【extra 】 Sftp增加put和upload重载(issue#I4FGDH@Gitee) ### 🐞Bug修复 * 【core 】 修复UrlBuilder.addPath歧义问题(issue#1912@Github) * 【core 】 修复StrBuilder中总长度计算问题(issue#I4F9L7@Gitee) +* 【core 】 修复CharSequenceUtil.wrapIfMissing预定义长度计算问题(issue#I4FDZ2@Gitee) ------------------------------------------------------------------------------------------------------------- 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 6ca7f6c32..511fb3c6e 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 @@ -2857,10 +2857,10 @@ public class CharSequenceUtil { len += str.length(); } if (isNotEmpty(prefix)) { - len += str.length(); + len += prefix.length(); } if (isNotEmpty(suffix)) { - len += str.length(); + len += suffix.length(); } StringBuilder sb = new StringBuilder(len); if (isNotEmpty(prefix) && false == startWith(str, prefix)) { 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 81f74df28..f94abd94c 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 @@ -489,9 +489,9 @@ public class Ftp extends AbstractFtp { * 上传文件到指定目录,可选: * *
-	 * 1. path为null或""上传到当前路径
-	 * 2. path为相对路径则相对于当前路径的子路径
-	 * 3. path为绝对路径则上传到此路径
+	 * 1. destPath为null或""上传到当前路径
+	 * 2. destPath为相对路径则相对于当前路径的子路径
+	 * 3. destPath为绝对路径则上传到此路径
 	 * 
* * @param file 文件 @@ -512,9 +512,9 @@ public class Ftp extends AbstractFtp { * 上传文件到指定目录,可选: * *
-	 * 1. path为null或""上传到当前路径
-	 * 2. path为相对路径则相对于当前路径的子路径
-	 * 3. path为绝对路径则上传到此路径
+	 * 1. destPath为null或""上传到当前路径
+	 * 2. destPath为相对路径则相对于当前路径的子路径
+	 * 3. destPath为绝对路径则上传到此路径
 	 * 
* * @param destPath 服务端路径,可以为{@code null} 或者相对路径或绝对路径 diff --git a/hutool-extra/src/main/java/cn/hutool/extra/ssh/Sftp.java b/hutool-extra/src/main/java/cn/hutool/extra/ssh/Sftp.java index a38b0c897..c553b33c8 100644 --- a/hutool-extra/src/main/java/cn/hutool/extra/ssh/Sftp.java +++ b/hutool-extra/src/main/java/cn/hutool/extra/ssh/Sftp.java @@ -17,6 +17,7 @@ import com.jcraft.jsch.SftpException; import com.jcraft.jsch.SftpProgressMonitor; import java.io.File; +import java.io.InputStream; import java.io.OutputStream; import java.nio.charset.Charset; import java.util.ArrayList; @@ -337,7 +338,7 @@ public class Sftp extends AbstractFtp { try { sftpATTRS = this.channel.stat(dir); } catch (SftpException e) { - if(e.getMessage().contains("No such file")){ + if (e.getMessage().contains("No such file")) { // 文件不存在直接返回false // pr#378@Gitee return false; @@ -464,6 +465,27 @@ public class Sftp extends AbstractFtp { return true; } + /** + * 上传文件到指定目录,可选: + * + *
+	 * 1. path为null或""上传到当前路径
+	 * 2. path为相对路径则相对于当前路径的子路径
+	 * 3. path为绝对路径则上传到此路径
+	 * 
+ * + * @param destPath 服务端路径,可以为{@code null} 或者相对路径或绝对路径 + * @param fileName 文件名 + * @param fileStream 文件流 + * @return 是否上传成功 + * @since 5.7.16 + */ + public boolean upload(String destPath, String fileName, InputStream fileStream) { + destPath = StrUtil.addSuffixIfNot(destPath, StrUtil.SLASH) + StrUtil.removePrefix(fileName, StrUtil.SLASH); + put(fileStream, destPath, null, Mode.OVERWRITE); + return true; + } + /** * 将本地文件上传到目标服务器,目标文件名为destPath,若destPath为目录,则目标文件名将与srcFilePath文件名相同。覆盖模式 * @@ -506,6 +528,25 @@ public class Sftp extends AbstractFtp { return this; } + /** + * 将本地数据流上传到目标服务器,目标文件名为destPath,目标必须为文件 + * + * @param srcStream 本地的数据流 + * @param destPath 目标路径, + * @param monitor 上传进度监控,通过实现此接口完成进度显示 + * @param mode {@link Mode} 模式 + * @return this + * @since 5.7.16 + */ + public Sftp put(InputStream srcStream, String destPath, SftpProgressMonitor monitor, Mode mode) { + try { + channel.put(srcStream, destPath, monitor, mode.ordinal()); + } catch (SftpException e) { + throw new JschRuntimeException(e); + } + return this; + } + @Override public void download(String src, File destFile) { get(src, FileUtil.getAbsolutePath(destFile)); From c8071336f99b22c30012db59e2054f991964f0c3 Mon Sep 17 00:00:00 2001 From: Looly Date: Wed, 27 Oct 2021 00:50:32 +0800 Subject: [PATCH 76/86] fix bug --- CHANGELOG.md | 3 +- .../java/cn/hutool/poi/excel/ExcelWriter.java | 2 +- .../java/cn/hutool/poi/excel/StyleSet.java | 82 ++++++++++++++++--- .../cn/hutool/poi/excel/cell/CellUtil.java | 33 +------- .../cn/hutool/poi/excel/ExcelWriteTest.java | 13 +++ 5 files changed, 86 insertions(+), 47 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 46aa7655b..ed9cd8e83 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,7 +3,7 @@ ------------------------------------------------------------------------------------------------------------- -# 5.7.16 (2021-10-26) +# 5.7.16 (2021-10-27) ### 🐣新特性 * 【core 】 增加DateTime.toLocalDateTime @@ -18,6 +18,7 @@ * 【core 】 修复UrlBuilder.addPath歧义问题(issue#1912@Github) * 【core 】 修复StrBuilder中总长度计算问题(issue#I4F9L7@Gitee) * 【core 】 修复CharSequenceUtil.wrapIfMissing预定义长度计算问题(issue#I4FDZ2@Gitee) +* 【poi 】 修复合并单元格为日期时,导出单元格数据为数字问题(issue#1911@Gitee) ------------------------------------------------------------------------------------------------------------- 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 02663d60b..68f8d7c7f 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 @@ -736,7 +736,7 @@ public class ExcelWriter extends ExcelBase { CellStyle style = null; if (null != this.styleSet) { - style = (isSetHeaderStyle && null != this.styleSet.headCellStyle) ? this.styleSet.headCellStyle : this.styleSet.cellStyle; + style = styleSet.getStyleByValueType(content, isSetHeaderStyle); } return merge(firstRow, lastRow, firstColumn, lastColumn, content, style); diff --git a/hutool-poi/src/main/java/cn/hutool/poi/excel/StyleSet.java b/hutool-poi/src/main/java/cn/hutool/poi/excel/StyleSet.java index 2bec87b58..6d94b4815 100644 --- a/hutool-poi/src/main/java/cn/hutool/poi/excel/StyleSet.java +++ b/hutool-poi/src/main/java/cn/hutool/poi/excel/StyleSet.java @@ -7,32 +7,48 @@ import org.apache.poi.ss.usermodel.CellStyle; import org.apache.poi.ss.usermodel.FillPatternType; import org.apache.poi.ss.usermodel.Font; import org.apache.poi.ss.usermodel.HorizontalAlignment; +import org.apache.poi.ss.usermodel.Hyperlink; import org.apache.poi.ss.usermodel.IndexedColors; import org.apache.poi.ss.usermodel.VerticalAlignment; import org.apache.poi.ss.usermodel.Workbook; import java.io.Serializable; +import java.math.BigDecimal; +import java.time.temporal.TemporalAccessor; +import java.util.Calendar; +import java.util.Date; /** * 样式集合,此样式集合汇集了整个工作簿的样式,用于减少样式的创建和冗余 * * @author looly - * */ -public class StyleSet implements Serializable{ +public class StyleSet implements Serializable { private static final long serialVersionUID = 1L; - /** 工作簿引用 */ + /** + * 工作簿引用 + */ private final Workbook workbook; - /** 标题样式 */ + /** + * 标题样式 + */ protected CellStyle headCellStyle; - /** 默认样式 */ + /** + * 默认样式 + */ protected CellStyle cellStyle; - /** 默认数字样式 */ + /** + * 默认数字样式 + */ protected CellStyle cellStyleForNumber; - /** 默认日期样式 */ + /** + * 默认日期样式 + */ protected CellStyle cellStyleForDate; - /** 默认链接样式 */ + /** + * 默认链接样式 + */ protected CellStyle cellStyleForHyperlink; /** @@ -148,7 +164,7 @@ public class StyleSet implements Serializable{ * 设置单元格背景样式 * * @param backgroundColor 背景色 - * @param withHeadCell 是否也定义头部样式 + * @param withHeadCell 是否也定义头部样式 * @return this * @since 4.0.0 */ @@ -166,9 +182,9 @@ public class StyleSet implements Serializable{ /** * 设置全局字体 * - * @param color 字体颜色 - * @param fontSize 字体大小,-1表示默认大小 - * @param fontName 字体名,null表示默认字体 + * @param color 字体颜色 + * @param fontSize 字体大小,-1表示默认大小 + * @param fontName 字体名,null表示默认字体 * @param ignoreHead 是否跳过头部样式 * @return this */ @@ -180,7 +196,7 @@ public class StyleSet implements Serializable{ /** * 设置全局字体 * - * @param font 字体,可以通过{@link StyleUtil#createFont(Workbook, short, short, String)}创建 + * @param font 字体,可以通过{@link StyleUtil#createFont(Workbook, short, short, String)}创建 * @param ignoreHead 是否跳过头部样式 * @return this * @since 4.1.0 @@ -210,4 +226,44 @@ public class StyleSet implements Serializable{ return this; } + /** + * 获取值对应的公共单元格样式 + * + * @param value 值 + * @param isHeader 是否为标题单元格 + * @return 值对应单元格样式 + * @since 5.7.16 + */ + public CellStyle getStyleByValueType(Object value, boolean isHeader) { + CellStyle style = null; + + if (isHeader && null != this.headCellStyle) { + style = headCellStyle; + } else if (null != cellStyle) { + style = cellStyle; + } + + if (value instanceof Date + || value instanceof TemporalAccessor + || value instanceof Calendar) { + // 日期单独定义格式 + if (null != this.cellStyleForDate) { + style = this.cellStyleForDate; + } + } else if (value instanceof Number) { + // 数字单独定义格式 + if ((value instanceof Double || value instanceof Float || value instanceof BigDecimal) && + null != this.cellStyleForNumber) { + style = this.cellStyleForNumber; + } + } else if (value instanceof Hyperlink) { + // 自定义超链接样式 + if (null != this.cellStyleForHyperlink) { + style = this.cellStyleForHyperlink; + } + } + + return style; + } + } diff --git a/hutool-poi/src/main/java/cn/hutool/poi/excel/cell/CellUtil.java b/hutool-poi/src/main/java/cn/hutool/poi/excel/cell/CellUtil.java index 9b31c340d..2d3883ddb 100644 --- a/hutool-poi/src/main/java/cn/hutool/poi/excel/cell/CellUtil.java +++ b/hutool-poi/src/main/java/cn/hutool/poi/excel/cell/CellUtil.java @@ -15,7 +15,6 @@ import org.apache.poi.ss.usermodel.ClientAnchor; import org.apache.poi.ss.usermodel.Comment; import org.apache.poi.ss.usermodel.CreationHelper; import org.apache.poi.ss.usermodel.Drawing; -import org.apache.poi.ss.usermodel.Hyperlink; import org.apache.poi.ss.usermodel.Row; import org.apache.poi.ss.usermodel.Sheet; import org.apache.poi.ss.usermodel.Workbook; @@ -23,11 +22,6 @@ import org.apache.poi.ss.util.CellRangeAddress; import org.apache.poi.ss.util.RegionUtil; import org.apache.poi.ss.util.SheetUtil; -import java.math.BigDecimal; -import java.time.temporal.TemporalAccessor; -import java.util.Calendar; -import java.util.Date; - /** * Excel表格中单元格工具类 * @@ -151,32 +145,7 @@ public class CellUtil { } if (null != styleSet) { - final CellStyle headCellStyle = styleSet.getHeadCellStyle(); - final CellStyle cellStyle = styleSet.getCellStyle(); - if (isHeader && null != headCellStyle) { - cell.setCellStyle(headCellStyle); - } else if (null != cellStyle) { - cell.setCellStyle(cellStyle); - } - } - - if (value instanceof Date - || value instanceof TemporalAccessor - || value instanceof Calendar) { - // 日期单独定义格式 - if (null != styleSet && null != styleSet.getCellStyleForDate()) { - cell.setCellStyle(styleSet.getCellStyleForDate()); - } - } else if (value instanceof Number) { - // 数字单独定义格式 - if ((value instanceof Double || value instanceof Float || value instanceof BigDecimal) && null != styleSet && null != styleSet.getCellStyleForNumber()) { - cell.setCellStyle(styleSet.getCellStyleForNumber()); - } - } else if(value instanceof Hyperlink){ - // 自定义超链接样式 - if (null != styleSet && null != styleSet.getCellStyleForHyperlink()) { - cell.setCellStyle(styleSet.getCellStyleForHyperlink()); - } + cell.setCellStyle(styleSet.getStyleByValueType(value, isHeader)); } setCellValue(cell, value); 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 d33fc890b..0dc63eade 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 @@ -727,6 +727,19 @@ public class ExcelWriteTest { writer.close(); } + @Test + @Ignore + public void mergeForDateTest(){ + // https://github.com/dromara/hutool/issues/1911 + + //通过工具类创建writer + String path = "d:/test/mergeForDate.xlsx"; + FileUtil.del(path); + ExcelWriter writer = ExcelUtil.getWriter(path); + writer.merge(0, 3, 0, 2, DateUtil.date(), false); + writer.close(); + } + @Test @Ignore public void changeHeaderStyleTest(){ From 54c015150868ef7cd0196ccce0bd4597343086dd Mon Sep 17 00:00:00 2001 From: Looly Date: Wed, 27 Oct 2021 11:46:42 +0800 Subject: [PATCH 77/86] fix bug --- CHANGELOG.md | 3 ++- .../src/main/java/cn/hutool/core/compiler/CompilerUtil.java | 4 ++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index ed9cd8e83..c6778ddbb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -18,7 +18,8 @@ * 【core 】 修复UrlBuilder.addPath歧义问题(issue#1912@Github) * 【core 】 修复StrBuilder中总长度计算问题(issue#I4F9L7@Gitee) * 【core 】 修复CharSequenceUtil.wrapIfMissing预定义长度计算问题(issue#I4FDZ2@Gitee) -* 【poi 】 修复合并单元格为日期时,导出单元格数据为数字问题(issue#1911@Gitee) +* 【poi 】 修复合并单元格为日期时,导出单元格数据为数字问题(issue#1911@Github) +* 【core 】 修复CompilerUtil.getFileManager参数没有使用的问题(issue#I4FIO6@Gitee) ------------------------------------------------------------------------------------------------------------- diff --git a/hutool-core/src/main/java/cn/hutool/core/compiler/CompilerUtil.java b/hutool-core/src/main/java/cn/hutool/core/compiler/CompilerUtil.java index 1af24f204..fb5cbc635 100644 --- a/hutool-core/src/main/java/cn/hutool/core/compiler/CompilerUtil.java +++ b/hutool-core/src/main/java/cn/hutool/core/compiler/CompilerUtil.java @@ -36,7 +36,7 @@ public class CompilerUtil { * @return {@link StandardJavaFileManager} */ public static StandardJavaFileManager getFileManager() { - return SYSTEM_COMPILER.getStandardFileManager(null, null, null); + return getFileManager(null); } /** @@ -47,7 +47,7 @@ public class CompilerUtil { * @since 5.5.8 */ public static StandardJavaFileManager getFileManager(DiagnosticListener diagnosticListener) { - return SYSTEM_COMPILER.getStandardFileManager(null, null, null); + return SYSTEM_COMPILER.getStandardFileManager(diagnosticListener, null, null); } /** From 1f2ca2e048800c9c9a9e26d29a7c06d467dbf315 Mon Sep 17 00:00:00 2001 From: Looly Date: Wed, 27 Oct 2021 16:59:19 +0800 Subject: [PATCH 78/86] fox code --- .../src/main/java/cn/hutool/core/util/ObjectUtil.java | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/hutool-core/src/main/java/cn/hutool/core/util/ObjectUtil.java b/hutool-core/src/main/java/cn/hutool/core/util/ObjectUtil.java index 649e18e19..2c84590ac 100644 --- a/hutool-core/src/main/java/cn/hutool/core/util/ObjectUtil.java +++ b/hutool-core/src/main/java/cn/hutool/core/util/ObjectUtil.java @@ -455,11 +455,14 @@ public class ObjectUtil { /** * 是否为基本类型,包括包装类型和非包装类型 * - * @param object 被检查对象 + * @param object 被检查对象,{@code null}返回{@code false} * @return 是否为基本类型 * @see ClassUtil#isBasicType(Class) */ public static boolean isBasicType(Object object) { + if (null == object) { + return false; + } return ClassUtil.isBasicType(object.getClass()); } From 96e3a6cdbeb52cbc25465a2fb2575a1d1192f7d7 Mon Sep 17 00:00:00 2001 From: Looly Date: Thu, 28 Oct 2021 02:02:23 +0800 Subject: [PATCH 79/86] add method --- CHANGELOG.md | 4 +- .../core/convert/ConverterRegistry.java | 3 + .../core/convert/impl/OptConverter.java | 21 +++++ .../java/cn/hutool/core/date/DateUtil.java | 26 ++++++ .../java/cn/hutool/core/date/StopWatch.java | 86 ++++++++++++++++--- .../cn/hutool/core/date/TemporalUtil.java | 64 ++++++++++++++ 6 files changed, 192 insertions(+), 12 deletions(-) create mode 100644 hutool-core/src/main/java/cn/hutool/core/convert/impl/OptConverter.java diff --git a/CHANGELOG.md b/CHANGELOG.md index c6778ddbb..caba2aed1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,7 +3,7 @@ ------------------------------------------------------------------------------------------------------------- -# 5.7.16 (2021-10-27) +# 5.7.16 (2021-10-28) ### 🐣新特性 * 【core 】 增加DateTime.toLocalDateTime @@ -13,6 +13,8 @@ * 【core 】 NumberUtil.compare修正注释说明(issue#I4FAJ1@Gitee) * 【core 】 增加RFC3986类 * 【extra 】 Sftp增加put和upload重载(issue#I4FGDH@Gitee) +* 【core 】 TemporalUtil增加toChronoUnit、toTimeUnit方法(issue#I4FGDH@Gitee) +* 【core 】 StopWatch增加prettyPrint重载(issue#1910@Github) ### 🐞Bug修复 * 【core 】 修复UrlBuilder.addPath歧义问题(issue#1912@Github) diff --git a/hutool-core/src/main/java/cn/hutool/core/convert/ConverterRegistry.java b/hutool-core/src/main/java/cn/hutool/core/convert/ConverterRegistry.java index 2475cb022..5cac6c295 100644 --- a/hutool-core/src/main/java/cn/hutool/core/convert/ConverterRegistry.java +++ b/hutool-core/src/main/java/cn/hutool/core/convert/ConverterRegistry.java @@ -20,6 +20,7 @@ import cn.hutool.core.convert.impl.EnumConverter; import cn.hutool.core.convert.impl.LocaleConverter; import cn.hutool.core.convert.impl.MapConverter; import cn.hutool.core.convert.impl.NumberConverter; +import cn.hutool.core.convert.impl.OptConverter; import cn.hutool.core.convert.impl.OptionalConverter; import cn.hutool.core.convert.impl.PathConverter; import cn.hutool.core.convert.impl.PeriodConverter; @@ -33,6 +34,7 @@ import cn.hutool.core.convert.impl.URIConverter; import cn.hutool.core.convert.impl.URLConverter; import cn.hutool.core.convert.impl.UUIDConverter; import cn.hutool.core.date.DateTime; +import cn.hutool.core.lang.Opt; import cn.hutool.core.lang.TypeReference; import cn.hutool.core.util.ClassUtil; import cn.hutool.core.util.ObjectUtil; @@ -444,6 +446,7 @@ public class ConverterRegistry implements Serializable { defaultConverterMap.put(UUID.class, new UUIDConverter());// since 4.0.10 defaultConverterMap.put(StackTraceElement.class, new StackTraceElementConverter());// since 4.5.2 defaultConverterMap.put(Optional.class, new OptionalConverter());// since 5.0.0 + defaultConverterMap.put(Opt.class, new OptConverter());// since 5.7.16 return this; } diff --git a/hutool-core/src/main/java/cn/hutool/core/convert/impl/OptConverter.java b/hutool-core/src/main/java/cn/hutool/core/convert/impl/OptConverter.java new file mode 100644 index 000000000..cf79c179e --- /dev/null +++ b/hutool-core/src/main/java/cn/hutool/core/convert/impl/OptConverter.java @@ -0,0 +1,21 @@ +package cn.hutool.core.convert.impl; + +import cn.hutool.core.convert.AbstractConverter; +import cn.hutool.core.lang.Opt; + +/** + * + * {@link Opt}对象转换器 + * + * @author Looly + * @since 5.7.16 + */ +public class OptConverter extends AbstractConverter> { + private static final long serialVersionUID = 1L; + + @Override + protected Opt convertInternal(Object value) { + return Opt.ofNullable(value); + } + +} diff --git a/hutool-core/src/main/java/cn/hutool/core/date/DateUtil.java b/hutool-core/src/main/java/cn/hutool/core/date/DateUtil.java index 4ba6d7a9b..37c935d04 100644 --- a/hutool-core/src/main/java/cn/hutool/core/date/DateUtil.java +++ b/hutool-core/src/main/java/cn/hutool/core/date/DateUtil.java @@ -2059,6 +2059,32 @@ public class DateUtil extends CalendarUtil { return format; } + /** + * 获取时长单位简写 + * + * @param unit 单位 + * @return 单位简写名称 + * @since 5.7.16 + */ + public static String getShotName(TimeUnit unit) { + switch (unit) { + case NANOSECONDS: + return "ns"; + case MICROSECONDS: + return "μs"; + case MILLISECONDS: + return "ms"; + case SECONDS: + return "s"; + case MINUTES: + return "min"; + case HOURS: + return "h"; + default: + return unit.name().toLowerCase(); + } + } + // ------------------------------------------------------------------------ Private method start /** diff --git a/hutool-core/src/main/java/cn/hutool/core/date/StopWatch.java b/hutool-core/src/main/java/cn/hutool/core/date/StopWatch.java index 7bf6b3401..7338644ed 100755 --- a/hutool-core/src/main/java/cn/hutool/core/date/StopWatch.java +++ b/hutool-core/src/main/java/cn/hutool/core/date/StopWatch.java @@ -6,6 +6,7 @@ import cn.hutool.core.util.StrUtil; import java.text.NumberFormat; import java.util.ArrayList; import java.util.List; +import java.util.concurrent.TimeUnit; /** * 秒表封装
@@ -48,7 +49,7 @@ public class StopWatch { * @return StopWatch * @since 5.5.2 */ - public static StopWatch create(String id){ + public static StopWatch create(String id) { return new StopWatch(id); } @@ -251,6 +252,17 @@ public class StopWatch { return this.lastTaskInfo; } + /** + * 获取所有任务的总花费时间 + * + * @param unit 时间单位,{@code null}表示默认{@link TimeUnit#NANOSECONDS} + * @return 花费时间 + * @since 5.7.16 + */ + public long getTotal(TimeUnit unit){ + return unit.convert(this.totalTimeNanos, TimeUnit.NANOSECONDS); + } + /** * 获取所有任务的总花费时间(纳秒) * @@ -270,7 +282,7 @@ public class StopWatch { * @see #getTotalTimeSeconds() */ public long getTotalTimeMillis() { - return DateUtil.nanosToMillis(this.totalTimeNanos); + return getTotal(TimeUnit.MILLISECONDS); } /** @@ -306,27 +318,61 @@ public class StopWatch { } /** - * 获取任务信息 + * 获取任务信息,类似于: + *
+	 *     StopWatch '[id]': running time = [total] ns
+	 * 
* * @return 任务信息 */ public String shortSummary() { - return StrUtil.format("StopWatch '{}': running time = {} ns", this.id, this.totalTimeNanos); + return shortSummary(null); + } + + /** + * 获取任务信息,类似于: + *
+	 *     StopWatch '[id]': running time = [total] [unit]
+	 * 
+ * + * @return 任务信息 + */ + public String shortSummary(TimeUnit unit) { + if(null == unit){ + unit = TimeUnit.NANOSECONDS; + } + return StrUtil.format("StopWatch '{}': running time = {} {}", + this.id, getTotal(unit), DateUtil.getShotName(unit)); + } + + /** + * 生成所有任务的一个任务花费时间表,单位纳秒 + * + * @return 任务时间表 + */ + public String prettyPrint() { + return prettyPrint(null); } /** * 生成所有任务的一个任务花费时间表 * + * @param unit 时间单位,{@code null}则默认{@link TimeUnit#NANOSECONDS} 纳秒 * @return 任务时间表 + * @since 5.7.16 */ - public String prettyPrint() { - StringBuilder sb = new StringBuilder(shortSummary()); + public String prettyPrint(TimeUnit unit) { + if (null == unit) { + unit = TimeUnit.NANOSECONDS; + } + + final StringBuilder sb = new StringBuilder(shortSummary(unit)); sb.append(FileUtil.getLineSeparator()); if (null == this.taskList) { sb.append("No task info kept"); } else { sb.append("---------------------------------------------").append(FileUtil.getLineSeparator()); - sb.append("ns % Task name").append(FileUtil.getLineSeparator()); + sb.append(DateUtil.getShotName(unit)).append(" % Task name").append(FileUtil.getLineSeparator()); sb.append("---------------------------------------------").append(FileUtil.getLineSeparator()); final NumberFormat nf = NumberFormat.getNumberInstance(); @@ -334,11 +380,12 @@ public class StopWatch { nf.setGroupingUsed(false); final NumberFormat pf = NumberFormat.getPercentInstance(); - pf.setMinimumIntegerDigits(3); + pf.setMinimumIntegerDigits(2); pf.setGroupingUsed(false); + for (TaskInfo task : getTaskInfo()) { - sb.append(nf.format(task.getTimeNanos())).append(" "); - sb.append(pf.format((double) task.getTimeNanos() / getTotalTimeNanos())).append(" "); + sb.append(nf.format(task.getTime(unit))).append(" "); + sb.append(pf.format((double) task.getTimeNanos() / getTotalTimeNanos())).append(" "); sb.append(task.getTaskName()).append(FileUtil.getLineSeparator()); } } @@ -370,6 +417,12 @@ public class StopWatch { private final String taskName; private final long timeNanos; + /** + * 构造 + * + * @param taskName 任务名称 + * @param timeNanos 花费时间(纳秒) + */ TaskInfo(String taskName, long timeNanos) { this.taskName = taskName; this.timeNanos = timeNanos; @@ -384,6 +437,17 @@ public class StopWatch { return this.taskName; } + /** + * 获取指定单位的任务花费时间 + * + * @param unit 单位 + * @return 任务花费时间 + * @since 5.7.16 + */ + public long getTime(TimeUnit unit) { + return unit.convert(this.timeNanos, TimeUnit.NANOSECONDS); + } + /** * 获取任务花费时间(单位:纳秒) * @@ -403,7 +467,7 @@ public class StopWatch { * @see #getTimeSeconds() */ public long getTimeMillis() { - return DateUtil.nanosToMillis(this.timeNanos); + return getTime(TimeUnit.MILLISECONDS); } /** diff --git a/hutool-core/src/main/java/cn/hutool/core/date/TemporalUtil.java b/hutool-core/src/main/java/cn/hutool/core/date/TemporalUtil.java index 28878b69a..d003f4971 100644 --- a/hutool-core/src/main/java/cn/hutool/core/date/TemporalUtil.java +++ b/hutool-core/src/main/java/cn/hutool/core/date/TemporalUtil.java @@ -3,6 +3,7 @@ package cn.hutool.core.date; import java.time.Duration; import java.time.temporal.ChronoUnit; import java.time.temporal.Temporal; +import java.util.concurrent.TimeUnit; /** * {@link Temporal} 工具类封装 @@ -38,4 +39,67 @@ public class TemporalUtil { public static long between(Temporal startTimeInclude, Temporal endTimeExclude, ChronoUnit unit) { return unit.between(startTimeInclude, endTimeExclude); } + + /** + * 将 {@link TimeUnit} 转换为 {@link ChronoUnit}. + * + * @param unit 被转换的{@link TimeUnit}单位,如果为{@code null}返回{@code null} + * @return {@link ChronoUnit} + * @since 5.7.16 + */ + public static ChronoUnit toChronoUnit(TimeUnit unit) throws IllegalArgumentException { + if (null == unit) { + return null; + } + switch (unit) { + case NANOSECONDS: + return ChronoUnit.NANOS; + case MICROSECONDS: + return ChronoUnit.MICROS; + case MILLISECONDS: + return ChronoUnit.MILLIS; + case SECONDS: + return ChronoUnit.SECONDS; + case MINUTES: + return ChronoUnit.MINUTES; + case HOURS: + return ChronoUnit.HOURS; + case DAYS: + return ChronoUnit.DAYS; + default: + throw new IllegalArgumentException("Unknown TimeUnit constant"); + } + } + + /** + * 转换 {@link ChronoUnit} 到 {@link TimeUnit}. + * + * @param unit {@link ChronoUnit},如果为{@code null}返回{@code null} + * @return {@link TimeUnit} + * @throws IllegalArgumentException 如果{@link TimeUnit}没有对应单位抛出 + * @since 5.7.16 + */ + public static TimeUnit toTimeUnit(ChronoUnit unit) throws IllegalArgumentException { + if (null == unit) { + return null; + } + switch (unit) { + case NANOS: + return TimeUnit.NANOSECONDS; + case MICROS: + return TimeUnit.MICROSECONDS; + case MILLIS: + return TimeUnit.MILLISECONDS; + case SECONDS: + return TimeUnit.SECONDS; + case MINUTES: + return TimeUnit.MINUTES; + case HOURS: + return TimeUnit.HOURS; + case DAYS: + return TimeUnit.DAYS; + default: + throw new IllegalArgumentException("ChronoUnit cannot be converted to TimeUnit: " + unit); + } + } } From 7766522f9ed53c64dd908a8b6e72ef42b6bbf4b1 Mon Sep 17 00:00:00 2001 From: Looly Date: Thu, 28 Oct 2021 02:06:16 +0800 Subject: [PATCH 80/86] fix comment --- hutool-core/src/main/java/cn/hutool/core/date/StopWatch.java | 1 + 1 file changed, 1 insertion(+) diff --git a/hutool-core/src/main/java/cn/hutool/core/date/StopWatch.java b/hutool-core/src/main/java/cn/hutool/core/date/StopWatch.java index 7338644ed..e83bd25de 100755 --- a/hutool-core/src/main/java/cn/hutool/core/date/StopWatch.java +++ b/hutool-core/src/main/java/cn/hutool/core/date/StopWatch.java @@ -335,6 +335,7 @@ public class StopWatch { * StopWatch '[id]': running time = [total] [unit] * * + * @param unit 时间单位,{@code null}则默认为{@link TimeUnit#NANOSECONDS} * @return 任务信息 */ public String shortSummary(TimeUnit unit) { From ccc9109197050f5ba2fe4c184fe4db37bbebfaf9 Mon Sep 17 00:00:00 2001 From: Jice Gao Date: Thu, 28 Oct 2021 09:46:12 +0800 Subject: [PATCH 81/86] =?UTF-8?q?=E4=BF=AE=E6=AD=A3cidr=E5=88=A4=E5=AE=9A?= =?UTF-8?q?=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/main/java/cn/hutool/core/net/NetUtil.java | 2 +- .../src/test/java/cn/hutool/core/net/NetUtilTest.java | 11 +++++++++++ 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/hutool-core/src/main/java/cn/hutool/core/net/NetUtil.java b/hutool-core/src/main/java/cn/hutool/core/net/NetUtil.java index ecebe74b6..65e521342 100644 --- a/hutool-core/src/main/java/cn/hutool/core/net/NetUtil.java +++ b/hutool-core/src/main/java/cn/hutool/core/net/NetUtil.java @@ -702,7 +702,7 @@ public class NetUtil { String[] ips = StrUtil.splitToArray(ip, '.'); int ipAddr = (Integer.parseInt(ips[0]) << 24) | (Integer.parseInt(ips[1]) << 16) | (Integer.parseInt(ips[2]) << 8) | Integer.parseInt(ips[3]); int type = Integer.parseInt(cidr.replaceAll(".*/", "")); - int mask = 0xFFFFFFFF << (32 - type); + int mask = (int)((-1L << 32 - type) & 0XFFFFFFFF); String cidrIp = cidr.replaceAll("/.*", ""); String[] cidrIps = cidrIp.split("\\."); int cidrIpAddr = (Integer.parseInt(cidrIps[0]) << 24) | (Integer.parseInt(cidrIps[1]) << 16) | (Integer.parseInt(cidrIps[2]) << 8) | Integer.parseInt(cidrIps[3]); diff --git a/hutool-core/src/test/java/cn/hutool/core/net/NetUtilTest.java b/hutool-core/src/test/java/cn/hutool/core/net/NetUtilTest.java index ff467456a..0bed82723 100644 --- a/hutool-core/src/test/java/cn/hutool/core/net/NetUtilTest.java +++ b/hutool-core/src/test/java/cn/hutool/core/net/NetUtilTest.java @@ -101,4 +101,15 @@ public class NetUtilTest { Console.log(txt); } + @Test + public void isInRangeTest(){ + Assert.assertTrue(NetUtil.isInRange("114.114.114.114","0.0.0.0/0")); + Assert.assertTrue(NetUtil.isInRange("192.168.3.4","192.0.0.0/8")); + Assert.assertTrue(NetUtil.isInRange("192.168.3.4","192.168.0.0/16")); + Assert.assertTrue(NetUtil.isInRange("192.168.3.4","192.168.3.0/24")); + Assert.assertTrue(NetUtil.isInRange("192.168.3.4","192.168.3.4/32")); + Assert.assertFalse(NetUtil.isInRange("8.8.8.8","192.0.0.0/8")); + Assert.assertFalse(NetUtil.isInRange("114.114.114.114","192.168.3.4/32")); + } + } From 55572e814b6dc9918b79d010363099a4c089ebdb Mon Sep 17 00:00:00 2001 From: Looly Date: Fri, 29 Oct 2021 00:26:39 +0800 Subject: [PATCH 82/86] update dependency --- hutool-extra/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hutool-extra/pom.xml b/hutool-extra/pom.xml index aef8a9bbd..fd49b4779 100644 --- a/hutool-extra/pom.xml +++ b/hutool-extra/pom.xml @@ -27,7 +27,7 @@ 1.6.2 0.1.55 3.4.1 - 3.7.2 + 3.8.0 5.1.1 4.0.1 2.5.6 From 8efae077ff3b229e536fb35cd181289014dc9d8b Mon Sep 17 00:00:00 2001 From: Looly Date: Fri, 29 Oct 2021 01:41:00 +0800 Subject: [PATCH 83/86] fix bug --- CHANGELOG.md | 3 +- .../java/cn/hutool/core/lang/RegexPool.java | 6 ++-- .../java/cn/hutool/core/net/Ipv4Util.java | 35 ++++++++++++++----- .../main/java/cn/hutool/core/net/NetUtil.java | 17 ++++----- .../cn/hutool/core/lang/ValidatorTest.java | 11 +++++- .../java/cn/hutool/core/net/Ipv4UtilTest.java | 17 +++++++-- 6 files changed, 66 insertions(+), 23 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index caba2aed1..1a0e56750 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,7 +3,7 @@ ------------------------------------------------------------------------------------------------------------- -# 5.7.16 (2021-10-28) +# 5.7.16 (2021-10-29) ### 🐣新特性 * 【core 】 增加DateTime.toLocalDateTime @@ -22,6 +22,7 @@ * 【core 】 修复CharSequenceUtil.wrapIfMissing预定义长度计算问题(issue#I4FDZ2@Gitee) * 【poi 】 修复合并单元格为日期时,导出单元格数据为数字问题(issue#1911@Github) * 【core 】 修复CompilerUtil.getFileManager参数没有使用的问题(issue#I4FIO6@Gitee) +* 【core 】 修复NetUtil.isInRange的cidr判断问题(pr#1917@Github) ------------------------------------------------------------------------------------------------------------- diff --git a/hutool-core/src/main/java/cn/hutool/core/lang/RegexPool.java b/hutool-core/src/main/java/cn/hutool/core/lang/RegexPool.java index d21b8523b..ed7f8cad6 100755 --- a/hutool-core/src/main/java/cn/hutool/core/lang/RegexPool.java +++ b/hutool-core/src/main/java/cn/hutool/core/lang/RegexPool.java @@ -32,9 +32,11 @@ public interface RegexPool { */ String GROUP_VAR = "\\$(\\d+)"; /** - * IP v4 + * IP v4
+ * 采用分组方式便于解析地址的每一个段 */ - String IPV4 = "\\b((?!\\d\\d\\d)\\d+|1\\d\\d|2[0-4]\\d|25[0-5])\\.((?!\\d\\d\\d)\\d+|1\\d\\d|2[0-4]\\d|25[0-5])\\.((?!\\d\\d\\d)\\d+|1\\d\\d|2[0-4]\\d|25[0-5])\\.((?!\\d\\d\\d)\\d+|1\\d\\d|2[0-4]\\d|25[0-5])\\b"; + //String IPV4 = "\\b((?!\\d\\d\\d)\\d+|1\\d\\d|2[0-4]\\d|25[0-5])\\.((?!\\d\\d\\d)\\d+|1\\d\\d|2[0-4]\\d|25[0-5])\\.((?!\\d\\d\\d)\\d+|1\\d\\d|2[0-4]\\d|25[0-5])\\.((?!\\d\\d\\d)\\d+|1\\d\\d|2[0-4]\\d|25[0-5])\\b"; + String IPV4 = "^(25[0-5]|2[0-4]\\d|[0-1]?\\d?\\d)\\.(25[0-5]|2[0-4]\\d|[0-1]?\\d?\\d)\\.(25[0-5]|2[0-4]\\d|[0-1]?\\d?\\d)\\.(25[0-5]|2[0-4]\\d|[0-1]?\\d?\\d)$"; /** * IP v6 */ diff --git a/hutool-core/src/main/java/cn/hutool/core/net/Ipv4Util.java b/hutool-core/src/main/java/cn/hutool/core/net/Ipv4Util.java index 3b6ab8140..9b45795e6 100644 --- a/hutool-core/src/main/java/cn/hutool/core/net/Ipv4Util.java +++ b/hutool-core/src/main/java/cn/hutool/core/net/Ipv4Util.java @@ -3,13 +3,14 @@ package cn.hutool.core.net; import cn.hutool.core.collection.ListUtil; import cn.hutool.core.convert.Convert; import cn.hutool.core.lang.Assert; -import cn.hutool.core.lang.Validator; +import cn.hutool.core.lang.PatternPool; import cn.hutool.core.util.CharUtil; import cn.hutool.core.util.StrUtil; import java.util.ArrayList; import java.util.List; import java.util.Objects; +import java.util.regex.Matcher; /** * IPV4地址工具类 @@ -20,6 +21,7 @@ import java.util.Objects; * @since 5.4.1 */ public class Ipv4Util { + /** * IP段的分割符 */ @@ -149,18 +151,25 @@ public class Ipv4Util { /** * 根据ip地址(xxx.xxx.xxx.xxx)计算出long型的数据 * 方法别名:inet_aton + * * @param strIP IP V4 地址 * @return long值 */ public static long ipv4ToLong(String strIP) { - Validator.validateIpv4(strIP, "Invalid IPv4 address!"); - final long[] ip = Convert.convert(long[].class, StrUtil.split(strIP, CharUtil.DOT)); - return (ip[0] << 24) + (ip[1] << 16) + (ip[2] << 8) + ip[3]; + final Matcher matcher = PatternPool.IPV4.matcher(strIP); + if (matcher.matches()) { + return matchAddress(matcher); + } +// Validator.validateIpv4(strIP, "Invalid IPv4 address!"); +// final long[] ip = Convert.convert(long[].class, StrUtil.split(strIP, CharUtil.DOT)); +// return (ip[0] << 24) + (ip[1] << 16) + (ip[2] << 8) + ip[3]; + throw new IllegalArgumentException("Invalid IPv4 address!"); } /** * 根据 ip/掩码位 计算IP段的起始IP(字符串型) * 方法别名:inet_ntoa + * * @param ip 给定的IP,如218.240.38.69 * @param maskBit 给定的掩码位,如30 * @return 起始IP的字符串表示 @@ -195,9 +204,7 @@ public class Ipv4Util { * 根据子网掩码转换为掩码位 * * @param mask 掩码的点分十进制表示,例如 255.255.255.0 - * * @return 掩码位,例如 24 - * * @throws IllegalArgumentException 子网掩码非法 */ public static int getMaskBitByMask(String mask) { @@ -282,7 +289,6 @@ public class Ipv4Util { * 判断掩码是否合法 * * @param mask 掩码的点分十进制表示,例如 255.255.255.0 - * * @return true:掩码合法;false:掩码不合法 */ public static boolean isMaskValid(String mask) { @@ -293,7 +299,6 @@ public class Ipv4Util { * 判断掩码位是否合法 * * @param maskBit 掩码位,例如 24 - * * @return true:掩码位合法;false:掩码位不合法 */ public static boolean isMaskBitValid(int maskBit) { @@ -315,5 +320,19 @@ public class Ipv4Util { return getBeginIpLong(ip, maskBit) + ~ipv4ToLong(getMaskByMaskBit(maskBit)); } + + /** + * 将匹配到的Ipv4地址的4个分组分别处理 + * + * @param matcher 匹配到的Ipv4正则 + * @return ipv4对应long + */ + private static long matchAddress(Matcher matcher) { + long addr = 0; + for (int i = 1; i <= 4; ++i) { + addr |= Long.parseLong(matcher.group(i)) << 8 * (4 - i); + } + return addr; + } //-------------------------------------------------------------------------------- Private method end } diff --git a/hutool-core/src/main/java/cn/hutool/core/net/NetUtil.java b/hutool-core/src/main/java/cn/hutool/core/net/NetUtil.java index 65e521342..7758998e5 100644 --- a/hutool-core/src/main/java/cn/hutool/core/net/NetUtil.java +++ b/hutool-core/src/main/java/cn/hutool/core/net/NetUtil.java @@ -699,14 +699,15 @@ public class NetUtil { * @since 4.0.6 */ public static boolean isInRange(String ip, String cidr) { - String[] ips = StrUtil.splitToArray(ip, '.'); - int ipAddr = (Integer.parseInt(ips[0]) << 24) | (Integer.parseInt(ips[1]) << 16) | (Integer.parseInt(ips[2]) << 8) | Integer.parseInt(ips[3]); - int type = Integer.parseInt(cidr.replaceAll(".*/", "")); - int mask = (int)((-1L << 32 - type) & 0XFFFFFFFF); - String cidrIp = cidr.replaceAll("/.*", ""); - String[] cidrIps = cidrIp.split("\\."); - int cidrIpAddr = (Integer.parseInt(cidrIps[0]) << 24) | (Integer.parseInt(cidrIps[1]) << 16) | (Integer.parseInt(cidrIps[2]) << 8) | Integer.parseInt(cidrIps[3]); - return (ipAddr & mask) == (cidrIpAddr & mask); + final int maskSplitMarkIndex = cidr.lastIndexOf(Ipv4Util.IP_MASK_SPLIT_MARK); + if(maskSplitMarkIndex < 0){ + throw new IllegalArgumentException("Invalid cidr: " + cidr); + } + + final long mask = (-1L << 32 - Integer.parseInt(cidr.substring(maskSplitMarkIndex + 1))); + long cidrIpAddr = ipv4ToLong(cidr.substring(0, maskSplitMarkIndex)); + + return (ipv4ToLong(ip) & mask) == (cidrIpAddr & mask); } /** diff --git a/hutool-core/src/test/java/cn/hutool/core/lang/ValidatorTest.java b/hutool-core/src/test/java/cn/hutool/core/lang/ValidatorTest.java index a14a3d8dd..cfcd7866f 100644 --- a/hutool-core/src/test/java/cn/hutool/core/lang/ValidatorTest.java +++ b/hutool-core/src/test/java/cn/hutool/core/lang/ValidatorTest.java @@ -21,7 +21,7 @@ public class ValidatorTest { } @Test - public void hasNumberTest() throws Exception { + public void hasNumberTest() { String var1 = ""; String var2 = "str"; String var3 = "180"; @@ -218,4 +218,13 @@ public class ValidatorTest { public void isCarDrivingLicenceTest(){ Assert.assertTrue(Validator.isCarDrivingLicence("430101758218")); } + + @Test + public void validateIpv4Test(){ + Validator.validateIpv4("192.168.1.1", "Error ip"); + Validator.validateIpv4("8.8.8.8", "Error ip"); + Validator.validateIpv4("0.0.0.0", "Error ip"); + Validator.validateIpv4("255.255.255.255", "Error ip"); + Validator.validateIpv4("127.0.0.0", "Error ip"); + } } diff --git a/hutool-core/src/test/java/cn/hutool/core/net/Ipv4UtilTest.java b/hutool-core/src/test/java/cn/hutool/core/net/Ipv4UtilTest.java index 5a472963c..13d5ddbd1 100644 --- a/hutool-core/src/test/java/cn/hutool/core/net/Ipv4UtilTest.java +++ b/hutool-core/src/test/java/cn/hutool/core/net/Ipv4UtilTest.java @@ -1,11 +1,10 @@ package cn.hutool.core.net; -import cn.hutool.core.lang.Console; import org.junit.Assert; import org.junit.Test; +import org.junit.function.ThrowingRunnable; import java.util.List; -import org.junit.function.ThrowingRunnable; public class Ipv4UtilTest { @@ -40,7 +39,7 @@ public class Ipv4UtilTest { String ip = "192.168.1.1"; final int maskBitByMask = Ipv4Util.getMaskBitByMask("255.255.255.0"); final String endIpStr = Ipv4Util.getEndIpStr(ip, maskBitByMask); - Console.log(endIpStr); + Assert.assertEquals("192.168.1.255", endIpStr); } @Test @@ -75,4 +74,16 @@ public class Ipv4UtilTest { boolean maskBitValid = Ipv4Util.isMaskBitValid(33); Assert.assertFalse("掩码位非法检验", maskBitValid); } + + @Test + public void ipv4ToLongTest(){ + long l = Ipv4Util.ipv4ToLong("127.0.0.1"); + Assert.assertEquals(2130706433L, l); + l = Ipv4Util.ipv4ToLong("114.114.114.114"); + Assert.assertEquals(1920103026L, l); + l = Ipv4Util.ipv4ToLong("0.0.0.0"); + Assert.assertEquals(0L, l); + l = Ipv4Util.ipv4ToLong("255.255.255.255"); + Assert.assertEquals(4294967295L, l); + } } From 090905ec596adb84b0fb6d9f48e888a66ec5e57e Mon Sep 17 00:00:00 2001 From: Looly Date: Fri, 29 Oct 2021 01:41:40 +0800 Subject: [PATCH 84/86] fix bug --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1a0e56750..7e45f17a7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -15,6 +15,7 @@ * 【extra 】 Sftp增加put和upload重载(issue#I4FGDH@Gitee) * 【core 】 TemporalUtil增加toChronoUnit、toTimeUnit方法(issue#I4FGDH@Gitee) * 【core 】 StopWatch增加prettyPrint重载(issue#1910@Github) +* 【core 】 修改RegexPool中Ipv4正则 ### 🐞Bug修复 * 【core 】 修复UrlBuilder.addPath歧义问题(issue#1912@Github) From 735325e8703accec3da55e6f564f2518c824ef41 Mon Sep 17 00:00:00 2001 From: Looly Date: Fri, 29 Oct 2021 02:37:27 +0800 Subject: [PATCH 85/86] add util --- CHANGELOG.md | 2 + .../java/cn/hutool/core/date/DateTime.java | 3 +- .../cn/hutool/core/util/SystemPropsUtil.java | 146 ++++++++++++++++++ .../java/cn/hutool/setting/SettingLoader.java | 29 ++-- .../java/cn/hutool/system/SystemUtil.java | 117 +------------- 5 files changed, 165 insertions(+), 132 deletions(-) create mode 100644 hutool-core/src/main/java/cn/hutool/core/util/SystemPropsUtil.java diff --git a/CHANGELOG.md b/CHANGELOG.md index 7e45f17a7..b705ebf86 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -41,6 +41,8 @@ * 【core 】 ZipUtil增加append方法(pr#441@Gitee) * 【core 】 CollUtil增加重载(issue#I4E9FS@Gitee) * 【core 】 CopyOptions新增setFieldValueEditor(issue#I4E08T@Gitee) +* 【core 】 增加SystemPropsUtil(issue#1918@Gitee) +* 【core 】 增加`hutool.date.lenient`系统属性(issue#1918@Gitee) ### 🐞Bug修复 * 【core 】 修复CollUtil.isEqualList两个null返回错误问题(issue#1885@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 d577399a7..5727c460f 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 @@ -7,6 +7,7 @@ import cn.hutool.core.date.format.GlobalCustomFormat; import cn.hutool.core.lang.Assert; import cn.hutool.core.util.ObjectUtil; import cn.hutool.core.util.StrUtil; +import cn.hutool.core.util.SystemPropsUtil; import java.sql.Timestamp; import java.text.DateFormat; @@ -288,7 +289,7 @@ public class DateTime extends Date { * @see DatePattern */ public DateTime(CharSequence dateStr, DateParser dateParser) { - this(dateStr, dateParser, true); + this(dateStr, dateParser, SystemPropsUtil.getBoolean(SystemPropsUtil.HUTOOL_DATE_LENIENT, true)); } /** diff --git a/hutool-core/src/main/java/cn/hutool/core/util/SystemPropsUtil.java b/hutool-core/src/main/java/cn/hutool/core/util/SystemPropsUtil.java new file mode 100644 index 000000000..d22679567 --- /dev/null +++ b/hutool-core/src/main/java/cn/hutool/core/util/SystemPropsUtil.java @@ -0,0 +1,146 @@ +package cn.hutool.core.util; + +import cn.hutool.core.convert.Convert; +import cn.hutool.core.lang.Console; + +import java.util.Properties; + +/** + * 系统属性工具
+ * 此工具用于读取系统属性或环境变量信息,封装包括: + *
    + *
  • {@link System#getProperty(String)}
  • + *
  • {@link System#getenv(String)}
  • + *
+ * + * @author looly + * @since 5.7.16 + */ +public class SystemPropsUtil { + + /** Hutool自定义系统属性:是否解析日期字符串采用严格模式 */ + public static String HUTOOL_DATE_LENIENT = "hutool.date.lenient"; + + /** + * 取得系统属性,如果因为Java安全的限制而失败,则将错误打在Log中,然后返回 defaultValue + * + * @param name 属性名 + * @param defaultValue 默认值 + * @return 属性值或defaultValue + * @see System#getProperty(String) + * @see System#getenv(String) + */ + public static String get(String name, String defaultValue) { + return StrUtil.nullToDefault(get(name, false), defaultValue); + } + + /** + * 取得系统属性,如果因为Java安全的限制而失败,则将错误打在Log中,然后返回 {@code null} + * + * @param name 属性名 + * @param quiet 安静模式,不将出错信息打在{@code System.err}中 + * @return 属性值或{@code null} + * @see System#getProperty(String) + * @see System#getenv(String) + */ + public static String get(String name, boolean quiet) { + String value = null; + try { + value = System.getProperty(name); + } catch (SecurityException e) { + if (false == quiet) { + Console.error("Caught a SecurityException reading the system property '{}'; " + + "the SystemUtil property value will default to null.", name); + } + } + + if (null == value) { + try { + value = System.getenv(name); + } catch (SecurityException e) { + if (false == quiet) { + Console.error("Caught a SecurityException reading the system env '{}'; " + + "the SystemUtil env value will default to null.", name); + } + } + } + + return value; + } + + /** + * 获得System属性 + * + * @param key 键 + * @return 属性值 + * @see System#getProperty(String) + * @see System#getenv(String) + */ + public static String get(String key) { + return get(key, null); + } + + /** + * 获得boolean类型值 + * + * @param key 键 + * @param defaultValue 默认值 + * @return 值 + */ + public static boolean getBoolean(String key, boolean defaultValue) { + String value = get(key); + if (value == null) { + return defaultValue; + } + + value = value.trim().toLowerCase(); + if (value.isEmpty()) { + return true; + } + + return Convert.toBool(value, defaultValue); + } + + /** + * 获得int类型值 + * + * @param key 键 + * @param defaultValue 默认值 + * @return 值 + */ + public static long getInt(String key, int defaultValue) { + return Convert.toInt(get(key), defaultValue); + } + + /** + * 获得long类型值 + * + * @param key 键 + * @param defaultValue 默认值 + * @return 值 + */ + public static long getLong(String key, long defaultValue) { + return Convert.toLong(get(key), defaultValue); + } + + /** + * @return 属性列表 + */ + public static Properties getProps() { + return System.getProperties(); + } + + /** + * 设置系统属性,value为{@code null}表示移除此属性 + * + * @param key 属性名 + * @param value 属性值,{@code null}表示移除此属性 + */ + public static void set(String key, String value) { + if (null == value) { + System.clearProperty(key); + } else { + System.setProperty(key, value); + } + } +} diff --git a/hutool-setting/src/main/java/cn/hutool/setting/SettingLoader.java b/hutool-setting/src/main/java/cn/hutool/setting/SettingLoader.java index c5186307f..81e349c2a 100644 --- a/hutool-setting/src/main/java/cn/hutool/setting/SettingLoader.java +++ b/hutool-setting/src/main/java/cn/hutool/setting/SettingLoader.java @@ -8,6 +8,7 @@ import cn.hutool.core.util.CharUtil; import cn.hutool.core.util.CharsetUtil; import cn.hutool.core.util.ReUtil; import cn.hutool.core.util.StrUtil; +import cn.hutool.core.util.SystemPropsUtil; import cn.hutool.log.Log; import java.io.BufferedReader; @@ -24,7 +25,7 @@ import java.util.Set; /** * Setting文件加载器 - * + * * @author Looly * */ @@ -47,7 +48,7 @@ public class SettingLoader { /** * 构造 - * + * * @param groupedMap GroupedMap */ public SettingLoader(GroupedMap groupedMap) { @@ -56,7 +57,7 @@ public class SettingLoader { /** * 构造 - * + * * @param groupedMap GroupedMap * @param charset 编码 * @param isUseVariable 是否使用变量 @@ -69,7 +70,7 @@ public class SettingLoader { /** * 加载设置文件 - * + * * @param resource 配置文件URL * @return 加载是否成功 */ @@ -93,7 +94,7 @@ public class SettingLoader { /** * 加载设置文件。 此方法不会关闭流对象 - * + * * @param settingStream 文件流 * @return 加载成功与否 * @throws IOException IO异常 @@ -146,7 +147,7 @@ public class SettingLoader { /** * 设置变量的正则
* 正则只能有一个group表示变量本身,剩余为字符 例如 \$\{(name)\}表示${name}变量名为name的一个变量表示 - * + * * @param regex 正则 */ public void setVarRegex(String regex) { @@ -155,7 +156,7 @@ public class SettingLoader { /** * 赋值分隔符(用于分隔键值对) - * + * * @param assignFlag 正则 * @since 4.6.5 */ @@ -166,7 +167,7 @@ public class SettingLoader { /** * 持久化当前设置,会覆盖掉之前的设置
* 持久化会不会保留之前的分组 - * + * * @param absolutePath 设置文件的绝对路径 */ public void store(String absolutePath) { @@ -194,7 +195,7 @@ public class SettingLoader { /** * 存储到Writer - * + * * @param writer Writer */ synchronized private void store(PrintWriter writer) { @@ -209,7 +210,7 @@ public class SettingLoader { // ----------------------------------------------------------------------------------- Private method start /** * 替换给定值中的变量标识 - * + * * @param group 所在分组 * @param value 值 * @return 替换后的字符串 @@ -230,13 +231,9 @@ public class SettingLoader { varValue = this.groupedMap.get(groupAndKey.get(0), groupAndKey.get(1)); } } - // 系统参数中查找 + // 系统参数和环境变量中查找 if (null == varValue) { - varValue = System.getProperty(key); - } - // 环境变量中查找 - if (null == varValue) { - varValue = System.getenv(key); + varValue = SystemPropsUtil.get(key); } if (null != varValue) { diff --git a/hutool-system/src/main/java/cn/hutool/system/SystemUtil.java b/hutool-system/src/main/java/cn/hutool/system/SystemUtil.java index fe27b9b1e..6a561b727 100644 --- a/hutool-system/src/main/java/cn/hutool/system/SystemUtil.java +++ b/hutool-system/src/main/java/cn/hutool/system/SystemUtil.java @@ -1,9 +1,9 @@ package cn.hutool.system; import cn.hutool.core.convert.Convert; -import cn.hutool.core.lang.Console; import cn.hutool.core.lang.Singleton; import cn.hutool.core.util.StrUtil; +import cn.hutool.core.util.SystemPropsUtil; import java.io.PrintWriter; import java.lang.management.ClassLoadingMXBean; @@ -17,7 +17,6 @@ import java.lang.management.OperatingSystemMXBean; import java.lang.management.RuntimeMXBean; import java.lang.management.ThreadMXBean; import java.util.List; -import java.util.Properties; /** * Java的System类封装工具类。
@@ -25,7 +24,7 @@ import java.util.Properties; * * @author Looly */ -public class SystemUtil { +public class SystemUtil extends SystemPropsUtil { // ----- Java运行时环境信息 -----/ /** @@ -149,117 +148,6 @@ public class SystemUtil { */ public final static String USER_DIR = SystemPropsKeys.USER_DIR; - // ----------------------------------------------------------------------- Basic start - - /** - * 取得系统属性,如果因为Java安全的限制而失败,则将错误打在Log中,然后返回 defaultValue - * - * @param name 属性名 - * @param defaultValue 默认值 - * @return 属性值或defaultValue - * @see System#getProperty(String) - * @see System#getenv(String) - */ - public static String get(String name, String defaultValue) { - return StrUtil.nullToDefault(get(name, false), defaultValue); - } - - /** - * 取得系统属性,如果因为Java安全的限制而失败,则将错误打在Log中,然后返回 {@code null} - * - * @param name 属性名 - * @param quiet 安静模式,不将出错信息打在{@code System.err}中 - * @return 属性值或{@code null} - * @see System#getProperty(String) - * @see System#getenv(String) - */ - public static String get(String name, boolean quiet) { - String value = null; - try { - value = System.getProperty(name); - } catch (SecurityException e) { - if (false == quiet) { - Console.error("Caught a SecurityException reading the system property '{}'; " + - "the SystemUtil property value will default to null.", name); - } - } - - if (null == value) { - try { - value = System.getenv(name); - } catch (SecurityException e) { - if (false == quiet) { - Console.error("Caught a SecurityException reading the system env '{}'; " + - "the SystemUtil env value will default to null.", name); - } - } - } - - return value; - } - - /** - * 获得System属性 - * - * @param key 键 - * @return 属性值 - * @see System#getProperty(String) - * @see System#getenv(String) - */ - public static String get(String key) { - return get(key, null); - } - - /** - * 获得boolean类型值 - * - * @param key 键 - * @param defaultValue 默认值 - * @return 值 - */ - public static boolean getBoolean(String key, boolean defaultValue) { - String value = get(key); - if (value == null) { - return defaultValue; - } - - value = value.trim().toLowerCase(); - if (value.isEmpty()) { - return true; - } - - return Convert.toBool(value, defaultValue); - } - - /** - * 获得int类型值 - * - * @param key 键 - * @param defaultValue 默认值 - * @return 值 - */ - public static long getInt(String key, int defaultValue) { - return Convert.toInt(get(key), defaultValue); - } - - /** - * 获得long类型值 - * - * @param key 键 - * @param defaultValue 默认值 - * @return 值 - */ - public static long getLong(String key, long defaultValue) { - return Convert.toLong(get(key), defaultValue); - } - - /** - * @return 属性列表 - */ - public static Properties props() { - return System.getProperties(); - } - /** * 获取当前进程 PID * @@ -268,7 +156,6 @@ public class SystemUtil { public static long getCurrentPID() { return Long.parseLong(getRuntimeMXBean().getName().split("@")[0]); } - // ----------------------------------------------------------------------- Basic end /** * 返回Java虚拟机类加载系统相关属性 From 2d1fc30c271079fd9a9a84ceacd54fd2e5f43ec2 Mon Sep 17 00:00:00 2001 From: Looly Date: Sat, 30 Oct 2021 02:23:28 +0800 Subject: [PATCH 86/86] fix code --- CHANGELOG.md | 3 +- .../main/java/cn/hutool/core/lang/Pair.java | 8 +- .../core/lang/mutable/MutableDouble.java | 8 +- .../hutool/core/lang/mutable/MutablePair.java | 57 +++++++++++++ .../java/cn/hutool/core/util/ArrayUtil.java | 5 +- .../java/cn/hutool/core/util/ObjectUtil.java | 6 +- .../main/java/cn/hutool/json/JSONArray.java | 22 ++--- .../main/java/cn/hutool/json/JSONObject.java | 15 ++-- .../java/cn/hutool/json/JSONObjectTest.java | 80 ++++++++++++++----- 9 files changed, 150 insertions(+), 54 deletions(-) create mode 100644 hutool-core/src/main/java/cn/hutool/core/lang/mutable/MutablePair.java diff --git a/CHANGELOG.md b/CHANGELOG.md index b705ebf86..a21c83482 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,7 +3,7 @@ ------------------------------------------------------------------------------------------------------------- -# 5.7.16 (2021-10-29) +# 5.7.16 (2021-10-30) ### 🐣新特性 * 【core 】 增加DateTime.toLocalDateTime @@ -16,6 +16,7 @@ * 【core 】 TemporalUtil增加toChronoUnit、toTimeUnit方法(issue#I4FGDH@Gitee) * 【core 】 StopWatch增加prettyPrint重载(issue#1910@Github) * 【core 】 修改RegexPool中Ipv4正则 +* 【json 】 Filter改为MutablePair,以便编辑键值对(issue#1921@Github) ### 🐞Bug修复 * 【core 】 修复UrlBuilder.addPath歧义问题(issue#1912@Github) diff --git a/hutool-core/src/main/java/cn/hutool/core/lang/Pair.java b/hutool-core/src/main/java/cn/hutool/core/lang/Pair.java index b6cfa3ca8..a7cb9cd69 100644 --- a/hutool-core/src/main/java/cn/hutool/core/lang/Pair.java +++ b/hutool-core/src/main/java/cn/hutool/core/lang/Pair.java @@ -16,17 +16,17 @@ import java.util.Objects; public class Pair extends CloneSupport> implements Serializable { private static final long serialVersionUID = 1L; - private final K key; - private final V value; + protected K key; + protected V value; /** - * 构建{@link Pair}对象 + * 构建{@code Pair}对象 * * @param 键类型 * @param 值类型 * @param key 键 * @param value 值 - * @return {@link Pair} + * @return {@code Pair} * @since 5.4.3 */ public static Pair of(K key, V value) { diff --git a/hutool-core/src/main/java/cn/hutool/core/lang/mutable/MutableDouble.java b/hutool-core/src/main/java/cn/hutool/core/lang/mutable/MutableDouble.java index 31a72d8bd..17702fbe7 100644 --- a/hutool-core/src/main/java/cn/hutool/core/lang/mutable/MutableDouble.java +++ b/hutool-core/src/main/java/cn/hutool/core/lang/mutable/MutableDouble.java @@ -3,7 +3,7 @@ package cn.hutool.core.lang.mutable; import cn.hutool.core.util.NumberUtil; /** - * 可变 double 类型 + * 可变 {@code double} 类型 * * @see Double * @since 3.0.1 @@ -150,12 +150,12 @@ public class MutableDouble extends Number implements Comparable, * 相等需同时满足如下条件: *
    *
  1. 非空
  2. - *
  3. 类型为 {@link MutableDouble}
  4. + *
  5. 类型为 {@code MutableDouble}
  6. *
  7. 值相等
  8. *
* * @param obj 比对的对象 - * @return 相同返回true,否则 false + * @return 相同返回true,否则 {@code false} */ @Override public boolean equals(final Object obj) { @@ -175,7 +175,7 @@ public class MutableDouble extends Number implements Comparable, /** * 比较 * - * @param other 其它 {@link MutableDouble} 对象 + * @param other 其它 {@code MutableDouble} 对象 * @return x==y返回0,x<y返回-1,x>y返回1 */ @Override diff --git a/hutool-core/src/main/java/cn/hutool/core/lang/mutable/MutablePair.java b/hutool-core/src/main/java/cn/hutool/core/lang/mutable/MutablePair.java new file mode 100644 index 000000000..7e5538515 --- /dev/null +++ b/hutool-core/src/main/java/cn/hutool/core/lang/mutable/MutablePair.java @@ -0,0 +1,57 @@ +package cn.hutool.core.lang.mutable; + +import cn.hutool.core.lang.Pair; + +/** + * 可变{@link Pair}实现,可以修改键和值 + * + * @param 键类型 + * @param 值类型 + * @since 5.7.16 + */ +public class MutablePair extends Pair implements Mutable>{ + private static final long serialVersionUID = 1L; + + /** + * 构造 + * + * @param key 键 + * @param value 值 + */ + public MutablePair(K key, V value) { + super(key, value); + } + + /** + * 设置键 + * + * @param key 新键 + * @return this + */ + public MutablePair setKey(K key) { + this.key = key; + return this; + } + + /** + * 设置值 + * + * @param value 新值 + * @return this + */ + public MutablePair setValue(V value) { + this.value = value; + return this; + } + + @Override + public Pair get() { + return this; + } + + @Override + public void set(Pair pair) { + this.key = pair.getKey(); + this.value = pair.getValue(); + } +} diff --git a/hutool-core/src/main/java/cn/hutool/core/util/ArrayUtil.java b/hutool-core/src/main/java/cn/hutool/core/util/ArrayUtil.java index 5912fd56b..adc11b2f8 100644 --- a/hutool-core/src/main/java/cn/hutool/core/util/ArrayUtil.java +++ b/hutool-core/src/main/java/cn/hutool/core/util/ArrayUtil.java @@ -22,7 +22,6 @@ import java.util.Iterator; import java.util.LinkedHashSet; import java.util.List; import java.util.Map; -import java.util.Objects; import java.util.Random; import java.util.Set; import java.util.function.Function; @@ -118,7 +117,7 @@ public class ArrayUtil extends PrimitiveArrayUtil { public static boolean hasNull(T... array) { if (isNotEmpty(array)) { for (T element : array) { - if (null == element) { + if (ObjectUtil.isNull(element)) { return true; } } @@ -150,7 +149,7 @@ public class ArrayUtil extends PrimitiveArrayUtil { */ @SuppressWarnings("unchecked") public static T firstNonNull(T... array) { - return firstMatch(Objects::nonNull, array); + return firstMatch(ObjectUtil::isNotNull, array); } /** diff --git a/hutool-core/src/main/java/cn/hutool/core/util/ObjectUtil.java b/hutool-core/src/main/java/cn/hutool/core/util/ObjectUtil.java index 2c84590ac..eeab6d5fc 100644 --- a/hutool-core/src/main/java/cn/hutool/core/util/ObjectUtil.java +++ b/hutool-core/src/main/java/cn/hutool/core/util/ObjectUtil.java @@ -292,7 +292,7 @@ public class ObjectUtil { * @since 3.0.7 */ public static T defaultIfNull(final T object, final T defaultValue) { - return (null != object) ? object : defaultValue; + return isNull(object) ? defaultValue : object; } @@ -300,14 +300,14 @@ public class ObjectUtil { * 如果给定对象为{@code null} 返回默认值, 如果不为null 返回自定义handle处理后的返回值 * * @param source Object 类型对象 - * @param handle 自定义的处理方法 + * @param handle 非空时自定义的处理方法 * @param defaultValue 默认为空的返回值 * @param 被检查对象为{@code null}返回默认值,否则返回自定义handle处理后的返回值 * @return 处理后的返回值 * @since 5.4.6 */ public static T defaultIfNull(Object source, Supplier handle, final T defaultValue) { - if (Objects.nonNull(source)) { + if (isNotNull(source)) { return handle.get(); } return defaultValue; 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 3cdea5a83..df5f34f13 100644 --- a/hutool-json/src/main/java/cn/hutool/json/JSONArray.java +++ b/hutool-json/src/main/java/cn/hutool/json/JSONArray.java @@ -4,7 +4,7 @@ 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.lang.mutable.MutablePair; import cn.hutool.core.text.StrJoiner; import cn.hutool.core.util.ArrayUtil; import cn.hutool.core.util.ObjectUtil; @@ -446,13 +446,13 @@ public class JSONArray implements JSON, JSONGetter, List, Rando /** * 加入或者替换JSONArray中指定Index的值,如果index大于JSONArray的长度,将在指定index设置值,之前的位置填充JSONNull.Null * - * @param index 位置 + * @param index 位置 * @param element 值对象. 可以是以下类型: Boolean, Double, Integer, JSONArray, JSONObject, Long, String, or the JSONNull.NULL. * @return 替换的值,即之前的值 */ @Override public Object set(int index, Object element) { - if(index >= size()){ + if (index >= size()) { add(index, element); } return this.rawList.set(index, JSONUtil.wrap(element, this.config)); @@ -537,11 +537,11 @@ public class JSONArray implements JSON, JSONGetter, List, Rando * 支持过滤器,即选择哪些字段或值不写出 * * @param indentFactor 每层缩进空格数 - * @param filter 键值对过滤器 + * @param filter 过滤器,可以修改值,key(index)无法修改 * @return JSON字符串 * @since 5.7.15 */ - public String toJSONString(int indentFactor, Filter> filter){ + public String toJSONString(int indentFactor, Filter> filter) { final StringWriter sw = new StringWriter(); synchronized (sw.getBuffer()) { return this.write(sw, indentFactor, 0, filter).toString(); @@ -560,18 +560,19 @@ public class JSONArray implements JSON, JSONGetter, List, Rando * @param writer writer * @param indentFactor 缩进因子,定义每一级别增加的缩进量 * @param indent 本级别缩进量 - * @param filter 过滤器 + * @param filter 过滤器,可以修改值,key(index)无法修改 * @return Writer * @throws JSONException JSON相关异常 * @since 5.7.15 */ - public Writer write(Writer writer, int indentFactor, int indent, Filter> filter) throws JSONException { + public Writer write(Writer writer, int indentFactor, int indent, Filter> filter) throws JSONException { final JSONWriter jsonWriter = JSONWriter.of(writer, indentFactor, indent, config) .beginArray(); - CollUtil.forEach(this, (value, index)->{ - if (null == filter || filter.accept(new Pair<>(index, value))) { - jsonWriter.writeValue(value); + CollUtil.forEach(this, (value, index) -> { + final MutablePair pair = new MutablePair<>(index, value); + if (null == filter || filter.accept(pair)) { + jsonWriter.writeValue(pair.getValue()); } }); jsonWriter.end(); @@ -580,6 +581,7 @@ public class JSONArray implements JSON, JSONGetter, List, Rando } // ------------------------------------------------------------------------------------------------- Private method start + /** * 初始化 * 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 7358cedec..ca7c08613 100644 --- a/hutool-json/src/main/java/cn/hutool/json/JSONObject.java +++ b/hutool-json/src/main/java/cn/hutool/json/JSONObject.java @@ -7,7 +7,7 @@ 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.lang.mutable.MutablePair; import cn.hutool.core.map.CaseInsensitiveLinkedMap; import cn.hutool.core.map.CaseInsensitiveMap; import cn.hutool.core.map.MapUtil; @@ -560,11 +560,11 @@ public class JSONObject implements JSON, JSONGetter, Map * 支持过滤器,即选择哪些字段或值不写出 * * @param indentFactor 每层缩进空格数 - * @param filter 键值对过滤器 + * @param filter 过滤器,同时可以修改编辑键和值 * @return JSON字符串 * @since 5.7.15 */ - public String toJSONString(int indentFactor, Filter> filter){ + public String toJSONString(int indentFactor, Filter> filter) { final StringWriter sw = new StringWriter(); synchronized (sw.getBuffer()) { return this.write(sw, indentFactor, 0, filter).toString(); @@ -583,17 +583,18 @@ public class JSONObject implements JSON, JSONGetter, Map * @param writer writer * @param indentFactor 缩进因子,定义每一级别增加的缩进量 * @param indent 本级别缩进量 - * @param filter 过滤器 + * @param filter 过滤器,同时可以修改编辑键和值 * @return Writer * @throws JSONException JSON相关异常 * @since 5.7.15 */ - public Writer write(Writer writer, int indentFactor, int indent, Filter> filter) throws JSONException { + 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((key, value) -> { - if (null == filter || filter.accept(new Pair<>(key, value))) { - jsonWriter.writeField(key, value); + final MutablePair pair = new MutablePair<>(key, value); + if (null == filter || filter.accept(pair)) { + jsonWriter.writeField(pair.getKey(), pair.getValue()); } }); jsonWriter.end(); 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 f2983b859..f3ae62388 100644 --- a/hutool-json/src/test/java/cn/hutool/json/JSONObjectTest.java +++ b/hutool-json/src/test/java/cn/hutool/json/JSONObjectTest.java @@ -10,6 +10,7 @@ import cn.hutool.core.io.FileUtil; import cn.hutool.core.io.resource.ResourceUtil; import cn.hutool.core.lang.Console; import cn.hutool.core.map.MapUtil; +import cn.hutool.core.util.ObjectUtil; import cn.hutool.core.util.StrUtil; import cn.hutool.json.test.bean.JSONBean; import cn.hutool.json.test.bean.ResultDto; @@ -67,7 +68,7 @@ public class JSONObjectTest { @Test public void toStringTest3() { JSONObject json = Objects.requireNonNull(JSONUtil.createObj()// - .set("dateTime", DateUtil.parse("2019-05-02 22:12:01")))// + .set("dateTime", DateUtil.parse("2019-05-02 22:12:01")))// .setDateFormat(DatePattern.NORM_DATE_PATTERN); Assert.assertEquals("{\"dateTime\":\"2019-05-02\"}", json.toString()); } @@ -86,14 +87,14 @@ public class JSONObjectTest { @Test public void putAllTest() { JSONObject json1 = JSONUtil.createObj() - .set("a", "value1") - .set("b", "value2") - .set("c", "value3") - .set("d", true); + .set("a", "value1") + .set("b", "value2") + .set("c", "value3") + .set("d", true); JSONObject json2 = JSONUtil.createObj() - .set("a", "value21") - .set("b", "value22"); + .set("a", "value21") + .set("b", "value22"); // putAll操作会覆盖相同key的值,因此a,b两个key的值改变,c的值不变 json1.putAll(json2); @@ -399,7 +400,7 @@ public class JSONObjectTest { } @Test - public void aliasTest(){ + public void aliasTest() { final BeanWithAlias beanWithAlias = new BeanWithAlias(); beanWithAlias.setValue1("张三"); beanWithAlias.setValue2(35); @@ -417,7 +418,7 @@ public class JSONObjectTest { } @Test - public void setDateFormatTest(){ + public void setDateFormatTest() { JSONConfig jsonConfig = JSONConfig.create(); jsonConfig.setDateFormat("yyyy-MM-dd HH:mm:ss"); jsonConfig.setOrder(true); @@ -430,7 +431,7 @@ public class JSONObjectTest { } @Test - public void setDateFormatTest2(){ + public void setDateFormatTest2() { JSONConfig jsonConfig = JSONConfig.create(); jsonConfig.setDateFormat("yyyy#MM#dd"); jsonConfig.setOrder(true); @@ -451,7 +452,7 @@ public class JSONObjectTest { } @Test - public void setCustomDateFormatTest(){ + public void setCustomDateFormatTest() { JSONConfig jsonConfig = JSONConfig.create(); jsonConfig.setDateFormat("#sss"); jsonConfig.setOrder(true); @@ -472,7 +473,7 @@ public class JSONObjectTest { } @Test - public void getTimestampTest(){ + public void getTimestampTest() { String timeStr = "1970-01-01 00:00:00"; final JSONObject jsonObject = JSONUtil.createObj().set("time", timeStr); final Timestamp time = jsonObject.get("time", Timestamp.class); @@ -518,7 +519,7 @@ public class JSONObjectTest { } @Test - public void parseBeanSameNameTest(){ + public void parseBeanSameNameTest() { final SameNameBean sameNameBean = new SameNameBean(); final JSONObject parse = JSONUtil.parseObj(sameNameBean); Assert.assertEquals("123", parse.getStr("username")); @@ -537,9 +538,11 @@ public class JSONObjectTest { public static class SameNameBean { private final String username = "123"; private final String userName = "abc"; + public String getUsername() { return username; } + @PropIgnore private final String fieldToIgnore = "sfdsdads"; @@ -547,13 +550,13 @@ public class JSONObjectTest { return userName; } - public String getFieldToIgnore(){ + public String getFieldToIgnore() { return this.fieldToIgnore; } } @Test - public void setEntryTest(){ + public void setEntryTest() { final HashMap of = MapUtil.of("test", "testValue"); final Set> entries = of.entrySet(); final Map.Entry next = entries.iterator().next(); @@ -563,13 +566,13 @@ public class JSONObjectTest { } @Test(expected = JSONException.class) - public void createJSONObjectTest(){ + public void createJSONObjectTest() { // 集合类不支持转为JSONObject new JSONObject(new JSONArray(), JSONConfig.create()); } @Test - public void floatTest(){ + public void floatTest() { Map map = new HashMap<>(); map.put("c", 2.0F); @@ -578,7 +581,7 @@ public class JSONObjectTest { } @Test - public void accumulateTest(){ + public void accumulateTest() { final JSONObject jsonObject = JSONUtil.createObj().accumulate("key1", "value1"); Assert.assertEquals("{\"key1\":\"value1\"}", jsonObject.toString()); @@ -598,7 +601,7 @@ public class JSONObjectTest { @Test - public void bigDecimalTest(){ + public void bigDecimalTest() { String jsonStr = "{\"orderId\":\"1704747698891333662002277\"}"; BigDecimalBean bigDecimalBean = JSONUtil.toBean(jsonStr, BigDecimalBean.class); Assert.assertEquals("{\"orderId\":1704747698891333662002277}", JSONUtil.toJsonStr(bigDecimalBean)); @@ -606,12 +609,12 @@ public class JSONObjectTest { @Data static - class BigDecimalBean{ + class BigDecimalBean { private BigDecimal orderId; } @Test - public void filterIncludeTest(){ + public void filterIncludeTest() { JSONObject json1 = JSONUtil.createObj(JSONConfig.create().setOrder(true)) .set("a", "value1") .set("b", "value2") @@ -623,7 +626,7 @@ public class JSONObjectTest { } @Test - public void filterExcludeTest(){ + public void filterExcludeTest() { JSONObject json1 = JSONUtil.createObj(JSONConfig.create().setOrder(true)) .set("a", "value1") .set("b", "value2") @@ -633,4 +636,37 @@ public class JSONObjectTest { final String s = json1.toJSONString(0, (pair) -> false == pair.getKey().equals("b")); Assert.assertEquals("{\"a\":\"value1\",\"c\":\"value3\",\"d\":true}", s); } + + @Test + public void editTest() { + 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) -> { + if ("b".equals(pair.getKey())) { + // 修改值为新值 + pair.setValue(pair.getValue() + "_edit"); + return true; + } + // 除了"b",其他都去掉 + return false; + }); + Assert.assertEquals("{\"b\":\"value2_edit\"}", s); + } + + @Test + public void nullToEmptyTest() { + JSONObject json1 = JSONUtil.createObj(JSONConfig.create().setOrder(true).setIgnoreNullValue(false)) + .set("a", null) + .set("b", "value2"); + + final String s = json1.toJSONString(0, (pair) -> { + pair.setValue(ObjectUtil.defaultIfNull(pair.getValue(), StrUtil.EMPTY)); + return true; + }); + Assert.assertEquals("{\"a\":\"\",\"b\":\"value2\"}", s); + } }