From 226d4bd205195ef911daf05a63a80ddf2116219e Mon Sep 17 00:00:00 2001 From: Looly Date: Wed, 27 Nov 2024 08:28:23 +0800 Subject: [PATCH] =?UTF-8?q?=F0=9F=A4=A4release=205.8.33?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- CHANGELOG.md | 19 ++ README-EN.md | 8 +- README.md | 8 +- 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 +- .../cn/hutool/cache/impl/StampedCache.java | 1 - hutool-captcha/pom.xml | 2 +- .../java/cn/hutool/captcha/CircleCaptcha.java | 1 - .../java/cn/hutool/captcha/GifCaptcha.java | 1 - .../java/cn/hutool/captcha/LineCaptcha.java | 1 - hutool-core/pom.xml | 2 +- .../core/comparator/VersionComparator.java | 3 - .../core/convert/NumberWordFormatter.java | 1 - .../core/convert/impl/MapConverter.java | 5 + .../java/cn/hutool/core/date/DateRange.java | 4 + .../cn/hutool/core/io/unit/DataSizeUtil.java | 18 ++ .../java/cn/hutool/core/io/unit/DataUnit.java | 15 +- .../java/cn/hutool/core/lang/Snowflake.java | 1 - .../java/cn/hutool/core/math/Calculator.java | 4 + .../cn/hutool/core/text/csv/CsvParser.java | 242 ++++++------------ .../cn/hutool/core/text/csv/CsvTokener.java | 102 ++++++++ .../java/cn/hutool/core/util/TypeUtil.java | 1 - .../java/cn/hutool/core/util/ZipUtil.java | 16 +- .../cn/hutool/core/date/DateRangeTest.java | 27 ++ .../cn/hutool/core/date/Issue3798Test.java | 22 ++ .../hutool/core/io/unit/DataSizeUtilTest.java | 15 +- .../cn/hutool/core/math/CalculatorTest.java | 13 +- .../cn/hutool/core/text/csv/CsvUtilTest.java | 15 +- .../hutool/core/text/csv/IssueIB5UQ8Test.java | 16 ++ hutool-cron/pom.xml | 7 +- hutool-crypto/pom.xml | 2 +- hutool-db/pom.xml | 2 +- .../main/java/cn/hutool/db/DialectRunner.java | 2 - .../cn/hutool/db/dialect/impl/DmDialect.java | 1 - .../hutool/db/ds/pooled/PooledConnection.java | 1 - hutool-dfa/pom.xml | 2 +- hutool-extra/pom.xml | 2 +- .../extra/compress/archiver/Archiver.java | 1 - .../cn/hutool/extra/mail/JakartaMail.java | 2 + .../main/java/cn/hutool/extra/mail/Mail.java | 7 +- .../engine/tinypinyin/TinyPinyinEngine.java | 1 - .../cn/hutool/extra/spring/SpringUtil.java | 25 ++ .../main/java/cn/hutool/extra/ssh/Sftp.java | 19 +- .../cn/hutool/extra/mail/JakartaMailTest.java | 9 + .../java/cn/hutool/extra/mail/MailTest.java | 9 + .../src/test/resources/image/Dromara.png | Bin 0 -> 29517 bytes hutool-http/pom.xml | 2 +- .../main/java/cn/hutool/http/HttpUtil.java | 1 - .../cn/hutool/http/useragent/Browser.java | 2 + .../http/useragent/IssueIB3SJFTest.java | 16 ++ hutool-json/pom.xml | 2 +- .../main/java/cn/hutool/json/JSONArray.java | 1 - .../java/cn/hutool/json/Issue3790Test.java | 25 ++ .../java/cn/hutool/json/Issue3795Test.java | 17 ++ hutool-jwt/pom.xml | 2 +- hutool-log/pom.xml | 2 +- hutool-poi/pom.xml | 2 +- .../cn/hutool/poi/excel/sax/ExcelSaxUtil.java | 11 +- .../poi/excel/sax/SheetDataSaxHandler.java | 20 +- .../cn/hutool/poi/excel/IssueIB0EJ9Test.java | 2 +- hutool-script/pom.xml | 2 +- hutool-setting/pom.xml | 2 +- hutool-socket/pom.xml | 2 +- hutool-system/pom.xml | 2 +- pom.xml | 2 +- 70 files changed, 533 insertions(+), 251 deletions(-) create mode 100644 hutool-core/src/main/java/cn/hutool/core/text/csv/CsvTokener.java create mode 100644 hutool-core/src/test/java/cn/hutool/core/date/DateRangeTest.java create mode 100644 hutool-core/src/test/java/cn/hutool/core/date/Issue3798Test.java create mode 100644 hutool-core/src/test/java/cn/hutool/core/text/csv/IssueIB5UQ8Test.java create mode 100644 hutool-extra/src/test/resources/image/Dromara.png create mode 100644 hutool-http/src/test/java/cn/hutool/http/useragent/IssueIB3SJFTest.java create mode 100644 hutool-json/src/test/java/cn/hutool/json/Issue3790Test.java create mode 100644 hutool-json/src/test/java/cn/hutool/json/Issue3795Test.java diff --git a/CHANGELOG.md b/CHANGELOG.md index 30275df375..24ef2f6259 100755 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,25 @@ # 🚀Changelog +------------------------------------------------------------------------------------------------------------- +# 5.8.34(2024-11-25) + +### 🐣新特性 +* 【http 】 增加Windows微信浏览器识别(issue#IB3SJF@Gitee) +* 【core 】 ZipUtil.unzip增加编码容错(issue#I3UZ28@Gitee) +* 【core 】 Calculator兼容`x`字符作为乘号(issue#3787@Github) +* 【poi 】 Excel07SaxReader中,对于小数类型,增加精度判断(issue#IB0EJ9@Gitee) +* 【extra 】 SpringUtil增加getBean重载(issue#3779@Github) +* 【core 】 DataSizeUtil 新增format方法(issue#IB6UUX@Gitee) + +### 🐞Bug修复 +* 【core 】 修复DateUtil.rangeToList中step小于等于0时无限循环问题(issue#3783@Github) +* 【cron 】 修复cron模块依赖log模块问题 +* 【extra 】 修复MailUtil发送html格式邮件无法正常展示图片问题(pr#1279@Gitee) +* 【core 】 【可能的向下兼容问题】修复双引号转义符转义错误问题,修改规则后,对非闭合双引号字段的策略变更,如"aa,则被识别为aa(issue#IB5UQ8@Gitee) +* 【extra 】 修复Sftp中传入Session重连时逻辑错误问题(issue#IB69U8@Gitee) +* 【json 】 修复JSONUtil.toBean()中将JSON数组字符串转Map对象返回错误问题(issue#3795@Github) + ------------------------------------------------------------------------------------------------------------- # 5.8.33(2024-11-05) diff --git a/README-EN.md b/README-EN.md index 1e201f42ef..e97a535e93 100755 --- a/README-EN.md +++ b/README-EN.md @@ -40,7 +40,7 @@

- +

