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 [![Stargazers over time](https://starchart.cc/dromara/hutool.svg)](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 [![Stargazers over time](https://starchart.cc/dromara/hutool.svg)](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 + + + + + + +
+
+ + hutool + +
+
+ + +
+
+
+ +
+ + +
+
+
+ + + 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 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 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>() { + }, false); + + bean1.forEach((k, v) -> Assert.assertNotNull(v.getTestimonials())); + } + + // 对象 + @Data + public static class MedicalCenter { + + private Map medicalCenterLocalized; + + @Data + public static class MedicalCenterLocalized { + + private List testimonials; + + @Data + public static class Testimonial { + private LocalDateTime createTime; + } + } + } +} diff --git a/hutool-json/src/test/java/cn/hutool/json/IssueI84V6ITest.java b/hutool-json/src/test/java/cn/hutool/json/IssueI84V6ITest.java new file mode 100644 index 000000000..963d10539 --- /dev/null +++ b/hutool-json/src/test/java/cn/hutool/json/IssueI84V6ITest.java @@ -0,0 +1,18 @@ +package cn.hutool.json; + +import org.junit.Assert; +import org.junit.Test; + +public class IssueI84V6ITest { + @Test + public void formatTest() { + final String a1 = "{'x':'\\n','y':','}"; + final String formatJsonStr = JSONUtil.formatJsonStr(a1); +// Console.log(formatJsonStr); + Assert.assertEquals( + "{\n" + + " 'x': '\\n',\n" + + " 'y': ','\n" + + "}", formatJsonStr); + } +} diff --git a/hutool-json/src/test/resources/issueI82AM8.json b/hutool-json/src/test/resources/issueI82AM8.json new file mode 100644 index 000000000..a12f9bde9 --- /dev/null +++ b/hutool-json/src/test/resources/issueI82AM8.json @@ -0,0 +1,46 @@ +{ + "en": { + "testimonials": [ + { + "createTime": { + "dayOfYear": 261, + "dayOfWeek": "MONDAY", + "year": 2023, + "month": "SEPTEMBER", + "nano": 0, + "monthValue": 9, + "dayOfMonth": 18, + "hour": 15, + "minute": 18, + "second": 0, + "chronology": { + "id": "ISO", + "calendarType": "iso8601" + } + } + } + ] + }, + "zh": { + "testimonials": [ + { + "createTime": { + "dayOfYear": 261, + "dayOfWeek": "MONDAY", + "year": 2023, + "month": "SEPTEMBER", + "nano": 0, + "monthValue": 9, + "dayOfMonth": 18, + "hour": 15, + "minute": 18, + "second": 0, + "chronology": { + "id": "ISO", + "calendarType": "iso8601" + } + } + } + ] + } +} diff --git a/hutool-jwt/pom.xml b/hutool-jwt/pom.xml index 873ca6bee..8c2afdea7 100755 --- a/hutool-jwt/pom.xml +++ b/hutool-jwt/pom.xml @@ -9,7 +9,7 @@ cn.hutool hutool-parent - 5.8.22 + 5.8.23-SNAPSHOT hutool-jwt diff --git a/hutool-log/pom.xml b/hutool-log/pom.xml index 4ee6b53c6..a56bc68db 100755 --- a/hutool-log/pom.xml +++ b/hutool-log/pom.xml @@ -9,7 +9,7 @@ cn.hutool hutool-parent - 5.8.22 + 5.8.23-SNAPSHOT hutool-log diff --git a/hutool-poi/pom.xml b/hutool-poi/pom.xml index 4a12fdc22..608fc724a 100755 --- a/hutool-poi/pom.xml +++ b/hutool-poi/pom.xml @@ -9,7 +9,7 @@ cn.hutool hutool-parent - 5.8.22 + 5.8.23-SNAPSHOT hutool-poi diff --git a/hutool-poi/src/main/java/cn/hutool/poi/excel/RowUtil.java b/hutool-poi/src/main/java/cn/hutool/poi/excel/RowUtil.java index a63bcd29d..d040d1ba6 100644 --- a/hutool-poi/src/main/java/cn/hutool/poi/excel/RowUtil.java +++ b/hutool-poi/src/main/java/cn/hutool/poi/excel/RowUtil.java @@ -12,7 +12,6 @@ import org.apache.poi.ss.util.CellRangeUtil; import java.util.ArrayList; import java.util.List; -import java.util.Optional; import java.util.stream.Collectors; import java.util.stream.IntStream; diff --git a/hutool-poi/src/main/java/cn/hutool/poi/excel/sax/Excel03SaxReader.java b/hutool-poi/src/main/java/cn/hutool/poi/excel/sax/Excel03SaxReader.java index 84fc2a51f..a24882c9d 100644 --- a/hutool-poi/src/main/java/cn/hutool/poi/excel/sax/Excel03SaxReader.java +++ b/hutool-poi/src/main/java/cn/hutool/poi/excel/sax/Excel03SaxReader.java @@ -1,12 +1,9 @@ package cn.hutool.poi.excel.sax; -import cn.hutool.core.collection.CollUtil; import cn.hutool.core.io.IoUtil; import cn.hutool.core.lang.Assert; -import cn.hutool.core.util.ArrayUtil; import cn.hutool.core.util.ObjectUtil; import cn.hutool.core.util.StrUtil; -import cn.hutool.log.StaticLog; import cn.hutool.poi.excel.sax.handler.RowHandler; import cn.hutool.poi.exceptions.POIException; import org.apache.poi.hssf.eventusermodel.EventWorkbookBuilder.SheetRecordCollectingListener; diff --git a/hutool-script/pom.xml b/hutool-script/pom.xml index 97ecf49c7..00c9ef9d7 100755 --- a/hutool-script/pom.xml +++ b/hutool-script/pom.xml @@ -9,7 +9,7 @@ cn.hutool hutool-parent - 5.8.22 + 5.8.23-SNAPSHOT hutool-script diff --git a/hutool-setting/pom.xml b/hutool-setting/pom.xml index 217400e93..3cc439e04 100755 --- a/hutool-setting/pom.xml +++ b/hutool-setting/pom.xml @@ -9,7 +9,7 @@ cn.hutool hutool-parent - 5.8.22 + 5.8.23-SNAPSHOT hutool-setting diff --git a/hutool-socket/pom.xml b/hutool-socket/pom.xml index 35ea9ddf0..5fbb39378 100755 --- a/hutool-socket/pom.xml +++ b/hutool-socket/pom.xml @@ -9,7 +9,7 @@ cn.hutool hutool-parent - 5.8.22 + 5.8.23-SNAPSHOT hutool-socket diff --git a/hutool-socket/src/main/java/cn/hutool/socket/nio/NioServer.java b/hutool-socket/src/main/java/cn/hutool/socket/nio/NioServer.java index d3eaee9c7..0e56c368e 100644 --- a/hutool-socket/src/main/java/cn/hutool/socket/nio/NioServer.java +++ b/hutool-socket/src/main/java/cn/hutool/socket/nio/NioServer.java @@ -3,7 +3,6 @@ package cn.hutool.socket.nio; import cn.hutool.core.io.IORuntimeException; import cn.hutool.core.io.IoUtil; import cn.hutool.log.Log; -import cn.hutool.log.StaticLog; import java.io.Closeable; import java.io.IOException; @@ -16,7 +15,7 @@ import java.util.Iterator; /** * 基于NIO的Socket服务端实现 - * + * * @author looly * */ @@ -31,7 +30,7 @@ public class NioServer implements Closeable { /** * 构造 - * + * * @param port 端口 */ public NioServer(int port) { @@ -40,7 +39,7 @@ public class NioServer implements Closeable { /** * 初始化 - * + * * @param address 地址和端口 * @return this */ @@ -109,7 +108,7 @@ public class NioServer implements Closeable { /** * 开始监听 - * + * * @throws IOException IO异常 */ private void doListen() throws IOException { @@ -125,7 +124,7 @@ public class NioServer implements Closeable { /** * 处理SelectionKey - * + * * @param key SelectionKey */ private void handle(SelectionKey key) { diff --git a/hutool-system/pom.xml b/hutool-system/pom.xml index f00d79484..966fc6954 100755 --- a/hutool-system/pom.xml +++ b/hutool-system/pom.xml @@ -9,7 +9,7 @@ cn.hutool hutool-parent - 5.8.22 + 5.8.23-SNAPSHOT hutool-system diff --git a/pom.xml b/pom.xml index 5dd131e3a..5407a588e 100755 --- a/pom.xml +++ b/pom.xml @@ -8,7 +8,7 @@ cn.hutool hutool-parent - 5.8.22 + 5.8.23-SNAPSHOT hutool Hutool是一个小而全的Java工具类库,通过静态方法封装,降低相关API的学习成本,提高工作效率,使Java拥有函数式语言般的优雅,让Java语言也可以“甜甜的”。 https://github.com/dromara/hutool @@ -46,7 +46,7 @@ 8 5.9.2 - 1.18.26 + 1.18.28