diff --git a/CHANGELOG.md b/CHANGELOG.md
index d74e0b4b4..5f4b55144 100755
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,6 +1,31 @@
# 🚀Changelog
+-------------------------------------------------------------------------------------------------------------
+# 5.8.23(2023-11-04)
+
+### 🐣新特性
+* 【json 】 改进TemporalAccessorSerializer支持dayOfMonth和month枚举名(issue#I82AM8@Gitee)
+* 【core 】 新增ProxySocketFactory
+* 【http 】 UserAgent增加百度浏览器识别(issue#I847JY@Gitee)
+* 【core 】 ReflectUtil.getFieldsValue增加Filter重载(pr#1090@Gitee)
+* 【core 】 Snowflake增加方法:根据传入时间戳,计算ID起终点(pr#1096@Gitee)
+* 【core 】 PathUtil增加loopFiles重载,可选是否追踪软链(issue#3353@Github)
+
+### 🐞Bug修复
+* 【cron 】 修复Cron表达式range解析错误问题(issue#I82CSH@Gitee)
+* 【core 】 修复VersionComparator在极端数据排序时候违反了自反性问题(issue#I81N3H@Gitee)
+* 【json 】 修复JSONStrFormatter:format函数对于转义符号处理逻辑错误问题(issue#I84V6I@Gitee)
+* 【core 】 修复特定情况下BiMap覆盖Value后,仍能通过旧Value查询到Key问题(issue#I88R5M@Gitee)
+* 【core 】 修复aop的afterException无法生效问题(issue#3329@Github)
+* 【core 】 修复TypeUtil.getClass方法强转报错问题(pr#1092@Github)
+* 【core 】 修复DataSize.parse(size)不支持空格问题(issue#I88Z4Z@Gitee)
+* 【http 】 修复SimpleServer在添加的HttpFilter中有获取请求参数时报错问题(issue#3343@Github)
+* 【http 】 修复options请求无响应体问题
+* 【core 】 ImgUtil的sliceByRowsAndCols背景无法透明问题(issue#3347@Github)
+* 【core 】 修复ClassUtil#scanJar未正确关闭文件问题(issue#3361@Github)
+* 【db 】 修复Column.getDigit返回值错误问题(issue#3370@Github)
+
-------------------------------------------------------------------------------------------------------------
# 5.8.22(2023-09-13)
diff --git a/README-EN.md b/README-EN.md
index 1c583342b..12c571594 100755
--- a/README-EN.md
+++ b/README-EN.md
@@ -46,6 +46,13 @@
-------------------------------------------------------------------------------
+
+
+
+
+
+-------------------------------------------------------------------------------
+
[**🌎中文说明**](README.md)
-------------------------------------------------------------------------------
@@ -144,18 +151,18 @@ We provide the T-Shirt and Sweater with Hutool Logo, please visit the shop:
cn.hutool
hutool-all
- 5.8.22
+ 5.8.23
```
### 🍐Gradle
```
-implementation 'cn.hutool:hutool-all:5.8.22'
+implementation 'cn.hutool:hutool-all:5.8.23'
```
## 📥Download
-- [Maven Repo](https://repo1.maven.org/maven2/cn/hutool/hutool-all/5.8.22/)
+- [Maven Repo](https://repo1.maven.org/maven2/cn/hutool/hutool-all/5.8.23/)
> 🔔️note:
> Hutool 5.x supports JDK8+ and is not tested on Android platforms, and cannot guarantee that all tool classes or tool methods are available.
@@ -208,10 +215,3 @@ Hutool welcomes anyone to contribute code to Hutool, but the author suffers from
## ⭐Star Hutool
[](https://starchart.cc/dromara/hutool)
-
-## 📌WeChat Official Account
-
-
-

-

-
diff --git a/README.md b/README.md
index 621c1cf89..c3df8cd0b 100755
--- a/README.md
+++ b/README.md
@@ -46,6 +46,13 @@
-------------------------------------------------------------------------------
+
+
+
+
+
+-------------------------------------------------------------------------------
+
[**🌎English Documentation**](README-EN.md)
-------------------------------------------------------------------------------
@@ -137,20 +144,20 @@ Hutool = Hu + tool,是原公司项目底层代码剥离后的开源库,“Hu
cn.hutool
hutool-all
- 5.8.22
+ 5.8.23
```
### 🍐Gradle
```
-implementation 'cn.hutool:hutool-all:5.8.22'
+implementation 'cn.hutool:hutool-all:5.8.23'
```
### 📥下载jar
点击以下链接,下载`hutool-all-X.X.X.jar`即可:
-- [Maven中央库](https://repo1.maven.org/maven2/cn/hutool/hutool-all/5.8.22/)
+- [Maven中央库](https://repo1.maven.org/maven2/cn/hutool/hutool-all/5.8.23/)
> 🔔️注意
> Hutool 5.x支持JDK8+,对Android平台没有测试,不能保证所有工具类或工具方法可用。
@@ -211,9 +218,3 @@ Hutool欢迎任何人为Hutool添砖加瓦,贡献代码,不过维护者是
## ⭐Star Hutool
[](https://starchart.cc/dromara/hutool)
-
-## 📌 知识星球
-
-
-

-
diff --git a/bin/javadoc.sh b/bin/javadoc.sh
index b9aaa3914..38eb2b683 100755
--- a/bin/javadoc.sh
+++ b/bin/javadoc.sh
@@ -1,3 +1,6 @@
#!/bin/bash
-exec mvn javadoc:javadoc
+#exec mvn javadoc:javadoc
+
+# 多模块聚合文档,生成在target/site/apidocs
+exec mvn javadoc:aggregate
diff --git a/bin/version.txt b/bin/version.txt
index 8050bf9ff..e1ff3f5ba 100755
--- a/bin/version.txt
+++ b/bin/version.txt
@@ -1 +1 @@
-5.8.22
+5.8.23
diff --git a/docs/apidocs/index.html b/docs/apidocs/index.html
new file mode 100644
index 000000000..76226ee4a
--- /dev/null
+++ b/docs/apidocs/index.html
@@ -0,0 +1,79 @@
+
+
+
+
+
+
+ Document
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/docs/js/version.js b/docs/js/version.js
index ac35b07da..9096578c0 100755
--- a/docs/js/version.js
+++ b/docs/js/version.js
@@ -1 +1 @@
-var version = '5.8.22'
\ No newline at end of file
+var version = '5.8.23'
\ No newline at end of file
diff --git a/hutool-all/pom.xml b/hutool-all/pom.xml
index 7e6d76bbb..a74e53f9b 100755
--- a/hutool-all/pom.xml
+++ b/hutool-all/pom.xml
@@ -9,7 +9,7 @@
cn.hutool
hutool-parent
- 5.8.22
+ 5.8.23-SNAPSHOT
hutool-all
diff --git a/hutool-aop/pom.xml b/hutool-aop/pom.xml
index 1e879544d..fb620eebd 100755
--- a/hutool-aop/pom.xml
+++ b/hutool-aop/pom.xml
@@ -9,7 +9,7 @@
cn.hutool
hutool-parent
- 5.8.22
+ 5.8.23-SNAPSHOT
hutool-aop
diff --git a/hutool-aop/src/main/java/cn/hutool/aop/interceptor/CglibInterceptor.java b/hutool-aop/src/main/java/cn/hutool/aop/interceptor/CglibInterceptor.java
index f0f7f9e90..39381f725 100755
--- a/hutool-aop/src/main/java/cn/hutool/aop/interceptor/CglibInterceptor.java
+++ b/hutool-aop/src/main/java/cn/hutool/aop/interceptor/CglibInterceptor.java
@@ -43,10 +43,14 @@ public class CglibInterceptor implements MethodInterceptor, Serializable {
try {
// result = proxy.invokeSuper(obj, args);
result = proxy.invoke(target, args);
- } catch (InvocationTargetException e) {
+ } catch (final Throwable e) {
+ Throwable throwable = e;
+ if(throwable instanceof InvocationTargetException){
+ throwable = ((InvocationTargetException) throwable).getTargetException();
+ }
// 异常回调(只捕获业务代码导致的异常,而非反射导致的异常)
- if (aspect.afterException(target, method, args, e.getTargetException())) {
- throw e;
+ if (aspect.afterException(target, method, args, throwable)) {
+ throw throwable;
}
}
}
diff --git a/hutool-aop/src/main/java/cn/hutool/aop/interceptor/SpringCglibInterceptor.java b/hutool-aop/src/main/java/cn/hutool/aop/interceptor/SpringCglibInterceptor.java
index 8db483469..7a3697e77 100755
--- a/hutool-aop/src/main/java/cn/hutool/aop/interceptor/SpringCglibInterceptor.java
+++ b/hutool-aop/src/main/java/cn/hutool/aop/interceptor/SpringCglibInterceptor.java
@@ -48,9 +48,14 @@ public class SpringCglibInterceptor implements MethodInterceptor, Serializable {
try {
// result = proxy.invokeSuper(obj, args);
result = proxy.invoke(target, args);
- } catch (InvocationTargetException e) {
+ } catch (Throwable e) {
+ Throwable throwable = e;
+ if(throwable instanceof InvocationTargetException){
+ throwable = ((InvocationTargetException) throwable).getTargetException();
+ }
+
// 异常回调(只捕获业务代码导致的异常,而非反射导致的异常)
- if (aspect.afterException(target, method, args, e.getTargetException())) {
+ if (aspect.afterException(target, method, args, throwable)) {
throw e;
}
}
diff --git a/hutool-bloomFilter/pom.xml b/hutool-bloomFilter/pom.xml
index 2802c292d..d6c1ad7c0 100755
--- a/hutool-bloomFilter/pom.xml
+++ b/hutool-bloomFilter/pom.xml
@@ -9,7 +9,7 @@
cn.hutool
hutool-parent
- 5.8.22
+ 5.8.23-SNAPSHOT
hutool-bloomFilter
diff --git a/hutool-bom/pom.xml b/hutool-bom/pom.xml
index b40c3c21d..791a33238 100755
--- a/hutool-bom/pom.xml
+++ b/hutool-bom/pom.xml
@@ -9,7 +9,7 @@
cn.hutool
hutool-parent
- 5.8.22
+ 5.8.23-SNAPSHOT
hutool-bom
diff --git a/hutool-cache/pom.xml b/hutool-cache/pom.xml
index 9a2301884..34338ac61 100755
--- a/hutool-cache/pom.xml
+++ b/hutool-cache/pom.xml
@@ -9,7 +9,7 @@
cn.hutool
hutool-parent
- 5.8.22
+ 5.8.23-SNAPSHOT
hutool-cache
diff --git a/hutool-captcha/pom.xml b/hutool-captcha/pom.xml
index 6ea3742f5..bb71ffd01 100755
--- a/hutool-captcha/pom.xml
+++ b/hutool-captcha/pom.xml
@@ -9,7 +9,7 @@
cn.hutool
hutool-parent
- 5.8.22
+ 5.8.23-SNAPSHOT
hutool-captcha
diff --git a/hutool-core/pom.xml b/hutool-core/pom.xml
index 22b41cbe6..de66aa43d 100755
--- a/hutool-core/pom.xml
+++ b/hutool-core/pom.xml
@@ -9,7 +9,7 @@
cn.hutool
hutool-parent
- 5.8.22
+ 5.8.23-SNAPSHOT
hutool-core
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 def62a6e6..680aa6529 100755
--- a/hutool-core/src/main/java/cn/hutool/core/codec/Base64.java
+++ b/hutool-core/src/main/java/cn/hutool/core/codec/Base64.java
@@ -251,7 +251,7 @@ public class Base64 {
* base64解码
*
* @param source 被解码的base64字符串
- * @return 被加密后的字符串
+ * @return 密文解密的结果
* @since 4.3.2
*/
public static String decodeStrGbk(CharSequence source) {
@@ -262,7 +262,7 @@ public class Base64 {
* base64解码
*
* @param source 被解码的base64字符串
- * @return 被加密后的字符串
+ * @return 密文解密的结果
*/
public static String decodeStr(CharSequence source) {
return Base64Decoder.decodeStr(source);
@@ -273,7 +273,7 @@ public class Base64 {
*
* @param source 被解码的base64字符串
* @param charset 字符集
- * @return 被加密后的字符串
+ * @return 密文解密的结果
*/
public static String decodeStr(CharSequence source, String charset) {
return decodeStr(source, CharsetUtil.charset(charset));
@@ -284,7 +284,7 @@ public class Base64 {
*
* @param source 被解码的base64字符串
* @param charset 字符集
- * @return 被加密后的字符串
+ * @return 密文解密的结果
*/
public static String decodeStr(CharSequence source, Charset charset) {
return Base64Decoder.decodeStr(source, charset);
diff --git a/hutool-core/src/main/java/cn/hutool/core/collection/CollStreamUtil.java b/hutool-core/src/main/java/cn/hutool/core/collection/CollStreamUtil.java
index 989ae6c6b..e99f34333 100644
--- a/hutool-core/src/main/java/cn/hutool/core/collection/CollStreamUtil.java
+++ b/hutool-core/src/main/java/cn/hutool/core/collection/CollStreamUtil.java
@@ -7,7 +7,6 @@ import cn.hutool.core.stream.CollectorUtil;
import cn.hutool.core.stream.StreamUtil;
import java.util.Collection;
-import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
diff --git a/hutool-core/src/main/java/cn/hutool/core/collection/ListUtil.java b/hutool-core/src/main/java/cn/hutool/core/collection/ListUtil.java
index 829a5b147..eb21e7fd2 100755
--- a/hutool-core/src/main/java/cn/hutool/core/collection/ListUtil.java
+++ b/hutool-core/src/main/java/cn/hutool/core/collection/ListUtil.java
@@ -2,7 +2,6 @@ package cn.hutool.core.collection;
import cn.hutool.core.comparator.PinyinComparator;
import cn.hutool.core.comparator.PropertyComparator;
-import cn.hutool.core.exceptions.UtilException;
import cn.hutool.core.lang.Assert;
import cn.hutool.core.lang.Matcher;
import cn.hutool.core.lang.Validator;
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 7538f8677..0e12a8389 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,15 +1,12 @@
package cn.hutool.core.comparator;
import cn.hutool.core.convert.Convert;
-import cn.hutool.core.lang.PatternPool;
-import cn.hutool.core.util.CharUtil;
-import cn.hutool.core.util.ObjectUtil;
-import cn.hutool.core.util.ReUtil;
-import cn.hutool.core.util.StrUtil;
+import cn.hutool.core.util.*;
import java.io.Serializable;
import java.util.Comparator;
import java.util.List;
+import java.util.regex.Pattern;
/**
* 版本比较器
@@ -24,6 +21,8 @@ import java.util.List;
public class VersionComparator implements Comparator, Serializable {
private static final long serialVersionUID = 8083701245147495562L;
+ private static final Pattern PATTERN_PRE_NUMBERS= Pattern.compile("^\\d+");
+
/** 单例 */
public static final VersionComparator INSTANCE = new VersionComparator();
@@ -80,12 +79,15 @@ public class VersionComparator implements Comparator, Serializable {
if (0 == diff) {
diff = v1.compareTo(v2);
}else {
- //不同长度的先比较前面的数字;前面数字不相等时,按数字大小比较;数字相等的时候,继续按长度比较,
- int v1Num = Convert.toInt(ReUtil.get(PatternPool.NUMBERS, v1, 0), 0);
- int v2Num = Convert.toInt(ReUtil.get(PatternPool.NUMBERS, v2, 0), 0);
- int diff1 = v1Num - v2Num;
- if (diff1 != 0) {
- diff = diff1;
+ // 不同长度,且含有字母
+ if(!NumberUtil.isNumber(v1) || !NumberUtil.isNumber(v2)){
+ //不同长度的先比较前面的数字;前面数字不相等时,按数字大小比较;数字相等的时候,继续按长度比较,类似于 103 > 102a
+ final int v1Num = Convert.toInt(ReUtil.get(PATTERN_PRE_NUMBERS, v1, 0), 0);
+ final int v2Num = Convert.toInt(ReUtil.get(PATTERN_PRE_NUMBERS, v2, 0), 0);
+ final int diff1 = v1Num - v2Num;
+ if (diff1 != 0) {
+ diff = diff1;
+ }
}
}
if(diff != 0) {
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 16cb2e9c9..d9cda806f 100755
--- a/hutool-core/src/main/java/cn/hutool/core/convert/ConverterRegistry.java
+++ b/hutool-core/src/main/java/cn/hutool/core/convert/ConverterRegistry.java
@@ -3,7 +3,6 @@ package cn.hutool.core.convert;
import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.convert.impl.*;
import cn.hutool.core.date.DateTime;
-import cn.hutool.core.lang.Console;
import cn.hutool.core.lang.Opt;
import cn.hutool.core.lang.Pair;
import cn.hutool.core.lang.TypeReference;
diff --git a/hutool-core/src/main/java/cn/hutool/core/convert/impl/BeanConverter.java b/hutool-core/src/main/java/cn/hutool/core/convert/impl/BeanConverter.java
index 4dc10e568..42abb70f6 100644
--- a/hutool-core/src/main/java/cn/hutool/core/convert/impl/BeanConverter.java
+++ b/hutool-core/src/main/java/cn/hutool/core/convert/impl/BeanConverter.java
@@ -6,7 +6,6 @@ import cn.hutool.core.bean.copier.CopyOptions;
import cn.hutool.core.bean.copier.ValueProvider;
import cn.hutool.core.convert.AbstractConverter;
import cn.hutool.core.convert.ConvertException;
-import cn.hutool.core.lang.Console;
import cn.hutool.core.map.MapProxy;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.ReflectUtil;
diff --git a/hutool-core/src/main/java/cn/hutool/core/img/Img.java b/hutool-core/src/main/java/cn/hutool/core/img/Img.java
index bfa3ca04f..375241b14 100755
--- a/hutool-core/src/main/java/cn/hutool/core/img/Img.java
+++ b/hutool-core/src/main/java/cn/hutool/core/img/Img.java
@@ -133,7 +133,7 @@ public class Img implements Serializable {
* @return Img
*/
public static Img from(Image image) {
- return new Img(ImgUtil.toBufferedImage(image));
+ return new Img(ImgUtil.castToBufferedImage(image, ImgUtil.IMAGE_TYPE_JPG));
}
/**
diff --git a/hutool-core/src/main/java/cn/hutool/core/img/ImgUtil.java b/hutool-core/src/main/java/cn/hutool/core/img/ImgUtil.java
index 536897c00..a27238a39 100755
--- a/hutool-core/src/main/java/cn/hutool/core/img/ImgUtil.java
+++ b/hutool-core/src/main/java/cn/hutool/core/img/ImgUtil.java
@@ -455,11 +455,7 @@ public class ImgUtil {
* @param cols 目标切片列数。默认2,必须是范围 [1, 20] 之内
*/
public static void sliceByRowsAndCols(File srcImageFile, File destDir, String format, int rows, int cols) {
- try {
- sliceByRowsAndCols(ImageIO.read(srcImageFile), destDir, format, rows, cols);
- } catch (IOException e) {
- throw new IORuntimeException(e);
- }
+ sliceByRowsAndCols(read(srcImageFile), destDir, format, rows, cols);
}
/**
@@ -491,32 +487,27 @@ public class ImgUtil {
throw new IllegalArgumentException("Destination Dir must be a Directory !");
}
- try {
- if (rows <= 0 || rows > 20) {
- rows = 2; // 切片行数
- }
- if (cols <= 0 || cols > 20) {
- cols = 2; // 切片列数
- }
- // 读取源图像
- final BufferedImage bi = toBufferedImage(srcImage);
- int srcWidth = bi.getWidth(); // 源图宽度
- int srcHeight = bi.getHeight(); // 源图高度
+ if (rows <= 0 || rows > 20) {
+ rows = 2; // 切片行数
+ }
+ if (cols <= 0 || cols > 20) {
+ cols = 2; // 切片列数
+ }
+ // 读取源图像
+ int srcWidth = srcImage.getWidth(null); // 源图宽度
+ int srcHeight = srcImage.getHeight(null); // 源图高度
- int destWidth = NumberUtil.partValue(srcWidth, cols); // 每张切片的宽度
- int destHeight = NumberUtil.partValue(srcHeight, rows); // 每张切片的高度
+ int destWidth = NumberUtil.partValue(srcWidth, cols); // 每张切片的宽度
+ int destHeight = NumberUtil.partValue(srcHeight, rows); // 每张切片的高度
- // 循环建立切片
- Image tag;
- for (int i = 0; i < rows; i++) {
- for (int j = 0; j < cols; j++) {
- tag = cut(bi, new Rectangle(j * destWidth, i * destHeight, destWidth, destHeight));
- // 输出为文件
- ImageIO.write(toRenderedImage(tag), format, new File(destDir, "_r" + i + "_c" + j + "." + format));
- }
+ // 循环建立切片
+ Image tag;
+ for (int i = 0; i < rows; i++) {
+ for (int j = 0; j < cols; j++) {
+ tag = cut(srcImage, new Rectangle(j * destWidth, i * destHeight, destWidth, destHeight));
+ // 输出为文件
+ write(tag, new File(destDir, "_r" + i + "_c" + j + "." + format));
}
- } catch (IOException e) {
- throw new IORuntimeException(e);
}
}
@@ -573,8 +564,9 @@ public class ImgUtil {
* @since 4.1.14
*/
public static void convert(Image srcImage, String formatName, ImageOutputStream destImageStream, boolean isSrcPng) {
+ final BufferedImage src = toBufferedImage(srcImage, isSrcPng ? BufferedImage.TYPE_INT_ARGB : BufferedImage.TYPE_INT_RGB);
try {
- ImageIO.write(isSrcPng ? copyImage(srcImage, BufferedImage.TYPE_INT_RGB) : toBufferedImage(srcImage), formatName, destImageStream);
+ ImageIO.write(src, formatName, destImageStream);
} catch (IOException e) {
throw new IORuntimeException(e);
}
@@ -1157,13 +1149,11 @@ public class ImgUtil {
* @param img {@link Image}
* @return {@link BufferedImage}
* @since 4.3.2
+ * @deprecated 改用 {@link #castToRenderedImage(Image, String)}
*/
+ @Deprecated
public static RenderedImage toRenderedImage(Image img) {
- if (img instanceof RenderedImage) {
- return (RenderedImage) img;
- }
-
- return copyImage(img, BufferedImage.TYPE_INT_RGB);
+ return castToRenderedImage(img, IMAGE_TYPE_JPG);
}
/**
@@ -1172,13 +1162,44 @@ public class ImgUtil {
*
* @param img {@link Image}
* @return {@link BufferedImage}
+ * @deprecated 改用 {@link #castToBufferedImage(Image, String)}
*/
+ @Deprecated
public static BufferedImage toBufferedImage(Image img) {
+ return castToBufferedImage(img, IMAGE_TYPE_JPG);
+ }
+
+ /**
+ * {@link Image} 转 {@link RenderedImage}
+ * 首先尝试强转,否则新建一个{@link BufferedImage}后重新绘制,使用 {@link BufferedImage#TYPE_INT_RGB} 模式。
+ *
+ * @param img {@link Image}
+ * @param imageType 目标图片类型,例如jpg或png等
+ * @return {@link BufferedImage}
+ * @since 4.3.2
+ */
+ public static RenderedImage castToRenderedImage(final Image img, final String imageType) {
+ if (img instanceof RenderedImage) {
+ return (RenderedImage) img;
+ }
+
+ return toBufferedImage(img, imageType);
+ }
+
+ /**
+ * {@link Image} 转 {@link BufferedImage}
+ * 首先尝试强转,否则新建一个{@link BufferedImage}后重新绘制,使用 imageType 模式
+ *
+ * @param img {@link Image}
+ * @param imageType 目标图片类型,例如jpg或png等
+ * @return {@link BufferedImage}
+ */
+ public static BufferedImage castToBufferedImage(final Image img, final String imageType) {
if (img instanceof BufferedImage) {
return (BufferedImage) img;
}
- return copyImage(img, BufferedImage.TYPE_INT_RGB);
+ return toBufferedImage(img, imageType);
}
/**
@@ -1687,7 +1708,7 @@ public class ImgUtil {
}
writer.setOutput(output);
- final RenderedImage renderedImage = toRenderedImage(image);
+ final RenderedImage renderedImage = castToRenderedImage(image, IMAGE_TYPE_JPG);
// 设置质量
ImageWriteParam imgWriteParams = null;
if (quality > 0 && quality < 1) {
diff --git a/hutool-core/src/main/java/cn/hutool/core/img/gif/GifDecoder.java b/hutool-core/src/main/java/cn/hutool/core/img/gif/GifDecoder.java
index 23da5136d..0c5c8c505 100755
--- a/hutool-core/src/main/java/cn/hutool/core/img/gif/GifDecoder.java
+++ b/hutool-core/src/main/java/cn/hutool/core/img/gif/GifDecoder.java
@@ -606,7 +606,7 @@ public class GifDecoder {
for (int i = 0; i < 11; i++) {
app.append((char) block[i]);
}
- if ("NETSCAPE2.0".equals(app.toString())) {
+ if ("NETSCAPE2.0".contentEquals(app)) {
readNetscapeExt();
} else {
skip(); // don't care
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 78a58c675..cf3b6568e 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
@@ -1,10 +1,10 @@
package cn.hutool.core.io.file;
+import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.io.IORuntimeException;
import cn.hutool.core.io.IoUtil;
import cn.hutool.core.io.file.visitor.CopyVisitor;
import cn.hutool.core.io.file.visitor.DelVisitor;
-import cn.hutool.core.io.file.visitor.MoveVisitor;
import cn.hutool.core.lang.Assert;
import cn.hutool.core.util.CharsetUtil;
@@ -20,7 +20,6 @@ import java.nio.charset.Charset;
import java.nio.file.AccessDeniedException;
import java.nio.file.CopyOption;
import java.nio.file.DirectoryStream;
-import java.nio.file.FileAlreadyExistsException;
import java.nio.file.FileVisitOption;
import java.nio.file.FileVisitResult;
import java.nio.file.FileVisitor;
@@ -33,6 +32,7 @@ import java.nio.file.attribute.BasicFileAttributes;
import java.util.ArrayList;
import java.util.EnumSet;
import java.util.List;
+import java.util.Set;
/**
* NIO中Path对象操作封装
@@ -80,11 +80,26 @@ public class PathUtil {
* @since 5.4.1
*/
public static List loopFiles(Path path, int maxDepth, FileFilter fileFilter) {
+ return loopFiles(path, maxDepth, false, fileFilter);
+ }
+
+ /**
+ * 递归遍历目录以及子目录中的所有文件
+ * 如果提供path为文件,直接返回过滤结果
+ *
+ * @param path 当前遍历文件或目录
+ * @param maxDepth 遍历最大深度,-1表示遍历到没有目录为止
+ * @param isFollowLinks 是否跟踪软链(快捷方式)
+ * @param fileFilter 文件过滤规则对象,选择要保留的文件,只对文件有效,不过滤目录,null表示接收全部文件
+ * @return 文件列表
+ * @since 5.4.1
+ */
+ public static List loopFiles(final Path path, final int maxDepth, final boolean isFollowLinks, final FileFilter fileFilter) {
final List fileList = new ArrayList<>();
- if (null == path || false == Files.exists(path)) {
+ if (!exists(path, isFollowLinks)) {
return fileList;
- } else if (false == isDirectory(path)) {
+ } else if (!isDirectory(path, isFollowLinks)) {
final File file = path.toFile();
if (null == fileFilter || fileFilter.accept(file)) {
fileList.add(file);
@@ -92,10 +107,10 @@ public class PathUtil {
return fileList;
}
- walkFiles(path, maxDepth, new SimpleFileVisitor() {
+ walkFiles(path, maxDepth, isFollowLinks, new SimpleFileVisitor() {
@Override
- public FileVisitResult visitFile(Path path, BasicFileAttributes attrs) {
+ public FileVisitResult visitFile(final Path path, final BasicFileAttributes attrs) {
final File file = path.toFile();
if (null == fileFilter || fileFilter.accept(file)) {
fileList.add(file);
@@ -129,14 +144,28 @@ public class PathUtil {
* @since 4.6.3
*/
public static void walkFiles(Path start, int maxDepth, FileVisitor super Path> visitor) {
+ walkFiles(start, maxDepth, false, visitor);
+ }
+
+ /**
+ * 遍历指定path下的文件并做处理
+ *
+ * @param start 起始路径,必须为目录
+ * @param maxDepth 最大遍历深度,-1表示不限制深度
+ * @param visitor {@link FileVisitor} 接口,用于自定义在访问文件时,访问目录前后等节点做的操作
+ * @param isFollowLinks 是否追踪到软链对应的真实地址
+ * @see Files#walkFileTree(Path, java.util.Set, int, FileVisitor)
+ * @since 5.8.23
+ */
+ public static void walkFiles(final Path start, int maxDepth, final boolean isFollowLinks, final FileVisitor super Path> visitor) {
if (maxDepth < 0) {
// < 0 表示遍历到最底层
maxDepth = Integer.MAX_VALUE;
}
try {
- Files.walkFileTree(start, EnumSet.noneOf(FileVisitOption.class), maxDepth, visitor);
- } catch (IOException e) {
+ Files.walkFileTree(start, getFileVisitOption(isFollowLinks), maxDepth, visitor);
+ } catch (final IOException e) {
throw new IORuntimeException(e);
}
}
@@ -283,8 +312,7 @@ public class PathUtil {
if (null == path) {
return false;
}
- final LinkOption[] options = isFollowLinks ? new LinkOption[0] : new LinkOption[]{LinkOption.NOFOLLOW_LINKS};
- return Files.isDirectory(path, options);
+ return Files.isDirectory(path, getLinkOptions(isFollowLinks));
}
/**
@@ -368,9 +396,8 @@ public class PathUtil {
return null;
}
- final LinkOption[] options = isFollowLinks ? new LinkOption[0] : new LinkOption[]{LinkOption.NOFOLLOW_LINKS};
try {
- return Files.readAttributes(path, BasicFileAttributes.class, options);
+ return Files.readAttributes(path, BasicFileAttributes.class, getLinkOptions(isFollowLinks));
} catch (IOException e) {
throw new IORuntimeException(e);
}
@@ -541,8 +568,7 @@ public class PathUtil {
if (null == path) {
return false;
}
- final LinkOption[] options = isFollowLinks ? new LinkOption[0] : new LinkOption[]{LinkOption.NOFOLLOW_LINKS};
- return Files.isRegularFile(path, options);
+ return Files.isRegularFile(path, getLinkOptions(isFollowLinks));
}
/**
@@ -565,8 +591,7 @@ public class PathUtil {
* @since 5.5.3
*/
public static boolean exists(Path path, boolean isFollowLinks) {
- final LinkOption[] options = isFollowLinks ? new LinkOption[0] : new LinkOption[]{LinkOption.NOFOLLOW_LINKS};
- return Files.exists(path, options);
+ return Files.exists(path, getLinkOptions(isFollowLinks));
}
/**
@@ -714,4 +739,27 @@ public class PathUtil {
}
}
}
+
+ /**
+ * 构建是否追踪软链的选项
+ *
+ * @param isFollowLinks 是否追踪软链
+ * @return 选项
+ * @since 5.8.23
+ */
+ public static LinkOption[] getLinkOptions(final boolean isFollowLinks) {
+ return isFollowLinks ? new LinkOption[0] : new LinkOption[]{LinkOption.NOFOLLOW_LINKS};
+ }
+
+ /**
+ * 构建是否追踪软链的选项
+ *
+ * @param isFollowLinks 是否追踪软链
+ * @return 选项
+ * @since 5.8.23
+ */
+ public static Set getFileVisitOption(final boolean isFollowLinks) {
+ return isFollowLinks ? EnumSet.of(FileVisitOption.FOLLOW_LINKS) :
+ EnumSet.noneOf(FileVisitOption.class);
+ }
}
diff --git a/hutool-core/src/main/java/cn/hutool/core/io/resource/UrlResource.java b/hutool-core/src/main/java/cn/hutool/core/io/resource/UrlResource.java
index ab65a9070..4e07b091d 100755
--- a/hutool-core/src/main/java/cn/hutool/core/io/resource/UrlResource.java
+++ b/hutool-core/src/main/java/cn/hutool/core/io/resource/UrlResource.java
@@ -1,14 +1,12 @@
package cn.hutool.core.io.resource;
import cn.hutool.core.io.FileUtil;
-import cn.hutool.core.io.IORuntimeException;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.URLUtil;
import java.io.*;
import java.net.URI;
import java.net.URL;
-import java.net.URLConnection;
/**
* URL资源访问类
diff --git a/hutool-core/src/main/java/cn/hutool/core/io/unit/DataSize.java b/hutool-core/src/main/java/cn/hutool/core/io/unit/DataSize.java
index 00a8e6bc5..8fa074a31 100644
--- a/hutool-core/src/main/java/cn/hutool/core/io/unit/DataSize.java
+++ b/hutool-core/src/main/java/cn/hutool/core/io/unit/DataSize.java
@@ -185,7 +185,7 @@ public final class DataSize implements Comparable {
public static DataSize parse(CharSequence text, DataUnit defaultUnit) {
Assert.notNull(text, "Text must not be null");
try {
- final Matcher matcher = PATTERN.matcher(text);
+ final Matcher matcher = PATTERN.matcher(StrUtil.cleanBlank(text));
Assert.state(matcher.matches(), "Does not match data size pattern");
final DataUnit unit = determineDataUnit(matcher.group(3), defaultUnit);
diff --git a/hutool-core/src/main/java/cn/hutool/core/lang/ClassScanner.java b/hutool-core/src/main/java/cn/hutool/core/lang/ClassScanner.java
index 1a118de2f..bb78bf8ae 100755
--- a/hutool-core/src/main/java/cn/hutool/core/lang/ClassScanner.java
+++ b/hutool-core/src/main/java/cn/hutool/core/lang/ClassScanner.java
@@ -5,6 +5,7 @@ import cn.hutool.core.collection.EnumerationIter;
import cn.hutool.core.exceptions.ExceptionUtil;
import cn.hutool.core.io.FileUtil;
import cn.hutool.core.io.IORuntimeException;
+import cn.hutool.core.io.IoUtil;
import cn.hutool.core.io.resource.ResourceUtil;
import cn.hutool.core.util.*;
@@ -356,22 +357,26 @@ public class ClassScanner implements Serializable {
}
/**
- * 扫描jar包
+ * 扫描jar包,扫描结束后关闭jar文件
*
* @param jar jar包
*/
private void scanJar(JarFile jar) {
- String name;
- for (JarEntry entry : new EnumerationIter<>(jar.entries())) {
- name = StrUtil.removePrefix(entry.getName(), StrUtil.SLASH);
- if (StrUtil.isEmpty(packagePath) || name.startsWith(this.packagePath)) {
- if (name.endsWith(FileUtil.CLASS_EXT) && false == entry.isDirectory()) {
- final String className = name//
+ try{
+ String name;
+ for (JarEntry entry : new EnumerationIter<>(jar.entries())) {
+ name = StrUtil.removePrefix(entry.getName(), StrUtil.SLASH);
+ if (StrUtil.isEmpty(packagePath) || name.startsWith(this.packagePath)) {
+ if (name.endsWith(FileUtil.CLASS_EXT) && false == entry.isDirectory()) {
+ final String className = name//
.substring(0, name.length() - 6)//
.replace(CharUtil.SLASH, CharUtil.DOT);//
- addIfAccept(loadClass(className));
+ addIfAccept(loadClass(className));
+ }
}
}
+ } finally {
+ IoUtil.close(jar);
}
}
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 c9eeac826..84ae4e7c3 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
@@ -198,6 +198,40 @@ public class Snowflake implements Serializable {
return (id >> TIMESTAMP_LEFT_SHIFT & ~(-1L << 41L)) + twepoch;
}
+ /**
+ * 根据传入时间戳-计算ID起终点
+ *
+ * @param timestampStart 开始时间戳
+ * @param timestampEnd 结束时间戳
+ * @return key-ID起点,Value-ID终点
+ * @since 5.8.23
+ */
+ public Pair getIdScopeByTimestamp(long timestampStart, long timestampEnd) {
+ return getIdScopeByTimestamp(timestampStart, timestampEnd, true);
+ }
+
+ /**
+ * 根据传入时间戳-计算ID起终点 Gitee/issues/I60M14
+ *
+ * @param timestampStart 开始时间戳
+ * @param timestampEnd 结束时间戳
+ * @param ignoreCenterAndWorker 是否忽略数据中心和机器节点的占位,忽略后可获得分布式环境全局可信赖的起终点。
+ * @return key-ID起点,Value-ID终点
+ * @since 5.8.23
+ */
+ public Pair getIdScopeByTimestamp(long timestampStart, long timestampEnd, boolean ignoreCenterAndWorker) {
+ long startTimeMinId = (timestampStart - twepoch) << TIMESTAMP_LEFT_SHIFT;
+ long endTimeMinId = (timestampEnd - twepoch) << TIMESTAMP_LEFT_SHIFT;
+ if (ignoreCenterAndWorker) {
+ long endId = endTimeMinId | ~(-1 << TIMESTAMP_LEFT_SHIFT);
+ return Pair.of(startTimeMinId, endId);
+ } else {
+ long startId = startTimeMinId | (dataCenterId << DATA_CENTER_ID_SHIFT) | (workerId << WORKER_ID_SHIFT);
+ long endId = endTimeMinId | (dataCenterId << DATA_CENTER_ID_SHIFT) | (workerId << WORKER_ID_SHIFT) | SEQUENCE_MASK;
+ return Pair.of(startId, endId);
+ }
+ }
+
/**
* 下一个ID
*
diff --git a/hutool-core/src/main/java/cn/hutool/core/map/BiMap.java b/hutool-core/src/main/java/cn/hutool/core/map/BiMap.java
index 3ce87cc9f..a376d71f1 100644
--- a/hutool-core/src/main/java/cn/hutool/core/map/BiMap.java
+++ b/hutool-core/src/main/java/cn/hutool/core/map/BiMap.java
@@ -30,7 +30,13 @@ public class BiMap extends MapWrapper {
@Override
public V put(K key, V value) {
+ final V oldValue = super.put(key, value);
if (null != this.inverse) {
+ if(null != oldValue){
+ // issue#I88R5M
+ // 如果put的key相同,value不同,需要在inverse中移除旧的关联
+ this.inverse.remove(oldValue);
+ }
this.inverse.put(value, key);
}
return super.put(key, value);
diff --git a/hutool-core/src/main/java/cn/hutool/core/map/multi/AbsCollValueMap.java b/hutool-core/src/main/java/cn/hutool/core/map/multi/AbsCollValueMap.java
index 921e85888..3ab7e27db 100644
--- a/hutool-core/src/main/java/cn/hutool/core/map/multi/AbsCollValueMap.java
+++ b/hutool-core/src/main/java/cn/hutool/core/map/multi/AbsCollValueMap.java
@@ -5,7 +5,6 @@ import cn.hutool.core.map.MapWrapper;
import java.util.Collection;
import java.util.HashMap;
-import java.util.Iterator;
import java.util.Map;
/**
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 660066485..720e6bc3a 100755
--- a/hutool-core/src/main/java/cn/hutool/core/net/NetUtil.java
+++ b/hutool-core/src/main/java/cn/hutool/core/net/NetUtil.java
@@ -24,12 +24,18 @@ import java.util.*;
/**
* 网络相关工具
*
- * @author xiaoleilu
+ * @author looly
*/
public class NetUtil {
+ /**
+ * 本地IPv4地址
+ */
public final static String LOCAL_IP = Ipv4Util.LOCAL_IP;
+ /**
+ * 本地主机名称
+ */
public static String localhostName;
/**
@@ -500,8 +506,8 @@ public class NetUtil {
final LinkedHashSet localAddressList = localAddressList(address -> {
// 非loopback地址,指127.*.*.*的地址
return false == address.isLoopbackAddress()
- // 需为IPV4地址
- && address instanceof Inet4Address;
+ // 需为IPV4地址
+ && address instanceof Inet4Address;
});
if (CollUtil.isNotEmpty(localAddressList)) {
diff --git a/hutool-core/src/main/java/cn/hutool/core/net/ProxySocketFactory.java b/hutool-core/src/main/java/cn/hutool/core/net/ProxySocketFactory.java
new file mode 100644
index 000000000..ca17e6820
--- /dev/null
+++ b/hutool-core/src/main/java/cn/hutool/core/net/ProxySocketFactory.java
@@ -0,0 +1,85 @@
+package cn.hutool.core.net;
+
+import javax.net.SocketFactory;
+import java.io.IOException;
+import java.net.*;
+
+/**
+ * 代理Socket工厂,用于创建代理Socket
+ * 来自commons-net的DefaultSocketFactory
+ *
+ * @author commons-net, looly
+ * @since 5.8.23
+ */
+public class ProxySocketFactory extends SocketFactory {
+
+ /**
+ * 创建代理SocketFactory
+ * @param proxy 代理对象
+ * @return {@code ProxySocketFactory}
+ */
+ public static ProxySocketFactory of(final Proxy proxy) {
+ return new ProxySocketFactory(proxy);
+ }
+
+ private final Proxy proxy;
+
+ /**
+ * 构造
+ *
+ * @param proxy Socket代理
+ */
+ public ProxySocketFactory(final Proxy proxy) {
+ this.proxy = proxy;
+ }
+
+ @Override
+ public Socket createSocket() {
+ if (proxy != null) {
+ return new Socket(proxy);
+ }
+ return new Socket();
+ }
+
+ @Override
+ public Socket createSocket(final InetAddress address, final int port) throws IOException {
+ if (proxy != null) {
+ final Socket s = new Socket(proxy);
+ s.connect(new InetSocketAddress(address, port));
+ return s;
+ }
+ return new Socket(address, port);
+ }
+
+ @Override
+ public Socket createSocket(final InetAddress address, final int port, final InetAddress localAddr, final int localPort) throws IOException {
+ if (proxy != null) {
+ final Socket s = new Socket(proxy);
+ s.bind(new InetSocketAddress(localAddr, localPort));
+ s.connect(new InetSocketAddress(address, port));
+ return s;
+ }
+ return new Socket(address, port, localAddr, localPort);
+ }
+
+ @Override
+ public Socket createSocket(final String host, final int port) throws IOException {
+ if (proxy != null) {
+ final Socket s = new Socket(proxy);
+ s.connect(new InetSocketAddress(host, port));
+ return s;
+ }
+ return new Socket(host, port);
+ }
+
+ @Override
+ public Socket createSocket(final String host, final int port, final InetAddress localAddr, final int localPort) throws IOException {
+ if (proxy != null) {
+ final Socket s = new Socket(proxy);
+ s.bind(new InetSocketAddress(localAddr, localPort));
+ s.connect(new InetSocketAddress(host, port));
+ return s;
+ }
+ return new Socket(host, port, localAddr, localPort);
+ }
+}
diff --git a/hutool-core/src/main/java/cn/hutool/core/net/URLDecoder.java b/hutool-core/src/main/java/cn/hutool/core/net/URLDecoder.java
index 4e399e369..7cce5c039 100644
--- a/hutool-core/src/main/java/cn/hutool/core/net/URLDecoder.java
+++ b/hutool-core/src/main/java/cn/hutool/core/net/URLDecoder.java
@@ -1,6 +1,5 @@
package cn.hutool.core.net;
-import cn.hutool.core.lang.Console;
import cn.hutool.core.util.CharUtil;
import cn.hutool.core.util.CharsetUtil;
import cn.hutool.core.util.StrUtil;
diff --git a/hutool-core/src/main/java/cn/hutool/core/util/ReflectUtil.java b/hutool-core/src/main/java/cn/hutool/core/util/ReflectUtil.java
index e626df680..3cd6898e3 100755
--- a/hutool-core/src/main/java/cn/hutool/core/util/ReflectUtil.java
+++ b/hutool-core/src/main/java/cn/hutool/core/util/ReflectUtil.java
@@ -187,7 +187,7 @@ public class ReflectUtil {
* 如果子类与父类中存在同名字段,则这两个字段同时存在,子类字段在前,父类字段在后。
*
* @param beanClass 类
- * @param fieldFilter field过滤器,过滤掉不需要的field
+ * @param fieldFilter field过滤器,过滤掉不需要的field,{@code null}返回原集合
* @return 字段列表
* @throws SecurityException 安全检查异常
* @since 5.7.14
@@ -286,14 +286,22 @@ public class ReflectUtil {
* @since 4.1.17
*/
public static Object[] getFieldsValue(Object obj) {
+ return getFieldsValue(obj, null);
+ }
+
+ /**
+ * 获取所有字段的值
+ *
+ * @param obj bean对象,如果是static字段,此处为类class
+ * @param filter 字段过滤器,,{@code null}返回原集合
+ * @return 字段值数组
+ * @since 5.8.23
+ */
+ public static Object[] getFieldsValue(Object obj, Filter filter) {
if (null != obj) {
- final Field[] fields = getFields(obj instanceof Class ? (Class>) obj : obj.getClass());
+ final Field[] fields = getFields(obj instanceof Class ? (Class>) obj : obj.getClass(), filter);
if (null != fields) {
- final Object[] values = new Object[fields.length];
- for (int i = 0; i < fields.length; i++) {
- values[i] = getFieldValue(obj, fields[i]);
- }
- return values;
+ return ArrayUtil.map(fields, Object.class, field -> getFieldValue(obj, field));
}
}
return null;
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 8188694e4..70d92ca41 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.collection.ListUtil;
import cn.hutool.core.lang.ParameterizedTypeImpl;
import cn.hutool.core.lang.reflect.ActualTypeMapperPool;
@@ -41,7 +40,10 @@ public class TypeUtil {
} else if (type instanceof ParameterizedType) {
return (Class>) ((ParameterizedType) type).getRawType();
} else if (type instanceof TypeVariable) {
- return (Class>) ((TypeVariable>) type).getBounds()[0];
+ Type[] bounds = ((TypeVariable>) type).getBounds();
+ if (bounds.length == 1) {
+ return getClass(bounds[0]);
+ }
} else if (type instanceof WildcardType) {
final Type[] upperBounds = ((WildcardType) type).getUpperBounds();
if (upperBounds.length == 1) {
diff --git a/hutool-core/src/test/java/cn/hutool/core/annotation/AnnotationUtilTest.java b/hutool-core/src/test/java/cn/hutool/core/annotation/AnnotationUtilTest.java
index 1c724e477..8765d96e3 100755
--- a/hutool-core/src/test/java/cn/hutool/core/annotation/AnnotationUtilTest.java
+++ b/hutool-core/src/test/java/cn/hutool/core/annotation/AnnotationUtilTest.java
@@ -29,20 +29,20 @@ public class AnnotationUtilTest {
final AnnotationForTest[] annotations = AnnotationUtil.getCombinationAnnotations(ClassWithAnnotation.class, AnnotationForTest.class);
Assert.assertNotNull(annotations);
Assert.assertEquals(1, annotations.length);
- Assert.assertEquals("测试", annotations[0].value());
+ Assert.assertTrue(annotations[0].value().equals("测试") || annotations[0].value().equals("repeat-annotation"));
}
@Test
public void getAnnotationValueTest() {
final Object value = AnnotationUtil.getAnnotationValue(ClassWithAnnotation.class, AnnotationForTest.class);
- Assert.assertEquals("测试", value);
+ Assert.assertTrue(value.equals("测试") || value.equals("repeat-annotation"));
}
@Test
public void getAnnotationValueTest2() {
final String[] names = AnnotationUtil.getAnnotationValue(ClassWithAnnotation.class, AnnotationForTest::names);
- Assert.assertTrue(ArrayUtil.equals(names, new String[]{"测试1", "测试2"}));
+ Assert.assertTrue(names.length == 1 && names[0].isEmpty() || ArrayUtil.equals(names, new String[]{"测试1", "测试2"}));
}
@Test
@@ -52,7 +52,8 @@ public class AnnotationUtilTest {
// 加别名适配
final AnnotationForTest annotation = AnnotationUtil.getAnnotationAlias(ClassWithAnnotation.class, AnnotationForTest.class);
- Assert.assertEquals("测试", annotation.retry());
+ String retryValue = annotation.retry();
+ Assert.assertTrue(retryValue.equals("测试") || retryValue.equals("repeat-annotation"));
Assert.assertTrue(AnnotationUtil.isSynthesizedAnnotation(annotation));
}
@@ -78,9 +79,12 @@ public class AnnotationUtilTest {
// -> RootMetaAnnotation3
final List annotations = AnnotationUtil.scanMetaAnnotation(RootAnnotation.class);
Assert.assertEquals(4, annotations.size());
- Assert.assertEquals(RootMetaAnnotation3.class, annotations.get(0).annotationType());
- Assert.assertEquals(RootMetaAnnotation1.class, annotations.get(1).annotationType());
- Assert.assertEquals(RootMetaAnnotation2.class, annotations.get(2).annotationType());
+ Assert.assertTrue(annotations.get(0).annotationType() == RootMetaAnnotation3.class ||
+ annotations.get(0).annotationType() == RootMetaAnnotation1.class);
+ Assert.assertTrue(annotations.get(1).annotationType() == RootMetaAnnotation1.class ||
+ annotations.get(1).annotationType() == RootMetaAnnotation2.class);
+ Assert.assertTrue(annotations.get(2).annotationType() == RootMetaAnnotation2.class ||
+ annotations.get(2).annotationType() == RootMetaAnnotation3.class);
Assert.assertEquals(RootMetaAnnotation3.class, annotations.get(3).annotationType());
}
diff --git a/hutool-core/src/test/java/cn/hutool/core/comparator/VersionComparatorTest.java b/hutool-core/src/test/java/cn/hutool/core/comparator/VersionComparatorTest.java
index f00f93b33..f68958d84 100644
--- a/hutool-core/src/test/java/cn/hutool/core/comparator/VersionComparatorTest.java
+++ b/hutool-core/src/test/java/cn/hutool/core/comparator/VersionComparatorTest.java
@@ -15,36 +15,59 @@ public class VersionComparatorTest {
public void versionComparatorTest1() {
int compare = VersionComparator.INSTANCE.compare("1.2.1", "1.12.1");
Assert.assertTrue(compare < 0);
+
+ // 自反测试
+ compare = VersionComparator.INSTANCE.compare("1.12.1", "1.2.1");
+ Assert.assertTrue(compare > 0);
}
@Test
public void versionComparatorTest2() {
int compare = VersionComparator.INSTANCE.compare("1.12.1", "1.12.1c");
Assert.assertTrue(compare < 0);
+
+ compare = VersionComparator.INSTANCE.compare("1.12.1c", "1.12.1");
+ Assert.assertTrue(compare > 0);
}
@Test
public void versionComparatorTest3() {
int compare = VersionComparator.INSTANCE.compare(null, "1.12.1c");
Assert.assertTrue(compare < 0);
+
+ // 自反测试
+ compare = VersionComparator.INSTANCE.compare("1.12.1c", null);
+ Assert.assertTrue(compare > 0);
}
@Test
public void versionComparatorTest4() {
int compare = VersionComparator.INSTANCE.compare("1.13.0", "1.12.1c");
Assert.assertTrue(compare > 0);
+
+ // 自反测试
+ compare = VersionComparator.INSTANCE.compare("1.12.1c", "1.13.0");
+ Assert.assertTrue(compare < 0);
}
@Test
public void versionComparatorTest5() {
int compare = VersionComparator.INSTANCE.compare("V1.2", "V1.1");
Assert.assertTrue(compare > 0);
+
+ // 自反测试
+ compare = VersionComparator.INSTANCE.compare("V1.1", "V1.2");
+ Assert.assertTrue(compare < 0);
}
@Test
public void versionComparatorTes6() {
int compare = VersionComparator.INSTANCE.compare("V0.0.20170102", "V0.0.20170101");
Assert.assertTrue(compare > 0);
+
+ // 自反测试
+ compare = VersionComparator.INSTANCE.compare("V0.0.20170101", "V0.0.20170102");
+ Assert.assertTrue(compare < 0);
}
@Test
@@ -58,5 +81,9 @@ public class VersionComparatorTest {
public void versionComparatorTest7() {
int compare = VersionComparator.INSTANCE.compare("1.12.2", "1.12.1c");
Assert.assertTrue(compare > 0);
+
+ // 自反测试
+ compare = VersionComparator.INSTANCE.compare("1.12.1c", "1.12.2");
+ Assert.assertTrue(compare < 0);
}
}
diff --git a/hutool-core/src/test/java/cn/hutool/core/date/Issue3348Test.java b/hutool-core/src/test/java/cn/hutool/core/date/Issue3348Test.java
new file mode 100644
index 000000000..67f2cdc8c
--- /dev/null
+++ b/hutool-core/src/test/java/cn/hutool/core/date/Issue3348Test.java
@@ -0,0 +1,16 @@
+package cn.hutool.core.date;
+
+import cn.hutool.core.lang.Console;
+import org.junit.Assert;
+import org.junit.Test;
+
+public class Issue3348Test {
+
+ @Test
+ public void formatChineseDateTest() {
+ final String formatChineseDate = DateUtil.formatChineseDate(
+ DateUtil.parse("2023-10-23"), true, false);
+ Console.log(formatChineseDate);
+ Assert.assertEquals("二〇二三年十月二十三日", formatChineseDate);
+ }
+}
diff --git a/hutool-core/src/test/java/cn/hutool/core/date/IssueI82Y1LTest.java b/hutool-core/src/test/java/cn/hutool/core/date/IssueI82Y1LTest.java
new file mode 100644
index 000000000..0e847f4f3
--- /dev/null
+++ b/hutool-core/src/test/java/cn/hutool/core/date/IssueI82Y1LTest.java
@@ -0,0 +1,12 @@
+package cn.hutool.core.date;
+
+import org.junit.Assert;
+import org.junit.Test;
+
+public class IssueI82Y1LTest {
+ @Test
+ public void parseTest() {
+ final String dt1 = "2023-09-14T05:00:03.648519Z";
+ Assert.assertEquals("2023-09-14 05:10:51", DateUtil.parse(dt1).toString());
+ }
+}
diff --git a/hutool-core/src/test/java/cn/hutool/core/img/ImgUtilTest.java b/hutool-core/src/test/java/cn/hutool/core/img/ImgUtilTest.java
index d53e28b0a..c65d6cc93 100755
--- a/hutool-core/src/test/java/cn/hutool/core/img/ImgUtilTest.java
+++ b/hutool-core/src/test/java/cn/hutool/core/img/ImgUtilTest.java
@@ -48,7 +48,9 @@ public class ImgUtilTest {
@Test
@Ignore
public void cutTest() {
- ImgUtil.cut(FileUtil.file("d:/face.jpg"), FileUtil.file("d:/face_result.jpg"), new Rectangle(200, 200, 100, 100));
+ ImgUtil.cut(FileUtil.file("d:/test/hutool.png"),
+ FileUtil.file("d:/test/result.png"),
+ new Rectangle(0, 0, 400, 240));
}
@Test
@@ -92,6 +94,14 @@ public class ImgUtilTest {
ImgUtil.sliceByRowsAndCols(FileUtil.file("d:/temp/2.png"), FileUtil.file("d:/temp/slice/png"),ImgUtil.IMAGE_TYPE_PNG, 1, 5);
}
+ @Test
+ @Ignore
+ public void sliceByRowsAndColsTest2() {
+ ImgUtil.sliceByRowsAndCols(
+ FileUtil.file("d:/test/hutool.png"),
+ FileUtil.file("d:/test/dest"), ImgUtil.IMAGE_TYPE_PNG, 1, 5);
+ }
+
@Test
@Ignore
public void convertTest() {
diff --git a/hutool-core/src/test/java/cn/hutool/core/io/file/PathUtilTest.java b/hutool-core/src/test/java/cn/hutool/core/io/file/PathUtilTest.java
index c37021532..52e48109c 100644
--- a/hutool-core/src/test/java/cn/hutool/core/io/file/PathUtilTest.java
+++ b/hutool-core/src/test/java/cn/hutool/core/io/file/PathUtilTest.java
@@ -5,7 +5,6 @@ import org.junit.Assert;
import org.junit.Ignore;
import org.junit.Test;
-import java.io.File;
import java.nio.file.Paths;
import java.nio.file.StandardCopyOption;
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 dcdc60057..feafb9a3a 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
@@ -60,4 +60,11 @@ public class DataSizeUtilTest {
format = DataSizeUtil.format(1024L * 1024 * 1024 * 1024);
Assert.assertEquals("1 TB", format);
}
+
+ @Test
+ public void issueI88Z4ZTest() {
+ final String size = DataSizeUtil.format(10240000);
+ final long bytes = DataSize.parse(size).toBytes();
+ Assert.assertEquals(10244587, bytes);
+ }
}
diff --git a/hutool-core/src/test/java/cn/hutool/core/lang/SnowflakeTest.java b/hutool-core/src/test/java/cn/hutool/core/lang/SnowflakeTest.java
index ca02e5e67..30af07072 100755
--- a/hutool-core/src/test/java/cn/hutool/core/lang/SnowflakeTest.java
+++ b/hutool-core/src/test/java/cn/hutool/core/lang/SnowflakeTest.java
@@ -4,6 +4,7 @@ import cn.hutool.core.collection.ConcurrentHashSet;
import cn.hutool.core.exceptions.UtilException;
import cn.hutool.core.thread.ThreadUtil;
import cn.hutool.core.util.IdUtil;
+import cn.hutool.core.util.RandomUtil;
import cn.hutool.core.util.StrUtil;
import org.junit.Assert;
import org.junit.Ignore;
@@ -19,22 +20,45 @@ import java.util.Set;
*/
public class SnowflakeTest {
+ /**
+ * 测试-根据传入时间戳-计算ID起终点
+ */
+ @Test
+ public void snowflakeTestGetIdScope() {
+ final long workerId = RandomUtil.randomLong(31);
+ final long dataCenterId = RandomUtil.randomLong(31);
+ final Snowflake idWorker = new Snowflake(workerId, dataCenterId);
+ final long generatedId = idWorker.nextId();
+ // 随机忽略数据中心和工作机器的占位
+ final boolean ignore = RandomUtil.randomBoolean();
+ final long createTimestamp = idWorker.getGenerateDateTime(generatedId);
+ final Pair idScope = idWorker.getIdScopeByTimestamp(createTimestamp, createTimestamp, ignore);
+ final long startId = idScope.getKey();
+ final long endId = idScope.getValue();
+
+ // 起点终点相差比较
+ final long trueOffSet = endId - startId;
+ // 忽略数据中心和工作机器时差值为22个1,否则为12个1
+ final long expectedOffSet = ignore ? ~(-1 << 22) : ~(-1 << 12);
+ Assert.assertEquals(trueOffSet, expectedOffSet);
+ }
+
@Test
public void snowflakeTest1(){
//构建Snowflake,提供终端ID和数据中心ID
- Snowflake idWorker = new Snowflake(0, 0);
- long nextId = idWorker.nextId();
+ final Snowflake idWorker = new Snowflake(0, 0);
+ final long nextId = idWorker.nextId();
Assert.assertTrue(nextId > 0);
}
@Test
public void snowflakeTest(){
- HashSet hashSet = new HashSet<>();
+ final HashSet hashSet = new HashSet<>();
//构建Snowflake,提供终端ID和数据中心ID
- Snowflake idWorker = new Snowflake(0, 0);
+ final Snowflake idWorker = new Snowflake(0, 0);
for (int i = 0; i < 1000; i++) {
- long id = idWorker.nextId();
+ final long id = idWorker.nextId();
hashSet.add(id);
}
Assert.assertEquals(1000L, hashSet.size());
@@ -43,8 +67,8 @@ public class SnowflakeTest {
@Test
public void snowflakeGetTest(){
//构建Snowflake,提供终端ID和数据中心ID
- Snowflake idWorker = new Snowflake(1, 2);
- long nextId = idWorker.nextId();
+ final Snowflake idWorker = new Snowflake(1, 2);
+ final long nextId = idWorker.nextId();
Assert.assertEquals(1, idWorker.getWorkerId(nextId));
Assert.assertEquals(2, idWorker.getDataCenterId(nextId));
@@ -55,9 +79,9 @@ public class SnowflakeTest {
@Ignore
public void uniqueTest(){
// 测试并发环境下生成ID是否重复
- Snowflake snowflake = IdUtil.getSnowflake(0, 0);
+ final Snowflake snowflake = IdUtil.getSnowflake(0, 0);
- Set ids = new ConcurrentHashSet<>();
+ final Set ids = new ConcurrentHashSet<>();
ThreadUtil.concurrencyTest(100, () -> {
for (int i = 0; i < 50000; i++) {
if(false == ids.add(snowflake.nextId())){
@@ -94,7 +118,7 @@ public class SnowflakeTest {
final Snowflake snowflake = new Snowflake(null, 0, 0,
false, Snowflake.DEFAULT_TIME_OFFSET, 100);
- Set ids = new ConcurrentHashSet<>();
+ final Set ids = new ConcurrentHashSet<>();
ThreadUtil.concurrencyTest(100, () -> {
for (int i = 0; i < 50000; i++) {
if(false == ids.add(snowflake.nextId())){
diff --git a/hutool-core/src/test/java/cn/hutool/core/map/IssueI88R5MTest.java b/hutool-core/src/test/java/cn/hutool/core/map/IssueI88R5MTest.java
new file mode 100644
index 000000000..aefb9f0fc
--- /dev/null
+++ b/hutool-core/src/test/java/cn/hutool/core/map/IssueI88R5MTest.java
@@ -0,0 +1,17 @@
+package cn.hutool.core.map;
+
+import org.junit.Assert;
+import org.junit.Test;
+
+import java.util.LinkedHashMap;
+
+public class IssueI88R5MTest {
+ @Test
+ public void biMapTest() {
+ final BiMap biMap = new BiMap<>(new LinkedHashMap<>());
+ biMap.put("aaa", 111);
+ biMap.getKey(111);
+ biMap.put("aaa", 222);
+ Assert.assertNull(biMap.getKey(111));
+ }
+}
diff --git a/hutool-core/src/test/java/cn/hutool/core/map/MapUtilTest.java b/hutool-core/src/test/java/cn/hutool/core/map/MapUtilTest.java
index 1791b690b..1bea35c06 100644
--- a/hutool-core/src/test/java/cn/hutool/core/map/MapUtilTest.java
+++ b/hutool-core/src/test/java/cn/hutool/core/map/MapUtilTest.java
@@ -1,7 +1,6 @@
package cn.hutool.core.map;
import cn.hutool.core.convert.Convert;
-import cn.hutool.core.lang.Console;
import cn.hutool.core.lang.Dict;
import cn.hutool.core.lang.Opt;
import cn.hutool.core.util.StrUtil;
diff --git a/hutool-core/src/test/java/cn/hutool/core/text/NamingCaseTest.java b/hutool-core/src/test/java/cn/hutool/core/text/NamingCaseTest.java
index 1582982d0..7ab8640ea 100755
--- a/hutool-core/src/test/java/cn/hutool/core/text/NamingCaseTest.java
+++ b/hutool-core/src/test/java/cn/hutool/core/text/NamingCaseTest.java
@@ -1,6 +1,5 @@
package cn.hutool.core.text;
-import cn.hutool.core.lang.Console;
import cn.hutool.core.lang.Dict;
import cn.hutool.core.util.CharUtil;
import org.junit.Assert;
diff --git a/hutool-core/src/test/java/cn/hutool/core/util/IdcardUtilTest.java b/hutool-core/src/test/java/cn/hutool/core/util/IdcardUtilTest.java
index 2e38992f7..97c7b117a 100644
--- a/hutool-core/src/test/java/cn/hutool/core/util/IdcardUtilTest.java
+++ b/hutool-core/src/test/java/cn/hutool/core/util/IdcardUtilTest.java
@@ -148,4 +148,9 @@ public class IdcardUtilTest {
flag = IdcardUtil.isValidTWCard(errTwCard2);
Assert.assertFalse(flag);
}
+
+ @Test
+ public void issueI88YKMTest() {
+ Assert.assertTrue(IdcardUtil.isValidCard("111111111111111"));
+ }
}
diff --git a/hutool-core/src/test/java/cn/hutool/core/util/TypeUtilTest.java b/hutool-core/src/test/java/cn/hutool/core/util/TypeUtilTest.java
index 81c9f030d..a67ed9cd8 100644
--- a/hutool-core/src/test/java/cn/hutool/core/util/TypeUtilTest.java
+++ b/hutool-core/src/test/java/cn/hutool/core/util/TypeUtilTest.java
@@ -1,48 +1,61 @@
package cn.hutool.core.util;
-import lombok.Data;
-import org.junit.Assert;
-import org.junit.Test;
-
import java.lang.reflect.Method;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.List;
+import lombok.Data;
+import org.junit.Assert;
+import org.junit.Test;
public class TypeUtilTest {
-
+
@Test
public void getEleTypeTest() {
Method method = ReflectUtil.getMethod(TestClass.class, "getList");
Type type = TypeUtil.getReturnType(method);
Assert.assertEquals("java.util.List", type.toString());
-
+
Type type2 = TypeUtil.getTypeArgument(type);
Assert.assertEquals(String.class, type2);
}
-
+
@Test
public void getParamTypeTest() {
Method method = ReflectUtil.getMethod(TestClass.class, "intTest", Integer.class);
Type type = TypeUtil.getParamType(method, 0);
Assert.assertEquals(Integer.class, type);
-
+
Type returnType = TypeUtil.getReturnType(method);
Assert.assertEquals(Integer.class, returnType);
}
-
+
+ @Test
+ public void getClasses() {
+ Method method = ReflectUtil.getMethod(Parent.class, "getLevel");
+ Type returnType = TypeUtil.getReturnType(method);
+ Class clazz = TypeUtil.getClass(returnType);
+ Assert.assertEquals(Level1.class, clazz);
+
+ method = ReflectUtil.getMethod(Level1.class, "getId");
+ returnType = TypeUtil.getReturnType(method);
+ clazz = TypeUtil.getClass(returnType);
+ Assert.assertEquals(Object.class, clazz);
+ }
+
public static class TestClass {
- public List getList(){
+ public List getList() {
return new ArrayList<>();
}
-
+
public Integer intTest(Integer integer) {
return 1;
}
+
}
@Test
- public void getTypeArgumentTest(){
+ public void getTypeArgumentTest() {
// 测试不继承父类,而是实现泛型接口时是否可以获取成功。
final Type typeArgument = TypeUtil.getTypeArgument(IPService.class);
Assert.assertEquals(String.class, typeArgument);
@@ -59,25 +72,29 @@ public class TypeUtilTest {
}
@Test
- public void getActualTypesTest(){
+ public void getActualTypesTest() {
// 测试多层级泛型参数是否能获取成功
- Type idType = TypeUtil.getActualType(Level3.class,
- ReflectUtil.getField(Level3.class, "id"));
+ Type idType = TypeUtil.getActualType(Level3.class, ReflectUtil.getField(Level3.class, "id"));
Assert.assertEquals(Long.class, idType);
}
- public static class Level3 extends Level2{
+ public static class Level3 extends Level2 {
}
- public static class Level2 extends Level1{
+ public static class Level2 extends Level1 {
}
@Data
- public static class Level1{
+ public static class Level1 {
private T id;
}
+ @Data
+ public static class Parent, B extends Long> {
+ private T level;
+ }
+
}
diff --git a/hutool-cron/pom.xml b/hutool-cron/pom.xml
index 6705a867d..a8c3a9969 100755
--- a/hutool-cron/pom.xml
+++ b/hutool-cron/pom.xml
@@ -9,7 +9,7 @@
cn.hutool
hutool-parent
- 5.8.22
+ 5.8.23-SNAPSHOT
hutool-cron
diff --git a/hutool-cron/src/main/java/cn/hutool/cron/pattern/Part.java b/hutool-cron/src/main/java/cn/hutool/cron/pattern/Part.java
index aa7ee5c3d..7178aab32 100644
--- a/hutool-cron/src/main/java/cn/hutool/cron/pattern/Part.java
+++ b/hutool-cron/src/main/java/cn/hutool/cron/pattern/Part.java
@@ -89,7 +89,7 @@ public enum Part {
*/
public int checkValue(int value) throws CronException {
Assert.checkBetween(value, min, max,
- () -> new CronException("Value {} out of range: [{} , {}]", value, min, max));
+ () -> new CronException("{} value {} out of range: [{} , {}]", this.name(), value, min, max));
return value;
}
diff --git a/hutool-cron/src/main/java/cn/hutool/cron/pattern/parser/PartParser.java b/hutool-cron/src/main/java/cn/hutool/cron/pattern/parser/PartParser.java
index 263b64135..7c33ed05c 100644
--- a/hutool-cron/src/main/java/cn/hutool/cron/pattern/parser/PartParser.java
+++ b/hutool-cron/src/main/java/cn/hutool/cron/pattern/parser/PartParser.java
@@ -3,16 +3,11 @@ package cn.hutool.cron.pattern.parser;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.date.Month;
import cn.hutool.core.date.Week;
-import cn.hutool.core.lang.Console;
import cn.hutool.core.util.NumberUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.cron.CronException;
import cn.hutool.cron.pattern.Part;
-import cn.hutool.cron.pattern.matcher.AlwaysTrueMatcher;
-import cn.hutool.cron.pattern.matcher.BoolArrayMatcher;
-import cn.hutool.cron.pattern.matcher.DayOfMonthMatcher;
-import cn.hutool.cron.pattern.matcher.PartMatcher;
-import cn.hutool.cron.pattern.matcher.YearValueMatcher;
+import cn.hutool.cron.pattern.matcher.*;
import java.util.ArrayList;
import java.util.List;
@@ -204,13 +199,11 @@ public class PartParser {
//在range模式下,如果步进不存在,表示步进为1
step = 1;
}
- if (v1 < v2) {// 正常范围,例如:2-5
+ if (v1 <= v2) {// 正常范围,例如:2-5,3-3
NumberUtil.appendRange(v1, v2, step, results);
- } else if (v1 > v2) {// 逆向范围,反选模式,例如:5-2
+ } else {// 逆向范围,反选模式,例如:5-2
NumberUtil.appendRange(v1, part.getMax(), step, results);
NumberUtil.appendRange(part.getMin(), v2, step, results);
- } else {// v1 == v2,此时与单值模式一致
- NumberUtil.appendRange(v1, part.getMax(), step, results);
}
} else {
throw new CronException("Invalid syntax of field: [{}]", value);
diff --git a/hutool-cron/src/test/java/cn/hutool/cron/pattern/IssueI82CSHTest.java b/hutool-cron/src/test/java/cn/hutool/cron/pattern/IssueI82CSHTest.java
new file mode 100644
index 000000000..4dfc28c0d
--- /dev/null
+++ b/hutool-cron/src/test/java/cn/hutool/cron/pattern/IssueI82CSHTest.java
@@ -0,0 +1,21 @@
+package cn.hutool.cron.pattern;
+
+import cn.hutool.core.date.DateTime;
+import cn.hutool.core.date.DateUtil;
+import org.junit.Assert;
+import org.junit.Test;
+
+import java.util.Date;
+import java.util.List;
+
+public class IssueI82CSHTest {
+
+ @Test
+ public void test() {
+ final DateTime begin = DateUtil.parse("2023-09-20");
+ final DateTime end = DateUtil.parse("2025-09-20");
+ final List dates = CronPatternUtil.matchedDates("0 0 1 3-3,9 *", begin, end, 20, false);
+ //dates.forEach(Console::log);
+ Assert.assertEquals(4, dates.size());
+ }
+}
diff --git a/hutool-crypto/pom.xml b/hutool-crypto/pom.xml
index 57e8c72c1..0ccc3100e 100755
--- a/hutool-crypto/pom.xml
+++ b/hutool-crypto/pom.xml
@@ -9,7 +9,7 @@
cn.hutool
hutool-parent
- 5.8.22
+ 5.8.23-SNAPSHOT
hutool-crypto
diff --git a/hutool-crypto/src/test/java/cn/hutool/crypto/BCUtilTest.java b/hutool-crypto/src/test/java/cn/hutool/crypto/BCUtilTest.java
index 68971d361..daf27255f 100644
--- a/hutool-crypto/src/test/java/cn/hutool/crypto/BCUtilTest.java
+++ b/hutool-crypto/src/test/java/cn/hutool/crypto/BCUtilTest.java
@@ -1,6 +1,5 @@
package cn.hutool.crypto;
-import cn.hutool.crypto.BCUtil;
import org.bouncycastle.crypto.params.ECPrivateKeyParameters;
import org.bouncycastle.crypto.params.ECPublicKeyParameters;
import org.junit.Assert;
diff --git a/hutool-crypto/src/test/java/cn/hutool/crypto/KeyUtilTest.java b/hutool-crypto/src/test/java/cn/hutool/crypto/KeyUtilTest.java
index 2ef57eea2..b981799f6 100644
--- a/hutool-crypto/src/test/java/cn/hutool/crypto/KeyUtilTest.java
+++ b/hutool-crypto/src/test/java/cn/hutool/crypto/KeyUtilTest.java
@@ -1,8 +1,5 @@
package cn.hutool.crypto;
-import cn.hutool.crypto.CryptoException;
-import cn.hutool.crypto.GlobalBouncyCastleProvider;
-import cn.hutool.crypto.KeyUtil;
import org.junit.Assert;
import org.junit.Ignore;
import org.junit.Test;
diff --git a/hutool-db/pom.xml b/hutool-db/pom.xml
index fe4727332..fcfc0170c 100755
--- a/hutool-db/pom.xml
+++ b/hutool-db/pom.xml
@@ -9,7 +9,7 @@
cn.hutool
hutool-parent
- 5.8.22
+ 5.8.23-SNAPSHOT
hutool-db
diff --git a/hutool-db/src/main/java/cn/hutool/db/meta/Column.java b/hutool-db/src/main/java/cn/hutool/db/meta/Column.java
index 6a13bec35..ed9cacc4d 100755
--- a/hutool-db/src/main/java/cn/hutool/db/meta/Column.java
+++ b/hutool-db/src/main/java/cn/hutool/db/meta/Column.java
@@ -258,7 +258,7 @@ public class Column implements Serializable, Cloneable {
*
* @return 大小或数据长度
*/
- public int getDigit() {
+ public Integer getDigit() {
return digit;
}
diff --git a/hutool-db/src/test/java/cn/hutool/db/MySQLTest.java b/hutool-db/src/test/java/cn/hutool/db/MySQLTest.java
index 8ecebb74e..c7aa2e4bf 100755
--- a/hutool-db/src/test/java/cn/hutool/db/MySQLTest.java
+++ b/hutool-db/src/test/java/cn/hutool/db/MySQLTest.java
@@ -1,7 +1,6 @@
package cn.hutool.db;
import cn.hutool.core.lang.Console;
-import cn.hutool.core.util.ArrayUtil;
import org.junit.Assert;
import org.junit.BeforeClass;
import org.junit.Ignore;
diff --git a/hutool-db/src/test/java/cn/hutool/db/dialect/DialectFactoryTest.java b/hutool-db/src/test/java/cn/hutool/db/dialect/DialectFactoryTest.java
index 519e671a4..8222dcdac 100644
--- a/hutool-db/src/test/java/cn/hutool/db/dialect/DialectFactoryTest.java
+++ b/hutool-db/src/test/java/cn/hutool/db/dialect/DialectFactoryTest.java
@@ -4,9 +4,7 @@ import cn.hutool.core.util.RandomUtil;
import org.junit.Assert;
import org.junit.Test;
-
import java.util.HashMap;
-
import java.util.Map;
import static cn.hutool.db.dialect.DriverNamePool.*;
diff --git a/hutool-dfa/pom.xml b/hutool-dfa/pom.xml
index 1a6a32716..2b5f43b67 100755
--- a/hutool-dfa/pom.xml
+++ b/hutool-dfa/pom.xml
@@ -9,7 +9,7 @@
cn.hutool
hutool-parent
- 5.8.22
+ 5.8.23-SNAPSHOT
hutool-dfa
diff --git a/hutool-extra/pom.xml b/hutool-extra/pom.xml
index 68e59f024..a4a0b662a 100755
--- a/hutool-extra/pom.xml
+++ b/hutool-extra/pom.xml
@@ -9,7 +9,7 @@
cn.hutool
hutool-parent
- 5.8.22
+ 5.8.23-SNAPSHOT
hutool-extra
@@ -464,7 +464,7 @@
org.apache.commons
commons-compress
- 1.22
+ 1.24.0
compile
true
diff --git a/hutool-http/pom.xml b/hutool-http/pom.xml
index 61a9f1b7d..03e8ff952 100755
--- a/hutool-http/pom.xml
+++ b/hutool-http/pom.xml
@@ -9,7 +9,7 @@
cn.hutool
hutool-parent
- 5.8.22
+ 5.8.23-SNAPSHOT
hutool-http
diff --git a/hutool-http/src/main/java/cn/hutool/http/HttpRequest.java b/hutool-http/src/main/java/cn/hutool/http/HttpRequest.java
index 9c9f8ed2b..657acd8e8 100755
--- a/hutool-http/src/main/java/cn/hutool/http/HttpRequest.java
+++ b/hutool-http/src/main/java/cn/hutool/http/HttpRequest.java
@@ -8,7 +8,6 @@ import cn.hutool.core.io.resource.FileResource;
import cn.hutool.core.io.resource.MultiFileResource;
import cn.hutool.core.io.resource.Resource;
import cn.hutool.core.lang.Assert;
-import cn.hutool.core.lang.Console;
import cn.hutool.core.map.MapUtil;
import cn.hutool.core.map.TableMap;
import cn.hutool.core.net.SSLUtil;
@@ -1405,7 +1404,7 @@ public class HttpRequest extends HttpBase {
/**
* 是否忽略读取响应body部分
- * HEAD、CONNECT、OPTIONS、TRACE方法将不读取响应体
+ * HEAD、CONNECT、TRACE方法将不读取响应体
*
* @return 是否需要忽略响应body部分
* @since 3.1.2
@@ -1413,7 +1412,6 @@ public class HttpRequest extends HttpBase {
private boolean isIgnoreResponseBody() {
return Method.HEAD == this.method //
|| Method.CONNECT == this.method //
- || Method.OPTIONS == this.method //
|| Method.TRACE == this.method;
}
diff --git a/hutool-http/src/main/java/cn/hutool/http/server/HttpExchangeWrapper.java b/hutool-http/src/main/java/cn/hutool/http/server/HttpExchangeWrapper.java
new file mode 100644
index 000000000..16f651fa0
--- /dev/null
+++ b/hutool-http/src/main/java/cn/hutool/http/server/HttpExchangeWrapper.java
@@ -0,0 +1,148 @@
+package cn.hutool.http.server;
+
+import com.sun.net.httpserver.Headers;
+import com.sun.net.httpserver.HttpContext;
+import com.sun.net.httpserver.HttpExchange;
+import com.sun.net.httpserver.HttpPrincipal;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.net.InetSocketAddress;
+import java.net.URI;
+
+/**
+ * {@link HttpExchange}包装类,提供增强方法和缓存
+ *
+ * @author looly
+ */
+public class HttpExchangeWrapper extends HttpExchange {
+
+ private final HttpExchange raw;
+ private final HttpServerRequest request;
+ private final HttpServerResponse response;
+
+ /**
+ * 构造
+ *
+ * @param raw {@link HttpExchange}
+ */
+ public HttpExchangeWrapper(final HttpExchange raw) {
+ this.raw = raw;
+ this.request = new HttpServerRequest(this);
+ this.response = new HttpServerResponse(this);
+ }
+
+ /**
+ * 获取原始对象
+ * @return 对象
+ */
+ public HttpExchange getRaw() {
+ return this.raw;
+ }
+
+ /**
+ * 获取请求
+ *
+ * @return 请求
+ */
+ public HttpServerRequest getRequest() {
+ return request;
+ }
+
+ /**
+ * 获取响应
+ *
+ * @return 响应
+ */
+ public HttpServerResponse getResponse() {
+ return response;
+ }
+
+ // region ----- HttpExchange methods
+ @Override
+ public Headers getRequestHeaders() {
+ return this.raw.getRequestHeaders();
+ }
+
+ @Override
+ public Headers getResponseHeaders() {
+ return this.raw.getResponseHeaders();
+ }
+
+ @Override
+ public URI getRequestURI() {
+ return this.raw.getRequestURI();
+ }
+
+ @Override
+ public String getRequestMethod() {
+ return this.raw.getRequestMethod();
+ }
+
+ @Override
+ public HttpContext getHttpContext() {
+ return this.raw.getHttpContext();
+ }
+
+ @Override
+ public void close() {
+ this.raw.close();
+ }
+
+ @Override
+ public InputStream getRequestBody() {
+ return this.raw.getRequestBody();
+ }
+
+ @Override
+ public OutputStream getResponseBody() {
+ return this.raw.getResponseBody();
+ }
+
+ @Override
+ public void sendResponseHeaders(final int rCode, final long responseLength) throws IOException {
+ this.raw.sendResponseHeaders(rCode, responseLength);
+ }
+
+ @Override
+ public InetSocketAddress getRemoteAddress() {
+ return this.raw.getRemoteAddress();
+ }
+
+ @Override
+ public int getResponseCode() {
+ return this.raw.getResponseCode();
+ }
+
+ @Override
+ public InetSocketAddress getLocalAddress() {
+ return this.raw.getLocalAddress();
+ }
+
+ @Override
+ public String getProtocol() {
+ return this.raw.getProtocol();
+ }
+
+ @Override
+ public Object getAttribute(final String name) {
+ return this.raw.getAttribute(name);
+ }
+
+ @Override
+ public void setAttribute(final String name, final Object value) {
+ this.raw.setAttribute(name, value);
+ }
+
+ @Override
+ public void setStreams(final InputStream i, final OutputStream o) {
+ this.raw.setStreams(i, o);
+ }
+
+ @Override
+ public HttpPrincipal getPrincipal() {
+ return this.raw.getPrincipal();
+ }
+ // endregion
+}
diff --git a/hutool-http/src/main/java/cn/hutool/http/server/SimpleServer.java b/hutool-http/src/main/java/cn/hutool/http/server/SimpleServer.java
index 05dc1ed62..e62e5e49e 100644
--- a/hutool-http/src/main/java/cn/hutool/http/server/SimpleServer.java
+++ b/hutool-http/src/main/java/cn/hutool/http/server/SimpleServer.java
@@ -126,7 +126,8 @@ public class SimpleServer {
return addFilter(new SimpleFilter() {
@Override
public void doFilter(HttpExchange httpExchange, Chain chain) throws IOException {
- filter.doFilter(new HttpServerRequest(httpExchange), new HttpServerResponse(httpExchange), chain);
+ final HttpExchangeWrapper httpExchangeWrapper = new HttpExchangeWrapper(httpExchange);
+ filter.doFilter(httpExchangeWrapper.getRequest(), httpExchangeWrapper.getResponse(), chain);
}
});
}
diff --git a/hutool-http/src/main/java/cn/hutool/http/server/handler/ActionHandler.java b/hutool-http/src/main/java/cn/hutool/http/server/handler/ActionHandler.java
index 42f88c548..1e1077162 100644
--- a/hutool-http/src/main/java/cn/hutool/http/server/handler/ActionHandler.java
+++ b/hutool-http/src/main/java/cn/hutool/http/server/handler/ActionHandler.java
@@ -1,5 +1,6 @@
package cn.hutool.http.server.handler;
+import cn.hutool.http.server.HttpExchangeWrapper;
import cn.hutool.http.server.HttpServerRequest;
import cn.hutool.http.server.HttpServerResponse;
import cn.hutool.http.server.action.Action;
@@ -29,10 +30,18 @@ public class ActionHandler implements HttpHandler {
@Override
public void handle(HttpExchange httpExchange) throws IOException {
- action.doAction(
- new HttpServerRequest(httpExchange),
- new HttpServerResponse(httpExchange)
- );
+ final HttpServerRequest request;
+ final HttpServerResponse response;
+ if (httpExchange instanceof HttpExchangeWrapper) {
+ // issue#3343 当使用Filter时,可能读取了请求参数,此时使用共享的req和res,可复用缓存
+ final HttpExchangeWrapper wrapper = (HttpExchangeWrapper) httpExchange;
+ request = wrapper.getRequest();
+ response = wrapper.getResponse();
+ } else {
+ request = new HttpServerRequest(httpExchange);
+ response = new HttpServerResponse(httpExchange);
+ }
+ action.doAction(request, response);
httpExchange.close();
}
}
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 8c1a060a7..0b0abf956 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
@@ -28,54 +28,56 @@ public class Browser extends UserAgentInfo {
* 支持的浏览器类型
*/
public static final List browers = CollUtil.newArrayList(
- // 部分特殊浏览器是基于安卓、Iphone等的,需要优先判断
- // 企业微信 企业微信使用微信浏览器内核,会包含 MicroMessenger 所以要放在前面
- new Browser("wxwork", "wxwork", "wxwork\\/([\\d\\w\\.\\-]+)"),
- // 微信
- new Browser("MicroMessenger", "MicroMessenger", Other_Version),
- // 微信小程序
- new Browser("miniProgram", "miniProgram", Other_Version),
- // QQ浏览器
- new Browser("QQBrowser", "MQQBrowser", "MQQBrowser\\/([\\d\\w\\.\\-]+)"),
- // 钉钉PC端浏览器
- new Browser("DingTalk-win", "dingtalk-win", "DingTalk\\(([\\d\\w\\.\\-]+)\\)"),
- // 钉钉内置浏览器
- new Browser("DingTalk", "DingTalk", "AliApp\\(DingTalk\\/([\\d\\w\\.\\-]+)\\)"),
- // 支付宝内置浏览器
- new Browser("Alipay", "AlipayClient", "AliApp\\(AP\\/([\\d\\w\\.\\-]+)\\)"),
- // 淘宝内置浏览器
- new Browser("Taobao", "taobao", "AliApp\\(TB\\/([\\d\\w\\.\\-]+)\\)"),
- // UC浏览器
- new Browser("UCBrowser", "UC?Browser", "UC?Browser\\/([\\d\\w\\.\\-]+)"),
- // XiaoMi 浏览器
- new Browser("MiuiBrowser", "MiuiBrowser|mibrowser", "MiuiBrowser\\/([\\d\\w\\.\\-]+)"),
- // 夸克浏览器
- new Browser("Quark", "Quark", Other_Version),
- // 联想浏览器
- new Browser("Lenovo", "SLBrowser", "SLBrowser/([\\d\\w\\.\\-]+)"),
- new Browser("MSEdge", "Edge|Edg", "(?:edge|Edg|EdgA)\\/([\\d\\w\\.\\-]+)"),
- new Browser("Chrome", "chrome|(iphone.*crios.*safari)", "(?:Chrome|CriOS)\\/([\\d\\w\\.\\-]+)"),
- new Browser("Firefox", "firefox", Other_Version),
- new Browser("IEMobile", "iemobile", Other_Version),
- new Browser("Android Browser", "android", "version\\/([\\d\\w\\.\\-]+)"),
- new Browser("Safari", "safari", "version\\/([\\d\\w\\.\\-]+)"),
- new Browser("Opera", "opera", Other_Version),
- new Browser("Konqueror", "konqueror", Other_Version),
- new Browser("PS3", "playstation 3", "([\\d\\w\\.\\-]+)\\)\\s*$"),
- new Browser("PSP", "playstation portable", "([\\d\\w\\.\\-]+)\\)?\\s*$"),
- new Browser("Lotus", "lotus.notes", "Lotus-Notes\\/([\\w.]+)"),
- new Browser("Thunderbird", "thunderbird", Other_Version),
- new Browser("Netscape", "netscape", Other_Version),
- new Browser("Seamonkey", "seamonkey", Other_Version),
- new Browser("Outlook", "microsoft.outlook", Other_Version),
- new Browser("Evolution", "evolution", Other_Version),
- new Browser("MSIE", "msie", "msie ([\\d\\w\\.\\-]+)"),
- new Browser("MSIE11", "rv:11", "rv:([\\d\\w\\.\\-]+)"),
- new Browser("Gabble", "Gabble", Other_Version),
- new Browser("Yammer Desktop", "AdobeAir", "([\\d\\w\\.\\-]+)\\/Yammer"),
- new Browser("Yammer Mobile", "Yammer[\\s]+([\\d\\w\\.\\-]+)", "Yammer[\\s]+([\\d\\w\\.\\-]+)"),
- new Browser("Apache HTTP Client", "Apache\\\\-HttpClient", "Apache\\-HttpClient\\/([\\d\\w\\.\\-]+)"),
- new Browser("BlackBerry", "BlackBerry", "BlackBerry[\\d]+\\/([\\d\\w\\.\\-]+)")
+ // 部分特殊浏览器是基于安卓、Iphone等的,需要优先判断
+ // 企业微信 企业微信使用微信浏览器内核,会包含 MicroMessenger 所以要放在前面
+ new Browser("wxwork", "wxwork", "wxwork\\/([\\d\\w\\.\\-]+)"),
+ // 微信
+ new Browser("MicroMessenger", "MicroMessenger", Other_Version),
+ // 微信小程序
+ new Browser("miniProgram", "miniProgram", Other_Version),
+ // QQ浏览器
+ new Browser("QQBrowser", "MQQBrowser", "MQQBrowser\\/([\\d\\w\\.\\-]+)"),
+ // 钉钉PC端浏览器
+ new Browser("DingTalk-win", "dingtalk-win", "DingTalk\\(([\\d\\w\\.\\-]+)\\)"),
+ // 钉钉内置浏览器
+ new Browser("DingTalk", "DingTalk", "AliApp\\(DingTalk\\/([\\d\\w\\.\\-]+)\\)"),
+ // 支付宝内置浏览器
+ new Browser("Alipay", "AlipayClient", "AliApp\\(AP\\/([\\d\\w\\.\\-]+)\\)"),
+ // 淘宝内置浏览器
+ new Browser("Taobao", "taobao", "AliApp\\(TB\\/([\\d\\w\\.\\-]+)\\)"),
+ // UC浏览器
+ new Browser("UCBrowser", "UC?Browser", "UC?Browser\\/([\\d\\w\\.\\-]+)"),
+ // XiaoMi 浏览器
+ new Browser("MiuiBrowser", "MiuiBrowser|mibrowser", "MiuiBrowser\\/([\\d\\w\\.\\-]+)"),
+ // 夸克浏览器
+ new Browser("Quark", "Quark", Other_Version),
+ // 联想浏览器
+ new Browser("Lenovo", "SLBrowser", "SLBrowser/([\\d\\w\\.\\-]+)"),
+ new Browser("MSEdge", "Edge|Edg", "(?:edge|Edg|EdgA)\\/([\\d\\w\\.\\-]+)"),
+ new Browser("Chrome", "chrome|(iphone.*crios.*safari)", "(?:Chrome|CriOS)\\/([\\d\\w\\.\\-]+)"),
+ new Browser("Firefox", "firefox", Other_Version),
+ new Browser("IEMobile", "iemobile", Other_Version),
+ new Browser("Android Browser", "android", "version\\/([\\d\\w\\.\\-]+)"),
+ new Browser("Safari", "safari", "version\\/([\\d\\w\\.\\-]+)"),
+ new Browser("Opera", "opera", Other_Version),
+ new Browser("Konqueror", "konqueror", Other_Version),
+ new Browser("PS3", "playstation 3", "([\\d\\w\\.\\-]+)\\)\\s*$"),
+ new Browser("PSP", "playstation portable", "([\\d\\w\\.\\-]+)\\)?\\s*$"),
+ new Browser("Lotus", "lotus.notes", "Lotus-Notes\\/([\\w.]+)"),
+ new Browser("Thunderbird", "thunderbird", Other_Version),
+ new Browser("Netscape", "netscape", Other_Version),
+ new Browser("Seamonkey", "seamonkey", Other_Version),
+ new Browser("Outlook", "microsoft.outlook", Other_Version),
+ new Browser("Evolution", "evolution", Other_Version),
+ new Browser("MSIE", "msie", "msie ([\\d\\w\\.\\-]+)"),
+ new Browser("MSIE11", "rv:11", "rv:([\\d\\w\\.\\-]+)"),
+ new Browser("Gabble", "Gabble", Other_Version),
+ new Browser("Yammer Desktop", "AdobeAir", "([\\d\\w\\.\\-]+)\\/Yammer"),
+ new Browser("Yammer Mobile", "Yammer[\\s]+([\\d\\w\\.\\-]+)", "Yammer[\\s]+([\\d\\w\\.\\-]+)"),
+ new Browser("Apache HTTP Client", "Apache\\\\-HttpClient", "Apache\\-HttpClient\\/([\\d\\w\\.\\-]+)"),
+ new Browser("BlackBerry", "BlackBerry", "BlackBerry[\\d]+\\/([\\d\\w\\.\\-]+)"),
+ // issue#I847JY 百度浏览器
+ new Browser("Baidu", "Baidu", "baiduboxapp\\/([\\d\\w\\.\\-]+)")
);
/**
@@ -116,7 +118,7 @@ public class Browser extends UserAgentInfo {
* @return 版本
*/
public String getVersion(String userAgentString) {
- if(isUnknown()){
+ if (isUnknown()) {
return null;
}
return ReUtil.getGroup1(this.versionPattern, userAgentString);
@@ -130,11 +132,11 @@ public class Browser extends UserAgentInfo {
public boolean isMobile() {
final String name = this.getName();
return "PSP".equals(name) ||
- "Yammer Mobile".equals(name) ||
- "Android Browser".equals(name) ||
- "IEMobile".equals(name) ||
- "MicroMessenger".equals(name) ||
- "miniProgram".equals(name) ||
- "DingTalk".equals(name);
+ "Yammer Mobile".equals(name) ||
+ "Android Browser".equals(name) ||
+ "IEMobile".equals(name) ||
+ "MicroMessenger".equals(name) ||
+ "miniProgram".equals(name) ||
+ "DingTalk".equals(name);
}
}
diff --git a/hutool-http/src/test/java/cn/hutool/http/Issue3314Test.java b/hutool-http/src/test/java/cn/hutool/http/Issue3314Test.java
new file mode 100644
index 000000000..3107c9d42
--- /dev/null
+++ b/hutool-http/src/test/java/cn/hutool/http/Issue3314Test.java
@@ -0,0 +1,21 @@
+package cn.hutool.http;
+
+import cn.hutool.core.io.FileUtil;
+import cn.hutool.core.lang.Console;
+import org.junit.Ignore;
+import org.junit.Test;
+
+public class Issue3314Test {
+ @Test
+ @Ignore
+ public void postTest() {
+ String url = "https://hutool.cn/test/getList";
+ final String body = HttpRequest.get(url)
+ .setRest(true)
+ .body(FileUtil.readBytes("d:/test/3314.xlsx"))
+ .execute()
+ .body();
+
+ Console.log(body);
+ }
+}
diff --git a/hutool-http/src/test/java/cn/hutool/http/server/Issue3343Test.java b/hutool-http/src/test/java/cn/hutool/http/server/Issue3343Test.java
new file mode 100644
index 000000000..b5f43ba21
--- /dev/null
+++ b/hutool-http/src/test/java/cn/hutool/http/server/Issue3343Test.java
@@ -0,0 +1,33 @@
+package cn.hutool.http.server;
+
+import cn.hutool.core.date.DateUtil;
+import cn.hutool.core.lang.Console;
+import cn.hutool.core.map.multi.ListValueMap;
+import cn.hutool.http.HttpUtil;
+
+/**
+ * http://localhost:8888/?name=hutool
+ */
+public class Issue3343Test {
+ public static void main(final String[] args) {
+ final SimpleServer server = HttpUtil.createServer(8888)
+ .addFilter((req, res, chain) -> {
+ Console.log(DateUtil.now() + " got request: " + req.getPath());
+ Console.log(" > from : " + req.getClientIP());
+ // 过滤器中获取请求参数
+ Console.log(" > params : " + req.getParams());
+ chain.doFilter(req.getHttpExchange());
+ });
+
+ server.addAction("/", Issue3343Test::index);
+
+ server.start();
+ }
+
+ private static void index(HttpServerRequest request, HttpServerResponse response) {
+ // 具体逻辑中再次获取请求参数
+ ListValueMap params = request.getParams();
+ Console.log("index params: " + params);
+ response.getWriter().write("GOT: " + params);
+ }
+}
diff --git a/hutool-http/src/test/java/cn/hutool/http/useragent/UserAgentUtilTest.java b/hutool-http/src/test/java/cn/hutool/http/useragent/UserAgentUtilTest.java
index 026313781..b17481d99 100644
--- a/hutool-http/src/test/java/cn/hutool/http/useragent/UserAgentUtilTest.java
+++ b/hutool-http/src/test/java/cn/hutool/http/useragent/UserAgentUtilTest.java
@@ -456,4 +456,20 @@ public class UserAgentUtilTest {
Assert.assertEquals("iPhone", ua2.getPlatform().toString());
Assert.assertTrue(ua2.isMobile());
}
+
+ @Test
+ public void issueI847JYTest() {
+ final String s = "Mozilla/5.0 (iPhone; CPU iPhone OS 17_0 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) " +
+ "Mobile/15E148 SP-engine/2.80.0 main%2F1.0 baiduboxapp/13.42.0.11 (Baidu; P2 17.0) NABar/1.0 themeUA=Them";
+ final UserAgent ua2 = UserAgentUtil.parse(s);
+
+ Assert.assertEquals("Baidu", ua2.getBrowser().toString());
+ Assert.assertEquals("13.42.0.11", ua2.getVersion());
+ Assert.assertEquals("Webkit", ua2.getEngine().toString());
+ Assert.assertEquals("605.1.15", ua2.getEngineVersion());
+ Assert.assertEquals("iPhone", ua2.getOs().toString());
+ Assert.assertEquals("17_0", ua2.getOsVersion());
+ Assert.assertEquals("iPhone", ua2.getPlatform().toString());
+ Assert.assertTrue(ua2.isMobile());
+ }
}
diff --git a/hutool-json/pom.xml b/hutool-json/pom.xml
index 159a1cf38..132992472 100755
--- a/hutool-json/pom.xml
+++ b/hutool-json/pom.xml
@@ -9,7 +9,7 @@
cn.hutool
hutool-parent
- 5.8.22
+ 5.8.23-SNAPSHOT
hutool-json
diff --git a/hutool-json/src/main/java/cn/hutool/json/JSONParser.java b/hutool-json/src/main/java/cn/hutool/json/JSONParser.java
index 3b1becd3a..7492c0acf 100755
--- a/hutool-json/src/main/java/cn/hutool/json/JSONParser.java
+++ b/hutool-json/src/main/java/cn/hutool/json/JSONParser.java
@@ -1,6 +1,5 @@
package cn.hutool.json;
-import cn.hutool.core.lang.Console;
import cn.hutool.core.lang.Filter;
import cn.hutool.core.lang.mutable.Mutable;
import cn.hutool.core.lang.mutable.MutablePair;
diff --git a/hutool-json/src/main/java/cn/hutool/json/JSONStrFormatter.java b/hutool-json/src/main/java/cn/hutool/json/JSONStrFormatter.java
index 5181401be..8b033c17d 100644
--- a/hutool-json/src/main/java/cn/hutool/json/JSONStrFormatter.java
+++ b/hutool-json/src/main/java/cn/hutool/json/JSONStrFormatter.java
@@ -42,10 +42,12 @@ public class JSONStrFormatter {
if (null == wrapChar) {
//字符串模式开始
wrapChar = key;
- } else if (isEscapeMode) {
- //在字符串模式下的转义
- isEscapeMode = false;
} else if (wrapChar.equals(key)) {
+ if (isEscapeMode) {
+ //字符串模式下,遇到结束符号,也同时结束转义
+ isEscapeMode = false;
+ }
+
//字符串包装结束
wrapChar = null;
}
diff --git a/hutool-json/src/main/java/cn/hutool/json/serialize/TemporalAccessorSerializer.java b/hutool-json/src/main/java/cn/hutool/json/serialize/TemporalAccessorSerializer.java
index 7ca54d49b..0577551d3 100755
--- a/hutool-json/src/main/java/cn/hutool/json/serialize/TemporalAccessorSerializer.java
+++ b/hutool-json/src/main/java/cn/hutool/json/serialize/TemporalAccessorSerializer.java
@@ -1,5 +1,6 @@
package cn.hutool.json.serialize;
+import cn.hutool.core.lang.Assert;
import cn.hutool.json.JSON;
import cn.hutool.json.JSONException;
import cn.hutool.json.JSONObject;
@@ -7,6 +8,7 @@ import cn.hutool.json.JSONObject;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
+import java.time.Month;
import java.time.temporal.TemporalAccessor;
/**
@@ -61,11 +63,33 @@ public class TemporalAccessorSerializer implements JSONObjectSerializer bean1 =
+ JSONUtil.toBean(json, new TypeReference