@@ -150,18 +150,18 @@ We provide the T-Shirt and Sweater with Hutool Logo, please visit the shop: cn.hutool hutool-all - 5.8.33 + 5.8.34 ``` ### 🍐Gradle ``` -implementation 'cn.hutool:hutool-all:5.8.33' +implementation 'cn.hutool:hutool-all:5.8.34' ``` ## 📥Download -- [Maven Repo](https://repo1.maven.org/maven2/cn/hutool/hutool-all/5.8.33/) +- [Maven Repo](https://repo1.maven.org/maven2/cn/hutool/hutool-all/5.8.34/) > 🔔️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 67b4a553cd..4f02853538 100755 --- a/README.md +++ b/README.md @@ -40,7 +40,7 @@

- +

@@ -143,20 +143,20 @@ Hutool = Hu + tool,是原公司项目底层代码剥离后的开源库,“Hu cn.hutool hutool-all - 5.8.33 + 5.8.34 ``` ### 🍐Gradle ``` -implementation 'cn.hutool:hutool-all:5.8.33' +implementation 'cn.hutool:hutool-all:5.8.34' ``` ### 📥下载jar 点击以下链接,下载`hutool-all-X.X.X.jar`即可: -- [Maven中央库](https://repo1.maven.org/maven2/cn/hutool/hutool-all/5.8.33/) +- [Maven中央库](https://repo1.maven.org/maven2/cn/hutool/hutool-all/5.8.34/) > 🔔️注意 > Hutool 5.x支持JDK8+,对Android平台没有测试,不能保证所有工具类或工具方法可用。 diff --git a/bin/version.txt b/bin/version.txt index ac78cf1a97..8cf2ade4e2 100755 --- a/bin/version.txt +++ b/bin/version.txt @@ -1 +1 @@ -5.8.33 +5.8.34 diff --git a/docs/js/version.js b/docs/js/version.js index 5af72462d4..36dcf0ff4b 100755 --- a/docs/js/version.js +++ b/docs/js/version.js @@ -1 +1 @@ -var version = '5.8.33' \ No newline at end of file +var version = '5.8.34' \ No newline at end of file diff --git a/hutool-all/pom.xml b/hutool-all/pom.xml index 8c23ef85e6..16816432b4 100755 --- a/hutool-all/pom.xml +++ b/hutool-all/pom.xml @@ -9,7 +9,7 @@ cn.hutool hutool-parent - 5.8.33 + 5.8.34-SNAPSHOT hutool-all diff --git a/hutool-aop/pom.xml b/hutool-aop/pom.xml index 130b313230..3b59fff4d6 100755 --- a/hutool-aop/pom.xml +++ b/hutool-aop/pom.xml @@ -9,7 +9,7 @@ cn.hutool hutool-parent - 5.8.33 + 5.8.34-SNAPSHOT hutool-aop diff --git a/hutool-bloomFilter/pom.xml b/hutool-bloomFilter/pom.xml index 2d146dd503..e9174e95f0 100755 --- a/hutool-bloomFilter/pom.xml +++ b/hutool-bloomFilter/pom.xml @@ -9,7 +9,7 @@ cn.hutool hutool-parent - 5.8.33 + 5.8.34-SNAPSHOT hutool-bloomFilter diff --git a/hutool-bom/pom.xml b/hutool-bom/pom.xml index 2b047ad205..70bd043cd3 100755 --- a/hutool-bom/pom.xml +++ b/hutool-bom/pom.xml @@ -9,7 +9,7 @@ cn.hutool hutool-parent - 5.8.33 + 5.8.34-SNAPSHOT hutool-bom diff --git a/hutool-cache/pom.xml b/hutool-cache/pom.xml index d561591363..1c6fdd3e66 100755 --- a/hutool-cache/pom.xml +++ b/hutool-cache/pom.xml @@ -9,7 +9,7 @@ cn.hutool hutool-parent - 5.8.33 + 5.8.34-SNAPSHOT hutool-cache 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 index 7727a1fbdd..2e90c9c4c5 100755 --- a/hutool-cache/src/main/java/cn/hutool/cache/impl/StampedCache.java +++ b/hutool-cache/src/main/java/cn/hutool/cache/impl/StampedCache.java @@ -1,7 +1,6 @@ package cn.hutool.cache.impl; import cn.hutool.core.collection.CopiedIter; -import cn.hutool.core.thread.ThreadUtil; import java.util.Iterator; import java.util.concurrent.locks.StampedLock; diff --git a/hutool-captcha/pom.xml b/hutool-captcha/pom.xml index d21ab174b9..55c92332e8 100755 --- a/hutool-captcha/pom.xml +++ b/hutool-captcha/pom.xml @@ -9,7 +9,7 @@ cn.hutool hutool-parent - 5.8.33 + 5.8.34-SNAPSHOT hutool-captcha diff --git a/hutool-captcha/src/main/java/cn/hutool/captcha/CircleCaptcha.java b/hutool-captcha/src/main/java/cn/hutool/captcha/CircleCaptcha.java index 7f6cf2ed18..f528cf359e 100755 --- a/hutool-captcha/src/main/java/cn/hutool/captcha/CircleCaptcha.java +++ b/hutool-captcha/src/main/java/cn/hutool/captcha/CircleCaptcha.java @@ -4,7 +4,6 @@ import cn.hutool.captcha.generator.RandomGenerator; import cn.hutool.core.img.GraphicsUtil; import cn.hutool.core.img.ImgUtil; -import cn.hutool.core.util.ObjectUtil; import cn.hutool.core.util.RandomUtil; import java.awt.*; diff --git a/hutool-captcha/src/main/java/cn/hutool/captcha/GifCaptcha.java b/hutool-captcha/src/main/java/cn/hutool/captcha/GifCaptcha.java index f6276967af..7da77496c4 100755 --- a/hutool-captcha/src/main/java/cn/hutool/captcha/GifCaptcha.java +++ b/hutool-captcha/src/main/java/cn/hutool/captcha/GifCaptcha.java @@ -5,7 +5,6 @@ import cn.hutool.captcha.generator.RandomGenerator; import cn.hutool.core.img.ImgUtil; import cn.hutool.core.img.gif.AnimatedGifEncoder; -import cn.hutool.core.util.ObjectUtil; import cn.hutool.core.util.RandomUtil; import java.awt.AlphaComposite; diff --git a/hutool-captcha/src/main/java/cn/hutool/captcha/LineCaptcha.java b/hutool-captcha/src/main/java/cn/hutool/captcha/LineCaptcha.java index 55be796cfa..a4d556da42 100755 --- a/hutool-captcha/src/main/java/cn/hutool/captcha/LineCaptcha.java +++ b/hutool-captcha/src/main/java/cn/hutool/captcha/LineCaptcha.java @@ -4,7 +4,6 @@ import cn.hutool.captcha.generator.RandomGenerator; import cn.hutool.core.img.GraphicsUtil; import cn.hutool.core.img.ImgUtil; -import cn.hutool.core.util.ObjectUtil; import cn.hutool.core.util.RandomUtil; import java.awt.*; diff --git a/hutool-core/pom.xml b/hutool-core/pom.xml index 4df6975019..84c3e0d8f1 100755 --- a/hutool-core/pom.xml +++ b/hutool-core/pom.xml @@ -9,7 +9,7 @@ cn.hutool hutool-parent - 5.8.33 + 5.8.34-SNAPSHOT hutool-core diff --git a/hutool-core/src/main/java/cn/hutool/core/comparator/VersionComparator.java b/hutool-core/src/main/java/cn/hutool/core/comparator/VersionComparator.java index 98df7a2d12..0cb62d4cfa 100644 --- a/hutool-core/src/main/java/cn/hutool/core/comparator/VersionComparator.java +++ b/hutool-core/src/main/java/cn/hutool/core/comparator/VersionComparator.java @@ -1,13 +1,10 @@ package cn.hutool.core.comparator; -import cn.hutool.core.convert.Convert; import cn.hutool.core.lang.Version; import cn.hutool.core.util.*; import java.io.Serializable; import java.util.Comparator; -import java.util.List; -import java.util.regex.Pattern; /** * 版本比较器
diff --git a/hutool-core/src/main/java/cn/hutool/core/convert/NumberWordFormatter.java b/hutool-core/src/main/java/cn/hutool/core/convert/NumberWordFormatter.java index d86f21eecc..cc9e6a4bb4 100644 --- a/hutool-core/src/main/java/cn/hutool/core/convert/NumberWordFormatter.java +++ b/hutool-core/src/main/java/cn/hutool/core/convert/NumberWordFormatter.java @@ -1,6 +1,5 @@ package cn.hutool.core.convert; -import cn.hutool.core.lang.Console; import cn.hutool.core.util.NumberUtil; import cn.hutool.core.util.StrUtil; diff --git a/hutool-core/src/main/java/cn/hutool/core/convert/impl/MapConverter.java b/hutool-core/src/main/java/cn/hutool/core/convert/impl/MapConverter.java index 72b4c32b7d..f01a46c049 100644 --- a/hutool-core/src/main/java/cn/hutool/core/convert/impl/MapConverter.java +++ b/hutool-core/src/main/java/cn/hutool/core/convert/impl/MapConverter.java @@ -74,6 +74,11 @@ public MapConverter(Type mapType, Type keyType, Type valueType) { } convertMapToMap((Map) value, map); } else if (BeanUtil.isBean(value.getClass())) { + if(value.getClass().getName().equals("cn.hutool.json.JSONArray")){ + // issue#3795 增加JSONArray转Map错误检查 + throw new UnsupportedOperationException(StrUtil.format("Unsupported {} to Map.", value.getClass().getName())); + } + map = BeanUtil.beanToMap(value); // 二次转换,转换键值类型 map = convertInternal(map); diff --git a/hutool-core/src/main/java/cn/hutool/core/date/DateRange.java b/hutool-core/src/main/java/cn/hutool/core/date/DateRange.java index 0e91132a42..69183f3a91 100644 --- a/hutool-core/src/main/java/cn/hutool/core/date/DateRange.java +++ b/hutool-core/src/main/java/cn/hutool/core/date/DateRange.java @@ -48,6 +48,10 @@ public DateRange(Date start, Date end, DateField unit, int step) { */ public DateRange(Date start, Date end, DateField unit, int step, boolean isIncludeStart, boolean isIncludeEnd) { super(DateUtil.date(start), DateUtil.date(end), (current, end1, index) -> { + if(step <= 0){ + // issue#3783 + return null; + } final DateTime dt = DateUtil.date(start).offsetNew(unit, (index + 1) * step); if (dt.isAfter(end1)) { return null; diff --git a/hutool-core/src/main/java/cn/hutool/core/io/unit/DataSizeUtil.java b/hutool-core/src/main/java/cn/hutool/core/io/unit/DataSizeUtil.java index 2835a80460..3962c56171 100644 --- a/hutool-core/src/main/java/cn/hutool/core/io/unit/DataSizeUtil.java +++ b/hutool-core/src/main/java/cn/hutool/core/io/unit/DataSizeUtil.java @@ -1,5 +1,7 @@ package cn.hutool.core.io.unit; +import cn.hutool.core.util.ArrayUtil; + import java.text.DecimalFormat; /** @@ -35,4 +37,20 @@ public static String format(long size) { return new DecimalFormat("#,##0.##") .format(size / Math.pow(1024, digitGroups)) + " " + DataUnit.UNIT_NAMES[digitGroups]; } + + /** + * 根据单位,将文件大小转换为对应单位的大小 + * + * @param size 文件大小 + * @param fileDataUnit 单位 + * @return 大小 + * @since 5.8.34 + */ + public static String format(Long size, DataUnit fileDataUnit){ + if (size <= 0) { + return "0"; + } + int digitGroups = ArrayUtil.indexOf(DataUnit.UNIT_NAMES,fileDataUnit.getSuffix()); + return new DecimalFormat("##0.##").format(size / Math.pow(1024, digitGroups)) + " " + DataUnit.UNIT_NAMES[digitGroups]; + } } diff --git a/hutool-core/src/main/java/cn/hutool/core/io/unit/DataUnit.java b/hutool-core/src/main/java/cn/hutool/core/io/unit/DataUnit.java index 2163fc94e7..f26ec8e0b0 100644 --- a/hutool-core/src/main/java/cn/hutool/core/io/unit/DataUnit.java +++ b/hutool-core/src/main/java/cn/hutool/core/io/unit/DataUnit.java @@ -44,10 +44,12 @@ public enum DataUnit { */ TERABYTES("TB", DataSize.ofTerabytes(1)); + /** + * 单位后缀 + */ public static final String[] UNIT_NAMES = new String[]{"B", "KB", "MB", "GB", "TB", "PB", "EB"}; private final String suffix; - private final DataSize size; @@ -56,6 +58,16 @@ public enum DataUnit { this.size = size; } + /** + * 单位后缀 + * + * @return 单位后缀 + * @since 5.8.34 + */ + public String getSuffix() { + return this.suffix; + } + DataSize size() { return this.size; } @@ -76,5 +88,4 @@ public static DataUnit fromSuffix(String suffix) { } throw new IllegalArgumentException("Unknown data unit suffix '" + suffix + "'"); } - } diff --git a/hutool-core/src/main/java/cn/hutool/core/lang/Snowflake.java b/hutool-core/src/main/java/cn/hutool/core/lang/Snowflake.java index 7e7f76d126..f44cf83d6c 100755 --- a/hutool-core/src/main/java/cn/hutool/core/lang/Snowflake.java +++ b/hutool-core/src/main/java/cn/hutool/core/lang/Snowflake.java @@ -2,7 +2,6 @@ import cn.hutool.core.date.SystemClock; import cn.hutool.core.lang.id.IdConstants; -import cn.hutool.core.util.IdUtil; import cn.hutool.core.util.RandomUtil; import cn.hutool.core.util.StrUtil; diff --git a/hutool-core/src/main/java/cn/hutool/core/math/Calculator.java b/hutool-core/src/main/java/cn/hutool/core/math/Calculator.java index dbda0cfc5b..ecaf93515f 100644 --- a/hutool-core/src/main/java/cn/hutool/core/math/Calculator.java +++ b/hutool-core/src/main/java/cn/hutool/core/math/Calculator.java @@ -1,5 +1,6 @@ package cn.hutool.core.math; +import cn.hutool.core.util.CharUtil; import cn.hutool.core.util.NumberUtil; import cn.hutool.core.util.StrUtil; @@ -192,6 +193,9 @@ private static String transform(String expression) { arr[i] = '~'; } } + } else if(CharUtil.equals(arr[i], 'x', true)){ + // issue#3787 x转换为* + arr[i] = '*'; } } if (arr[0] == '~' && (arr.length > 1 && arr[1] == '(')) { diff --git a/hutool-core/src/main/java/cn/hutool/core/text/csv/CsvParser.java b/hutool-core/src/main/java/cn/hutool/core/text/csv/CsvParser.java index f58a0a276a..e9344822b7 100755 --- a/hutool-core/src/main/java/cn/hutool/core/text/csv/CsvParser.java +++ b/hutool-core/src/main/java/cn/hutool/core/text/csv/CsvParser.java @@ -1,24 +1,33 @@ +/* + * Copyright (c) 2013-2024 Hutool Team and hutool.cn + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + package cn.hutool.core.text.csv; import cn.hutool.core.collection.ComputeIter; import cn.hutool.core.io.IORuntimeException; -import cn.hutool.core.io.IoUtil; import cn.hutool.core.map.MapUtil; -import cn.hutool.core.text.StrBuilder; import cn.hutool.core.util.CharUtil; -import cn.hutool.core.util.ObjectUtil; +import cn.hutool.core.util.ObjUtil; import cn.hutool.core.util.StrUtil; import java.io.Closeable; import java.io.IOException; import java.io.Reader; import java.io.Serializable; -import java.util.ArrayList; -import java.util.Collections; -import java.util.LinkedHashMap; -import java.util.List; -import java.util.Map; -import java.util.Objects; +import java.util.*; /** * CSV行解析器,参考:FastCSV @@ -30,10 +39,8 @@ public final class CsvParser extends ComputeIter implements Closeable, S private static final int DEFAULT_ROW_CAPACITY = 10; - private final Reader reader; private final CsvReadConfig config; - - private final Buffer buf = new Buffer(IoUtil.DEFAULT_LARGE_BUFFER_SIZE); + private final CsvTokener tokener; /** * 前一个特殊分界字符 */ @@ -45,7 +52,7 @@ public final class CsvParser extends ComputeIter implements Closeable, S /** * 当前读取字段 */ - private final StrBuilder currentField = new StrBuilder(512); + private final StringBuilder currentField = new StringBuilder(512); /** * 标题行 @@ -78,9 +85,9 @@ public final class CsvParser extends ComputeIter implements Closeable, S * @param reader Reader * @param config 配置,null则为默认配置 */ - public CsvParser(final Reader reader, CsvReadConfig config) { - this.reader = Objects.requireNonNull(reader, "reader must not be null"); - this.config = ObjectUtil.defaultIfNull(config, CsvReadConfig::defaultConfig); + public CsvParser(final Reader reader, final CsvReadConfig config) { + this.config = ObjUtil.defaultIfNull(config, CsvReadConfig::defaultConfig); + this.tokener = new CsvTokener(reader); } /** @@ -90,13 +97,13 @@ public CsvParser(final Reader reader, CsvReadConfig config) { * @throws IllegalStateException 如果不解析头部或者没有调用nextRow()方法 */ public List getHeader() { - if (config.headerLineNo < 0) { + if (config.headerLineNo < 0) { throw new IllegalStateException("No header available - header parsing is disabled"); } if (lineNo < config.beginLineNo) { throw new IllegalStateException("No header available - call nextRow() first"); } - return header.fields; + return header.getRawList(); } @Override @@ -107,13 +114,13 @@ protected CsvRow computeNext() { /** * 读取下一行数据 * - * @return CsvRow + * @return CsvRow,{@code null}表示 * @throws IORuntimeException IO读取异常 */ public CsvRow nextRow() throws IORuntimeException { List currentFields; int fieldCount; - while (false == finished) { + while (!finished) { currentFields = readLine(); fieldCount = currentFields.size(); if (fieldCount < 1) { @@ -122,11 +129,11 @@ public CsvRow nextRow() throws IORuntimeException { } // 读取范围校验 - if(lineNo < config.beginLineNo){ + if (lineNo < config.beginLineNo) { // 未达到读取起始行,继续 continue; } - if(lineNo > config.endLineNo){ + if (lineNo > config.endLineNo) { // 超出结束行,读取结束 break; } @@ -175,9 +182,9 @@ private void initHeader(final List currentFields) { String field = currentFields.get(i); if (MapUtil.isNotEmpty(this.config.headerAlias)) { // 自定义别名 - field = ObjectUtil.defaultIfNull(this.config.headerAlias.get(field), field); + field = ObjUtil.defaultIfNull(this.config.headerAlias.get(field), field); } - if (StrUtil.isNotEmpty(field) && false == localHeaderMap.containsKey(field)) { + if (StrUtil.isNotEmpty(field) && !localHeaderMap.containsKey(field)) { localHeaderMap.put(field, i); } } @@ -190,7 +197,7 @@ private void initHeader(final List currentFields) { * 空行是size为1的List,唯一元素是"" * *

- * 行号要考虑注释行和引号包装的内容中的换行 + * 行号要考虑注释行和引号包装的内容中的换行 *

* * @return 一行数据 @@ -199,70 +206,67 @@ private void initHeader(final List currentFields) { private List readLine() throws IORuntimeException { // 矫正行号 // 当一行内容包含多行数据时,记录首行行号,但是读取下一行时,需要把多行内容的行数加上 - if(inQuotesLineCount > 0){ + if (inQuotesLineCount > 0) { this.lineNo += this.inQuotesLineCount; this.inQuotesLineCount = 0; } final List currentFields = new ArrayList<>(maxFieldCount > 0 ? maxFieldCount : DEFAULT_ROW_CAPACITY); - final StrBuilder currentField = this.currentField; - final Buffer buf = this.buf; + final StringBuilder currentField = this.currentField; int preChar = this.preChar;//前一个特殊分界字符 - int copyLen = 0; //拷贝长度 boolean inComment = false; + int c; while (true) { - if (false == buf.hasRemaining()) { - // 此Buffer读取结束,开始读取下一段 - if (copyLen > 0) { - buf.appendTo(currentField, copyLen); - // 此处无需mark,read方法会重置mark - } - if (buf.read(this.reader) < 0) { - // CSV读取结束 - finished = true; - - if (currentField.hasContent() || preChar == config.fieldSeparator) { - //剩余部分作为一个字段 - addField(currentFields, currentField.toStringAndReset()); + c = tokener.next(); + if(c < 0){ + if (currentField.length() > 0 || preChar == config.fieldSeparator) { + if(this.inQuotes){ + // 未闭合的文本包装,在末尾补充包装符 + currentField.append(config.textDelimiter); } - break; - } - //重置 - copyLen = 0; + //剩余部分作为一个字段 + addField(currentFields, currentField.toString()); + currentField.setLength(0); + } + // 读取结束 + this.finished = true; + break; } - final char c = buf.get(); - // 注释行标记 - if(preChar < 0 || preChar == CharUtil.CR || preChar == CharUtil.LF){ + if (preChar < 0 || preChar == CharUtil.CR || preChar == CharUtil.LF) { // 判断行首字符为指定注释字符的注释开始,直到遇到换行符 // 行首分两种,1是preChar < 0表示文本开始,2是换行符后紧跟就是下一行的开始 // issue#IA8WE0 如果注释符出现在包装符内,被认为是普通字符 - if((false == inQuotes) && null != this.config.commentCharacter && c == this.config.commentCharacter){ + if (!inQuotes && null != this.config.commentCharacter && c == this.config.commentCharacter) { inComment = true; } } // 注释行处理 - if(inComment){ + if (inComment) { if (c == CharUtil.CR || c == CharUtil.LF) { // 注释行以换行符为结尾 lineNo++; inComment = false; } // 跳过注释行中的任何字符 - buf.mark(); - preChar = c; continue; } if (inQuotes) { //引号内,作为内容,直到引号结束 if (c == config.textDelimiter) { - // End of quoted text - inQuotes = false; + // issue#IB5UQ8 文本包装符转义 + final int next = tokener.next(); + if(next != config.textDelimiter){ + // 包装结束 + inQuotes = false; + tokener.back(); + } + // https://datatracker.ietf.org/doc/html/rfc4180#section-2 跳过转义符,只保留被转义的包装符 } else { // 字段内容中新行 if (isLineEnd(c, preChar)) { @@ -270,46 +274,34 @@ private List readLine() throws IORuntimeException { } } // 普通字段字符 - copyLen++; + currentField.append((char)c); } else { // 非引号内 if (c == config.fieldSeparator) { //一个字段结束 - if (copyLen > 0) { - buf.appendTo(currentField, copyLen); - copyLen = 0; - } - buf.mark(); - addField(currentFields, currentField.toStringAndReset()); + addField(currentFields, currentField.toString()); + currentField.setLength(0); } else if (c == config.textDelimiter && isFieldBegin(preChar)) { // 引号开始且出现在字段开头 inQuotes = true; - copyLen++; + currentField.append((char)c); } else if (c == CharUtil.CR) { - // \r,直接结束 - if (copyLen > 0) { - buf.appendTo(currentField, copyLen); - } - buf.mark(); - addField(currentFields, currentField.toStringAndReset()); + // \r + addField(currentFields, currentField.toString()); + currentField.setLength(0); preChar = c; break; } else if (c == CharUtil.LF) { // \n if (preChar != CharUtil.CR) { - if (copyLen > 0) { - buf.appendTo(currentField, copyLen); - } - buf.mark(); - addField(currentFields, currentField.toStringAndReset()); + addField(currentFields, currentField.toString()); + currentField.setLength(0); preChar = c; break; } // 前一个字符是\r,已经处理过这个字段了,此处直接跳过 - buf.mark(); } else { - // 普通字符 - copyLen++; + currentField.append((char)c); } } @@ -325,7 +317,7 @@ private List readLine() throws IORuntimeException { @Override public void close() throws IOException { - reader.close(); + tokener.close(); } /** @@ -334,7 +326,7 @@ public void close() throws IOException { * @param currentFields 当前的字段列表(即为行) * @param field 字段 */ - private void addField(List currentFields, String field) { + private void addField(final List currentFields, String field) { final char textDelimiter = this.config.textDelimiter; // 忽略多余引号后的换行符 @@ -342,12 +334,9 @@ private void addField(List currentFields, String field) { if(StrUtil.isWrap(field, textDelimiter)){ field = StrUtil.sub(field, 1, field.length() - 1); - // https://datatracker.ietf.org/doc/html/rfc4180#section-2 - // 第七条规则,只有包装内的包装符需要转义 - field = StrUtil.replace(field, String.valueOf(textDelimiter) + textDelimiter, String.valueOf(textDelimiter)); } - if(this.config.trimField){ + if (this.config.trimField) { // issue#I49M0C@Gitee field = StrUtil.trim(field); } @@ -362,7 +351,7 @@ private void addField(List currentFields, String field) { * @return 是否结束 * @since 5.7.4 */ - private boolean isLineEnd(char c, int preChar) { + private boolean isLineEnd(final int c, final int preChar) { return (c == CharUtil.CR || c == CharUtil.LF) && preChar != CharUtil.CR; } @@ -383,89 +372,4 @@ private boolean isFieldBegin(final int preChar) { || preChar == CharUtil.LF || preChar == CharUtil.CR; } - - /** - * 内部Buffer - * - * @author looly - */ - private static class Buffer implements Serializable{ - private static final long serialVersionUID = 1L; - - final char[] buf; - - /** - * 标记位置,用于读数据 - */ - private int mark; - /** - * 当前位置 - */ - private int position; - /** - * 读取的数据长度,一般小于buf.length,-1表示无数据 - */ - private int limit; - - Buffer(int capacity) { - buf = new char[capacity]; - } - - /** - * 是否还有未读数据 - * - * @return 是否还有未读数据 - */ - public final boolean hasRemaining() { - return position < limit; - } - - /** - * 读取到缓存
- * 全量读取,会重置Buffer中所有数据 - * - * @param reader {@link Reader} - */ - int read(Reader reader) { - int length; - try { - length = reader.read(this.buf); - } catch (IOException e) { - throw new IORuntimeException(e); - } - this.mark = 0; - this.position = 0; - this.limit = length; - return length; - } - - /** - * 先获取当前字符,再将当前位置后移一位
- * 此方法不检查是否到了数组末尾,请自行使用{@link #hasRemaining()}判断。 - * - * @return 当前位置字符 - * @see #hasRemaining() - */ - char get() { - return this.buf[this.position++]; - } - - /** - * 标记位置记为下次读取位置 - */ - void mark() { - this.mark = this.position; - } - - /** - * 将数据追加到{@link StrBuilder},追加结束后需手动调用{@link #mark()} 重置读取位置 - * - * @param builder {@link StrBuilder} - * @param length 追加的长度 - * @see #mark() - */ - void appendTo(StrBuilder builder, int length) { - builder.append(this.buf, this.mark, length); - } - } } diff --git a/hutool-core/src/main/java/cn/hutool/core/text/csv/CsvTokener.java b/hutool-core/src/main/java/cn/hutool/core/text/csv/CsvTokener.java new file mode 100644 index 0000000000..8e957e0222 --- /dev/null +++ b/hutool-core/src/main/java/cn/hutool/core/text/csv/CsvTokener.java @@ -0,0 +1,102 @@ +/* + * Copyright (c) 2024 Hutool Team and hutool.cn + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package cn.hutool.core.text.csv; + +import cn.hutool.core.io.IORuntimeException; +import cn.hutool.core.io.IoUtil; + +import java.io.Closeable; +import java.io.IOException; +import java.io.Reader; + +/** + * CSV解析器,用于解析CSV文件 + * + * @author looly + * @since 5.8.0 + */ +public class CsvTokener implements Closeable { + + private final Reader raw; + /** + * 在Reader的位置(解析到第几个字符) + */ + private long index; + /** + * 前一个字符 + */ + private int prev; + /** + * 是否使用前一个字符 + */ + private boolean usePrev; + + /** + * 构造 + * + * @param reader {@link Reader} + */ + public CsvTokener(final Reader reader) { + this.raw = IoUtil.toBuffered(reader); + } + + /** + * 读取下一个字符,并记录位置 + * + * @return 下一个字符 + */ + public int next() { + if(this.usePrev){ + this.usePrev = false; + }else{ + try { + this.prev = this.raw.read(); + } catch (final IOException e) { + throw new IORuntimeException(e); + } + } + this.index++; + return this.prev; + } + + /** + * 将标记回退到第一个字符 + * + * @throws IllegalStateException 当多次调用back时,抛出此异常 + */ + public void back() throws IllegalStateException { + if (this.usePrev || this.index <= 0) { + throw new IllegalStateException("Stepping back two steps is not supported"); + } + this.index --; + this.usePrev = true; + } + + /** + * 获取当前位置 + * + * @return 位置 + */ + public long getIndex() { + return this.index; + } + + @Override + public void close() throws IOException { + IoUtil.close(this.raw); + } +} diff --git a/hutool-core/src/main/java/cn/hutool/core/util/TypeUtil.java b/hutool-core/src/main/java/cn/hutool/core/util/TypeUtil.java index 8d5317d52f..4ca0f78857 100755 --- a/hutool-core/src/main/java/cn/hutool/core/util/TypeUtil.java +++ b/hutool-core/src/main/java/cn/hutool/core/util/TypeUtil.java @@ -1,6 +1,5 @@ package cn.hutool.core.util; -import cn.hutool.core.lang.Console; import cn.hutool.core.lang.ParameterizedTypeImpl; import cn.hutool.core.lang.reflect.ActualTypeMapperPool; 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 d44059f601..2e0dad0534 100755 --- a/hutool-core/src/main/java/cn/hutool/core/util/ZipUtil.java +++ b/hutool-core/src/main/java/cn/hutool/core/util/ZipUtil.java @@ -34,10 +34,7 @@ import java.util.Enumeration; import java.util.List; import java.util.function.Consumer; -import java.util.zip.ZipEntry; -import java.util.zip.ZipFile; -import java.util.zip.ZipInputStream; -import java.util.zip.ZipOutputStream; +import java.util.zip.*; /** * 压缩工具类 @@ -65,6 +62,17 @@ public static ZipFile toZipFile(File file, Charset charset) { try { return new ZipFile(file, ObjectUtil.defaultIfNull(charset, CharsetUtil.CHARSET_UTF_8)); } catch (IOException e) { + // issue#I3UZ28 可能编码错误提示 + if(e instanceof ZipException){ + if(e.getMessage().contains("invalid CEN header")){ + try { + // 尝试使用不同编码 + return new ZipFile(file, CharsetUtil.CHARSET_UTF_8.equals(charset) ? CharsetUtil.CHARSET_GBK : CharsetUtil.CHARSET_UTF_8); + } catch (final IOException ex) { + throw new IORuntimeException(ex); + } + } + } throw new IORuntimeException(e); } } diff --git a/hutool-core/src/test/java/cn/hutool/core/date/DateRangeTest.java b/hutool-core/src/test/java/cn/hutool/core/date/DateRangeTest.java new file mode 100644 index 0000000000..0a6404d260 --- /dev/null +++ b/hutool-core/src/test/java/cn/hutool/core/date/DateRangeTest.java @@ -0,0 +1,27 @@ +package cn.hutool.core.date; + +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +import java.util.Date; +import java.util.List; + +public class DateRangeTest { + @Test + void issue3783Test() { + final Date start = DateUtil.parse("2024-01-01"); + final Date end = DateUtil.parse("2024-02-01"); + final List dateTimes = DateUtil.rangeToList(start, end, DateField.DAY_OF_MONTH, 0); + Assertions.assertEquals(1, dateTimes.size()); + Assertions.assertEquals("2024-01-01 00:00:00", dateTimes.get(0).toString()); + } + + @Test + void issue3783Test2() { + final Date start = DateUtil.parse("2024-01-01"); + final Date end = DateUtil.parse("2024-02-01"); + final List dateTimes = DateUtil.rangeToList(start, end, DateField.DAY_OF_MONTH, -2); + Assertions.assertEquals(1, dateTimes.size()); + Assertions.assertEquals("2024-01-01 00:00:00", dateTimes.get(0).toString()); + } +} diff --git a/hutool-core/src/test/java/cn/hutool/core/date/Issue3798Test.java b/hutool-core/src/test/java/cn/hutool/core/date/Issue3798Test.java new file mode 100644 index 0000000000..b041fc3dab --- /dev/null +++ b/hutool-core/src/test/java/cn/hutool/core/date/Issue3798Test.java @@ -0,0 +1,22 @@ +package cn.hutool.core.date; + +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +import java.util.TimeZone; + +public class Issue3798Test { + @Test + void parseTest() { + final String iso_datetime1 = "2000-01-01T12:00:00+08:00"; + final DateTime parse1 = DateUtil.parse(iso_datetime1); + Assertions.assertEquals(TimeZone.getTimeZone("GMT+08:00"), parse1.getTimeZone()); + Assertions.assertEquals("2000-01-01 12:00:00", parse1.toString()); + + // 伦敦时间(Greenwich Mean Time, GMT)和北京时间(China Standard Time, CST)之间的时差是8小时。北京时间比伦敦时间快8小时 + final String iso_datetime2 = "2000-01-01T12:00:00+00:00"; + final DateTime parse2 = DateUtil.parse(iso_datetime2); + Assertions.assertEquals(TimeZone.getTimeZone("GMT+00:00"), parse2.getTimeZone()); + Assertions.assertEquals("2000-01-01 20:00:00", parse2.toString(TimeZone.getTimeZone("GMT+08:00"))); + } +} diff --git a/hutool-core/src/test/java/cn/hutool/core/io/unit/DataSizeUtilTest.java b/hutool-core/src/test/java/cn/hutool/core/io/unit/DataSizeUtilTest.java index fdf5575392..388878eb96 100644 --- a/hutool-core/src/test/java/cn/hutool/core/io/unit/DataSizeUtilTest.java +++ b/hutool-core/src/test/java/cn/hutool/core/io/unit/DataSizeUtilTest.java @@ -1,8 +1,9 @@ package cn.hutool.core.io.unit; -import static org.junit.jupiter.api.Assertions.*; import org.junit.jupiter.api.Test; +import static org.junit.jupiter.api.Assertions.assertEquals; + public class DataSizeUtilTest { @Test @@ -61,6 +62,18 @@ public void formatTest(){ assertEquals("1 TB", format); } + @Test + public void formatWithUnitTest(){ + String format = DataSizeUtil.format(Long.MAX_VALUE, DataUnit.TERABYTES); + assertEquals("8388608 TB", format); + + format = DataSizeUtil.format(1024L * 1024 * 1024 * 1024 * 1024, DataUnit.GIGABYTES); + assertEquals("1048576 GB", format); + + format = DataSizeUtil.format(1024L * 1024 * 1024 * 1024, DataUnit.GIGABYTES); + assertEquals("1024 GB", format); + } + @Test public void issueI88Z4ZTest() { final String size = DataSizeUtil.format(10240000); diff --git a/hutool-core/src/test/java/cn/hutool/core/math/CalculatorTest.java b/hutool-core/src/test/java/cn/hutool/core/math/CalculatorTest.java index f1e1a8387f..0e561e3014 100644 --- a/hutool-core/src/test/java/cn/hutool/core/math/CalculatorTest.java +++ b/hutool-core/src/test/java/cn/hutool/core/math/CalculatorTest.java @@ -1,8 +1,9 @@ package cn.hutool.core.math; -import static org.junit.jupiter.api.Assertions.*; import org.junit.jupiter.api.Test; +import static org.junit.jupiter.api.Assertions.assertEquals; + public class CalculatorTest { @Test @@ -55,4 +56,14 @@ public void issue2964Test() { final double calcValue = Calculator.conversion("(11+2)12"); assertEquals(156D, calcValue, 0.001); } + + @Test + void issue3787Test() { + final Calculator calculator1 = new Calculator(); + double result = calculator1.calculate("0+50/100x(1/0.5)"); + assertEquals(1D, result); + + result = calculator1.calculate("0+50/100X(1/0.5)"); + assertEquals(1D, result); + } } diff --git a/hutool-core/src/test/java/cn/hutool/core/text/csv/CsvUtilTest.java b/hutool-core/src/test/java/cn/hutool/core/text/csv/CsvUtilTest.java index 9f75cc2089..e44e492507 100755 --- a/hutool-core/src/test/java/cn/hutool/core/text/csv/CsvUtilTest.java +++ b/hutool-core/src/test/java/cn/hutool/core/text/csv/CsvUtilTest.java @@ -7,7 +7,6 @@ import cn.hutool.core.util.CharsetUtil; import lombok.AllArgsConstructor; import lombok.Data; -import static org.junit.jupiter.api.Assertions.*; import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; @@ -17,6 +16,8 @@ import java.util.Map; import java.util.stream.Collectors; +import static org.junit.jupiter.api.Assertions.assertEquals; + public class CsvUtilTest { @Test @@ -32,7 +33,8 @@ public void readTest() { assertEquals("关注\"对象\"", row0.get(3)); assertEquals("年龄", row0.get(4)); assertEquals("", row0.get(5)); - assertEquals("\"", row0.get(6)); + // 由于"""未闭合包装,因此末尾的换行符被当作包装内的内容,相当于:"""\n",转义后就是"\n + assertEquals("\"\n", row0.get(6)); } @Test @@ -46,7 +48,8 @@ public void readTest2() { assertEquals("关注\"对象\"", csvRow.get(3)); assertEquals("年龄", csvRow.get(4)); assertEquals("", csvRow.get(5)); - assertEquals("\"", csvRow.get(6)); + // 由于"""未闭合包装,因此末尾的换行符被当作包装内的内容,相当于:"""\n",转义后就是"\n + assertEquals("\"\n", csvRow.get(6)); }); } @@ -70,7 +73,8 @@ public void readCsvStr1(){ assertEquals("关注\"对象\"", row0.get(3)); assertEquals("年龄", row0.get(4)); assertEquals("", row0.get(5)); - assertEquals("\"", row0.get(6)); + // 由于"""未闭合包装,因此末尾的换行符被当作包装内的内容,相当于:"""\n",转义后就是"\n + assertEquals("\"\n", row0.get(6)); } @Test @@ -84,7 +88,8 @@ public void readCsvStr2(){ assertEquals("关注\"对象\"", csvRow.get(3)); assertEquals("年龄", csvRow.get(4)); assertEquals("", csvRow.get(5)); - assertEquals("\"", csvRow.get(6)); + // 由于"""未闭合包装,因此末尾的换行符被当作包装内的内容,相当于:"""\n",转义后就是"\n + assertEquals("\"\n", csvRow.get(6)); }); } diff --git a/hutool-core/src/test/java/cn/hutool/core/text/csv/IssueIB5UQ8Test.java b/hutool-core/src/test/java/cn/hutool/core/text/csv/IssueIB5UQ8Test.java new file mode 100644 index 0000000000..daaf7ab38a --- /dev/null +++ b/hutool-core/src/test/java/cn/hutool/core/text/csv/IssueIB5UQ8Test.java @@ -0,0 +1,16 @@ +package cn.hutool.core.text.csv; + +import cn.hutool.core.lang.Console; +import org.junit.jupiter.api.Test; + +import java.io.StringReader; + +public class IssueIB5UQ8Test { + @Test + void parseEscapeTest() { + String csv = "\"Consultancy, 10\"\",, food\""; + final CsvReader reader = CsvUtil.getReader(new StringReader(csv)); + final String s = reader.read().getRow(0).get(0); + Console.log(s); + } +} diff --git a/hutool-cron/pom.xml b/hutool-cron/pom.xml index a31c341652..c945f9637a 100755 --- a/hutool-cron/pom.xml +++ b/hutool-cron/pom.xml @@ -9,7 +9,7 @@ cn.hutool hutool-parent - 5.8.33 + 5.8.34-SNAPSHOT hutool-cron @@ -26,6 +26,11 @@ hutool-core ${project.parent.version} + + cn.hutool + hutool-log + ${project.parent.version} + cn.hutool hutool-setting diff --git a/hutool-crypto/pom.xml b/hutool-crypto/pom.xml index 6b1e2c6ed8..33def367a0 100755 --- a/hutool-crypto/pom.xml +++ b/hutool-crypto/pom.xml @@ -9,7 +9,7 @@ cn.hutool hutool-parent - 5.8.33 + 5.8.34-SNAPSHOT hutool-crypto diff --git a/hutool-db/pom.xml b/hutool-db/pom.xml index 2bda607420..d885d9db03 100755 --- a/hutool-db/pom.xml +++ b/hutool-db/pom.xml @@ -9,7 +9,7 @@ cn.hutool hutool-parent - 5.8.33 + 5.8.34-SNAPSHOT hutool-db diff --git a/hutool-db/src/main/java/cn/hutool/db/DialectRunner.java b/hutool-db/src/main/java/cn/hutool/db/DialectRunner.java index c0c7906e65..ea9dfd9523 100644 --- a/hutool-db/src/main/java/cn/hutool/db/DialectRunner.java +++ b/hutool-db/src/main/java/cn/hutool/db/DialectRunner.java @@ -2,10 +2,8 @@ import cn.hutool.core.lang.Assert; import cn.hutool.core.lang.PatternPool; -import cn.hutool.core.lang.RegexPool; import cn.hutool.core.map.MapUtil; import cn.hutool.core.util.ArrayUtil; -import cn.hutool.core.util.ReUtil; import cn.hutool.core.util.StrUtil; import cn.hutool.db.dialect.Dialect; import cn.hutool.db.dialect.DialectFactory; diff --git a/hutool-db/src/main/java/cn/hutool/db/dialect/impl/DmDialect.java b/hutool-db/src/main/java/cn/hutool/db/dialect/impl/DmDialect.java index 111d0682a2..06ac9e7ef7 100644 --- a/hutool-db/src/main/java/cn/hutool/db/dialect/impl/DmDialect.java +++ b/hutool-db/src/main/java/cn/hutool/db/dialect/impl/DmDialect.java @@ -3,7 +3,6 @@ import cn.hutool.core.lang.Assert; import cn.hutool.core.util.StrUtil; import cn.hutool.db.Entity; -import cn.hutool.db.Page; import cn.hutool.db.StatementUtil; import cn.hutool.db.dialect.DialectName; import cn.hutool.db.sql.SqlBuilder; diff --git a/hutool-db/src/main/java/cn/hutool/db/ds/pooled/PooledConnection.java b/hutool-db/src/main/java/cn/hutool/db/ds/pooled/PooledConnection.java index ffca393902..d94bf27c9e 100644 --- a/hutool-db/src/main/java/cn/hutool/db/ds/pooled/PooledConnection.java +++ b/hutool-db/src/main/java/cn/hutool/db/ds/pooled/PooledConnection.java @@ -1,7 +1,6 @@ package cn.hutool.db.ds.pooled; import cn.hutool.core.map.MapUtil; -import cn.hutool.core.util.ClassUtil; import cn.hutool.core.util.StrUtil; import cn.hutool.db.DbRuntimeException; import cn.hutool.db.DbUtil; diff --git a/hutool-dfa/pom.xml b/hutool-dfa/pom.xml index 0fb5e6b44d..992185a0ed 100755 --- a/hutool-dfa/pom.xml +++ b/hutool-dfa/pom.xml @@ -9,7 +9,7 @@ cn.hutool hutool-parent - 5.8.33 + 5.8.34-SNAPSHOT hutool-dfa diff --git a/hutool-extra/pom.xml b/hutool-extra/pom.xml index 26b110a5b1..208f86684c 100755 --- a/hutool-extra/pom.xml +++ b/hutool-extra/pom.xml @@ -9,7 +9,7 @@ cn.hutool hutool-parent - 5.8.33 + 5.8.34-SNAPSHOT hutool-extra diff --git a/hutool-extra/src/main/java/cn/hutool/extra/compress/archiver/Archiver.java b/hutool-extra/src/main/java/cn/hutool/extra/compress/archiver/Archiver.java index d3eec1df5d..dd8fa12256 100755 --- a/hutool-extra/src/main/java/cn/hutool/extra/compress/archiver/Archiver.java +++ b/hutool-extra/src/main/java/cn/hutool/extra/compress/archiver/Archiver.java @@ -1,7 +1,6 @@ package cn.hutool.extra.compress.archiver; import cn.hutool.core.lang.Filter; -import cn.hutool.core.util.StrUtil; import java.io.Closeable; import java.io.File; diff --git a/hutool-extra/src/main/java/cn/hutool/extra/mail/JakartaMail.java b/hutool-extra/src/main/java/cn/hutool/extra/mail/JakartaMail.java index 8f73611ec0..99a74280ab 100644 --- a/hutool-extra/src/main/java/cn/hutool/extra/mail/JakartaMail.java +++ b/hutool-extra/src/main/java/cn/hutool/extra/mail/JakartaMail.java @@ -268,6 +268,8 @@ public JakartaMail setAttachments(DataSource... attachments) { if (StrUtil.startWith(attachment.getContentType(), "image/")) { // 图片附件,用于正文中引用图片 bodyPart.setContentID(nameEncoded); + // 图片附件设置内联,否则无法正常引用图片 + bodyPart.setDisposition(MimeBodyPart.INLINE); } this.multipart.addBodyPart(bodyPart); } 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 2d71b2dccd..535300bf9d 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 @@ -8,10 +8,7 @@ import cn.hutool.core.util.ObjectUtil; import cn.hutool.core.util.StrUtil; -import javax.activation.DataHandler; -import javax.activation.DataSource; -import javax.activation.FileDataSource; -import javax.activation.FileTypeMap; +import javax.activation.*; import javax.mail.Address; import javax.mail.MessagingException; import javax.mail.Multipart; @@ -273,6 +270,8 @@ public Mail setAttachments(DataSource... attachments) { if (StrUtil.startWith(attachment.getContentType(), "image/")) { // 图片附件,用于正文中引用图片 bodyPart.setContentID(nameEncoded); + // 图片附件设置内联,否则无法正常引用图片 + bodyPart.setDisposition(MimeBodyPart.INLINE); } this.multipart.addBodyPart(bodyPart); } diff --git a/hutool-extra/src/main/java/cn/hutool/extra/pinyin/engine/tinypinyin/TinyPinyinEngine.java b/hutool-extra/src/main/java/cn/hutool/extra/pinyin/engine/tinypinyin/TinyPinyinEngine.java index 2ecf0dd1cb..0ac8b0c525 100644 --- a/hutool-extra/src/main/java/cn/hutool/extra/pinyin/engine/tinypinyin/TinyPinyinEngine.java +++ b/hutool-extra/src/main/java/cn/hutool/extra/pinyin/engine/tinypinyin/TinyPinyinEngine.java @@ -1,6 +1,5 @@ package cn.hutool.extra.pinyin.engine.tinypinyin; -import cn.hutool.core.lang.Opt; import cn.hutool.core.util.StrUtil; import cn.hutool.extra.pinyin.PinyinEngine; import com.github.promeg.pinyinhelper.Pinyin; diff --git a/hutool-extra/src/main/java/cn/hutool/extra/spring/SpringUtil.java b/hutool-extra/src/main/java/cn/hutool/extra/spring/SpringUtil.java index abbdbda40d..6a625dec83 100755 --- a/hutool-extra/src/main/java/cn/hutool/extra/spring/SpringUtil.java +++ b/hutool-extra/src/main/java/cn/hutool/extra/spring/SpringUtil.java @@ -122,6 +122,19 @@ public static T getBean(Class clazz) { return getBeanFactory().getBean(clazz); } + /** + * 通过class获取Bean + * + * @param Bean类型 + * @param clazz Bean类 + * @param args 创建bean需要的参数属性 + * @return Bean对象 + * @since 5.8.34 + */ + public static T getBean(Class clazz, Object... args) { + return getBeanFactory().getBean(clazz, args); + } + /** * 通过name,以及Clazz返回指定的Bean * @@ -134,6 +147,18 @@ public static T getBean(String name, Class clazz) { return getBeanFactory().getBean(name, clazz); } + /** + * 通过name,以及Clazz返回指定的Bean + * + * @param name Bean名称 + * @param args 创建bean需要的参数属性 + * @return Bean对象 + * @since 5.8.34 + */ + public static Object getBean(String name, Object... args) { + return getBeanFactory().getBean(name, args); + } + /** * 通过类型参考返回带泛型参数的Bean * 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 bbca18dd0b..865f216d44 100755 --- a/hutool-extra/src/main/java/cn/hutool/extra/ssh/Sftp.java +++ b/hutool-extra/src/main/java/cn/hutool/extra/ssh/Sftp.java @@ -8,13 +8,9 @@ import cn.hutool.extra.ftp.AbstractFtp; import cn.hutool.extra.ftp.FtpConfig; import cn.hutool.extra.ftp.FtpException; -import com.jcraft.jsch.ChannelSftp; +import com.jcraft.jsch.*; import com.jcraft.jsch.ChannelSftp.LsEntry; import com.jcraft.jsch.ChannelSftp.LsEntrySelector; -import com.jcraft.jsch.Session; -import com.jcraft.jsch.SftpATTRS; -import com.jcraft.jsch.SftpException; -import com.jcraft.jsch.SftpProgressMonitor; import java.io.File; import java.io.InputStream; @@ -111,7 +107,7 @@ public Sftp(Session session) { * @since 4.1.14 */ public Sftp(Session session, Charset charset) { - super(FtpConfig.create().setCharset(charset)); + super(FtpConfig.create().setCharset(charset).setHost(session.getHost()).setPort(session.getPort())); init(session, charset); } @@ -172,6 +168,17 @@ public void init(String sshHost, int sshPort, String sshUser, String sshPass, Ch * @since 5.3.3 */ public void init() { + // issue#IB69U8 如果用户传入Session对象,则不能使用配置初始化,而是尝试重新连接 + if(StrUtil.isEmpty(this.ftpConfig.getHost()) && null != this.session){ + try { + this.session.connect((int) this.ftpConfig.getConnectionTimeout()); + } catch (JSchException e) { + throw new JschRuntimeException(e); + } + init(this.session, this.ftpConfig.getCharset()); + return; + } + init(this.ftpConfig); } diff --git a/hutool-extra/src/test/java/cn/hutool/extra/mail/JakartaMailTest.java b/hutool-extra/src/test/java/cn/hutool/extra/mail/JakartaMailTest.java index ec0824aacb..20b419cfff 100644 --- a/hutool-extra/src/test/java/cn/hutool/extra/mail/JakartaMailTest.java +++ b/hutool-extra/src/test/java/cn/hutool/extra/mail/JakartaMailTest.java @@ -38,6 +38,15 @@ public void sendWithImageTest() { JakartaMailUtil.sendHtml("hutool@foxmail.com", "测试", "

邮件来自Hutool测试

", map); } + @Test + @Disabled + public void sendHtmlWithImageTest() { + Map map = new HashMap<>(); + InputStream in = getClass().getClassLoader().getResourceAsStream("image/Dromara.png"); + map.put("", in); + JakartaMailUtil.sendHtml("hutool@foxmail.com;li7hai26@outlook.com", "测试", "

邮件来自Hutool测试

", map); + } + @Test @Disabled public void sendHtmlTest() { diff --git a/hutool-extra/src/test/java/cn/hutool/extra/mail/MailTest.java b/hutool-extra/src/test/java/cn/hutool/extra/mail/MailTest.java index 9784b70afc..3a29c9ec26 100644 --- a/hutool-extra/src/test/java/cn/hutool/extra/mail/MailTest.java +++ b/hutool-extra/src/test/java/cn/hutool/extra/mail/MailTest.java @@ -38,6 +38,15 @@ public void sendWithImageTest() { MailUtil.sendHtml("hutool@foxmail.com", "测试", "

邮件来自Hutool测试

", map); } + @Test + @Disabled + public void sendHtmlWithImageTest() { + Map map = new HashMap<>(); + InputStream in = getClass().getClassLoader().getResourceAsStream("image/Dromara.png"); + map.put("", in); + MailUtil.sendHtml("hutool@foxmail.com;li7hai26@outlook.com", "测试", "

邮件来自Hutool测试

", map); + } + @Test @Disabled public void sendHtmlTest() { diff --git a/hutool-extra/src/test/resources/image/Dromara.png b/hutool-extra/src/test/resources/image/Dromara.png new file mode 100644 index 0000000000000000000000000000000000000000..b5c594ff938bac95bf0b97adf7d4bb32dc7fb36d GIT binary patch literal 29517 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 literal 0 HcmV?d00001 diff --git a/hutool-http/pom.xml b/hutool-http/pom.xml index 18d5cb2382..ece9531215 100755 --- a/hutool-http/pom.xml +++ b/hutool-http/pom.xml @@ -9,7 +9,7 @@ cn.hutool hutool-parent - 5.8.33 + 5.8.34-SNAPSHOT hutool-http diff --git a/hutool-http/src/main/java/cn/hutool/http/HttpUtil.java b/hutool-http/src/main/java/cn/hutool/http/HttpUtil.java index 45609ccb8d..de88d1630b 100755 --- a/hutool-http/src/main/java/cn/hutool/http/HttpUtil.java +++ b/hutool-http/src/main/java/cn/hutool/http/HttpUtil.java @@ -5,7 +5,6 @@ import cn.hutool.core.io.FileUtil; import cn.hutool.core.io.IoUtil; import cn.hutool.core.io.StreamProgress; -import cn.hutool.core.lang.Console; import cn.hutool.core.map.MapUtil; import cn.hutool.core.net.RFC3986; import cn.hutool.core.net.url.UrlQuery; diff --git a/hutool-http/src/main/java/cn/hutool/http/useragent/Browser.java b/hutool-http/src/main/java/cn/hutool/http/useragent/Browser.java index 3eef7862b9..e6f4d7b9a2 100755 --- a/hutool-http/src/main/java/cn/hutool/http/useragent/Browser.java +++ b/hutool-http/src/main/java/cn/hutool/http/useragent/Browser.java @@ -31,6 +31,8 @@ public class Browser extends UserAgentInfo { // 部分特殊浏览器是基于安卓、Iphone等的,需要优先判断 // 企业微信 企业微信使用微信浏览器内核,会包含 MicroMessenger 所以要放在前面 new Browser("wxwork", "wxwork", "wxwork\\/([\\d\\w\\.\\-]+)"), + // issue#IB3SJF 微信电脑端 + new Browser("WindowsWechat", "WindowsWechat", "MicroMessenger" + Other_Version), // 微信 new Browser("MicroMessenger", "MicroMessenger", Other_Version), // 微信小程序 diff --git a/hutool-http/src/test/java/cn/hutool/http/useragent/IssueIB3SJFTest.java b/hutool-http/src/test/java/cn/hutool/http/useragent/IssueIB3SJFTest.java new file mode 100644 index 0000000000..7af4c91e78 --- /dev/null +++ b/hutool-http/src/test/java/cn/hutool/http/useragent/IssueIB3SJFTest.java @@ -0,0 +1,16 @@ +package cn.hutool.http.useragent; + +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +public class IssueIB3SJFTest { + @Test + void isMobileTest() { + String str="Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/122.0.0.0 Safari/537.36 NetType/WIFI " + + "MicroMessenger/7.0.20.1781(0x6700143B) WindowsWechat(0x63090c11) XWEB/11275 Flue"; + UserAgent ua = UserAgentUtil.parse(str); + + Assertions.assertFalse(ua.isMobile()); + Assertions.assertEquals("7.0.20.1781", ua.getBrowser().getVersion(str)); + } +} diff --git a/hutool-json/pom.xml b/hutool-json/pom.xml index 92e4f3af8b..fbf76976dc 100755 --- a/hutool-json/pom.xml +++ b/hutool-json/pom.xml @@ -9,7 +9,7 @@ cn.hutool hutool-parent - 5.8.33 + 5.8.34-SNAPSHOT hutool-json 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 8e0a709bbd..e7371f14fc 100755 --- a/hutool-json/src/main/java/cn/hutool/json/JSONArray.java +++ b/hutool-json/src/main/java/cn/hutool/json/JSONArray.java @@ -2,7 +2,6 @@ import cn.hutool.core.bean.BeanPath; import cn.hutool.core.collection.CollUtil; -import cn.hutool.core.lang.Console; import cn.hutool.core.lang.Filter; import cn.hutool.core.lang.Validator; import cn.hutool.core.lang.mutable.Mutable; diff --git a/hutool-json/src/test/java/cn/hutool/json/Issue3790Test.java b/hutool-json/src/test/java/cn/hutool/json/Issue3790Test.java new file mode 100644 index 0000000000..46132355c7 --- /dev/null +++ b/hutool-json/src/test/java/cn/hutool/json/Issue3790Test.java @@ -0,0 +1,25 @@ +package cn.hutool.json; + +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +import java.math.BigDecimal; +import java.math.RoundingMode; + +public class Issue3790Test { + @Test + void bigDecimalToStringTest() { + BigDecimal bigDecimal = new BigDecimal("0.01"); + bigDecimal = bigDecimal.setScale(4, RoundingMode.HALF_UP); + + Dto dto = new Dto(); + dto.remain = bigDecimal; + + final String jsonStr = JSONUtil.toJsonStr(dto, JSONConfig.create().setStripTrailingZeros(false)); + Assertions.assertEquals("{\"remain\":0.0100}", jsonStr); + } + + static class Dto { + public BigDecimal remain; + } +} diff --git a/hutool-json/src/test/java/cn/hutool/json/Issue3795Test.java b/hutool-json/src/test/java/cn/hutool/json/Issue3795Test.java new file mode 100644 index 0000000000..6e0ef606fa --- /dev/null +++ b/hutool-json/src/test/java/cn/hutool/json/Issue3795Test.java @@ -0,0 +1,17 @@ +package cn.hutool.json; + +import cn.hutool.core.lang.TypeReference; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +import java.util.Map; + +public class Issue3795Test { + @Test + void toBeanTest() { + String fieldMapping = "[{\"lable\":\"id\",\"value\":\"id\"},{\"lable\":\"name\",\"value\":\"name\"},{\"lable\":\"age\",\"value\":\"age\"}]"; + Assertions.assertThrows(UnsupportedOperationException.class, ()->{ + JSONUtil.toBean(fieldMapping, new TypeReference>() {}, false); + }); + } +} diff --git a/hutool-jwt/pom.xml b/hutool-jwt/pom.xml index c0f0dd96cf..6f5be6e0ff 100755 --- a/hutool-jwt/pom.xml +++ b/hutool-jwt/pom.xml @@ -9,7 +9,7 @@ cn.hutool hutool-parent - 5.8.33 + 5.8.34-SNAPSHOT hutool-jwt diff --git a/hutool-log/pom.xml b/hutool-log/pom.xml index ebda5848de..4cb957f252 100755 --- a/hutool-log/pom.xml +++ b/hutool-log/pom.xml @@ -9,7 +9,7 @@ cn.hutool hutool-parent - 5.8.33 + 5.8.34-SNAPSHOT hutool-log diff --git a/hutool-poi/pom.xml b/hutool-poi/pom.xml index 631555a309..c7c0797c1f 100755 --- a/hutool-poi/pom.xml +++ b/hutool-poi/pom.xml @@ -9,7 +9,7 @@ cn.hutool hutool-parent - 5.8.33 + 5.8.34-SNAPSHOT hutool-poi diff --git a/hutool-poi/src/main/java/cn/hutool/poi/excel/sax/ExcelSaxUtil.java b/hutool-poi/src/main/java/cn/hutool/poi/excel/sax/ExcelSaxUtil.java index ff12045efa..66e5b176b4 100644 --- a/hutool-poi/src/main/java/cn/hutool/poi/excel/sax/ExcelSaxUtil.java +++ b/hutool-poi/src/main/java/cn/hutool/poi/excel/sax/ExcelSaxUtil.java @@ -5,6 +5,7 @@ import cn.hutool.core.exceptions.DependencyException; import cn.hutool.core.io.IORuntimeException; import cn.hutool.core.util.CharUtil; +import cn.hutool.core.util.NumberUtil; import cn.hutool.core.util.StrUtil; import cn.hutool.poi.excel.ExcelDateUtil; import cn.hutool.poi.excel.sax.handler.RowHandler; @@ -264,7 +265,15 @@ private static Number getNumberValue(String value, String numFmtString) { if (StrUtil.isBlank(value)) { return null; } - return getNumberValue(Double.parseDouble(value), numFmtString); + + // issue#IB0EJ9 可能精度丢失,对含有小数的value判断并转为BigDecimal + final double number = Double.parseDouble(value); + if(StrUtil.contains(value, CharUtil.DOT) && !value.equals(Double.toString(number))){ + // 精度丢失 + return NumberUtil.toBigDecimal(value); + } + + return getNumberValue(number, numFmtString); } /** diff --git a/hutool-poi/src/main/java/cn/hutool/poi/excel/sax/SheetDataSaxHandler.java b/hutool-poi/src/main/java/cn/hutool/poi/excel/sax/SheetDataSaxHandler.java index a2734d8198..5dbbeae8e1 100644 --- a/hutool-poi/src/main/java/cn/hutool/poi/excel/sax/SheetDataSaxHandler.java +++ b/hutool-poi/src/main/java/cn/hutool/poi/excel/sax/SheetDataSaxHandler.java @@ -18,9 +18,10 @@ /** * sheetData标签内容读取处理器 * - *
- * <sheetData></sheetData>
- * 
+ *
{@code
+ *   
+ * }
+ * * @since 5.5.3 */ public class SheetDataSaxHandler extends DefaultHandler { @@ -62,7 +63,12 @@ public class SheetDataSaxHandler extends DefaultHandler { // 存储每行的列元素 private List rowCellList = new ArrayList<>(); - public SheetDataSaxHandler(RowHandler rowHandler){ + /** + * 构造 + * + * @param rowHandler 行处理器 + */ + public SheetDataSaxHandler(RowHandler rowHandler) { this.rowHandler = rowHandler; } @@ -156,7 +162,7 @@ public void characters(char[] ch, int start, int length) { lastFormula.append(ch, start, length); break; } - } else{ + } else { // 按理说内容应该为"内容",但是某些特别的XML内容不在v或f标签中,此处做一些兼容 // issue#1303@Github lastContent.append(ch, start, length); @@ -292,8 +298,8 @@ private void setCellType(Attributes attributes) { // 单元格存储格式的索引,对应style.xml中的numFmts元素的子元素索引 final int numFmtIndex = xssfCellStyle.getDataFormat(); this.numFmtString = ObjectUtil.defaultIfNull( - xssfCellStyle.getDataFormatString(), - () -> BuiltinFormats.getBuiltinFormat(numFmtIndex)); + xssfCellStyle.getDataFormatString(), + () -> BuiltinFormats.getBuiltinFormat(numFmtIndex)); if (CellDataType.NUMBER == this.cellDataType && ExcelSaxUtil.isDateFormat(numFmtIndex, numFmtString)) { cellDataType = CellDataType.DATE; } diff --git a/hutool-poi/src/test/java/cn/hutool/poi/excel/IssueIB0EJ9Test.java b/hutool-poi/src/test/java/cn/hutool/poi/excel/IssueIB0EJ9Test.java index dbc32f4e2d..c5f82332e7 100644 --- a/hutool-poi/src/test/java/cn/hutool/poi/excel/IssueIB0EJ9Test.java +++ b/hutool-poi/src/test/java/cn/hutool/poi/excel/IssueIB0EJ9Test.java @@ -9,7 +9,7 @@ public class IssueIB0EJ9Test { @Test @Disabled void saxReadTest() { - ExcelUtil.readBySax(FileUtil.file("d:/test/bbb.xlsx"), "Sheet1", + ExcelUtil.readBySax(FileUtil.file("d:/test/数值型测试.xlsx"), "hcm工资表", (sheetIndex, rowIndex, rowlist) -> Console.log("[{}] [{}] {}", sheetIndex, rowIndex, rowlist)); } } diff --git a/hutool-script/pom.xml b/hutool-script/pom.xml index d858f51be4..92b0c66b2e 100755 --- a/hutool-script/pom.xml +++ b/hutool-script/pom.xml @@ -9,7 +9,7 @@ cn.hutool hutool-parent - 5.8.33 + 5.8.34-SNAPSHOT hutool-script diff --git a/hutool-setting/pom.xml b/hutool-setting/pom.xml index 45bdc10400..570ae96860 100755 --- a/hutool-setting/pom.xml +++ b/hutool-setting/pom.xml @@ -9,7 +9,7 @@ cn.hutool hutool-parent - 5.8.33 + 5.8.34-SNAPSHOT hutool-setting diff --git a/hutool-socket/pom.xml b/hutool-socket/pom.xml index 5437b8189e..36fbbb44ed 100755 --- a/hutool-socket/pom.xml +++ b/hutool-socket/pom.xml @@ -9,7 +9,7 @@ cn.hutool hutool-parent - 5.8.33 + 5.8.34-SNAPSHOT hutool-socket diff --git a/hutool-system/pom.xml b/hutool-system/pom.xml index 58377d7db0..a34035627b 100755 --- a/hutool-system/pom.xml +++ b/hutool-system/pom.xml @@ -9,7 +9,7 @@ cn.hutool hutool-parent - 5.8.33 + 5.8.34-SNAPSHOT hutool-system diff --git a/pom.xml b/pom.xml index 97472b0075..268ebe5d93 100755 --- a/pom.xml +++ b/pom.xml @@ -8,7 +8,7 @@ cn.hutool hutool-parent - 5.8.33 + 5.8.34-SNAPSHOT hutool Hutool是一个小而全的Java工具类库,通过静态方法封装,降低相关API的学习成本,提高工作效率,使Java拥有函数式语言般的优雅,让Java语言也可以“甜甜的”。 https://github.com/dromara/hutool