mirror of
https://gitee.com/chinabugotech/hutool.git
synced 2025-05-09 23:51:34 +08:00
Prepare release
This commit is contained in:
commit
ced8bf817a
20
CHANGELOG.md
20
CHANGELOG.md
@ -1,6 +1,26 @@
|
|||||||
|
|
||||||
# 🚀Changelog
|
# 🚀Changelog
|
||||||
|
|
||||||
|
-------------------------------------------------------------------------------------------------------------
|
||||||
|
# 5.8.35(2024-12-25)
|
||||||
|
|
||||||
|
### 🐣新特性
|
||||||
|
* 【poi 】 优化ExcelWriter中使用比较器writer的方法,只对第一条数据进行排序(pr#3807@Github)
|
||||||
|
* 【extra 】 优化Ftp.download,返回false抛出异常(issue#3805@Github)
|
||||||
|
* 【core 】 优化MAC地址正则(issue#IB95X4@Gitee)
|
||||||
|
* 【json 】 JSON的getByPath方法新增更为通用的指定出参类型重载(pr#3814@Github)
|
||||||
|
* 【core 】 DateUtil.parseUTC方法标记废弃,改名为parseISO8601(issue#IBB6I5@Gitee)
|
||||||
|
* 【core 】 添加EnumUtil#getBy(Class, Func1, Object)方法(pr#1283@Gitee)
|
||||||
|
* 【db 】 添加Entity.addCondition方法(issue#IBCDL2@Gitee)
|
||||||
|
* 【poi 】 添加StopReadException,定义sax读取时用户可手动终止(issue#3820@Github)
|
||||||
|
|
||||||
|
### 🐞Bug修复
|
||||||
|
* 【crypto 】 修复JWTSignerUtil.createSigner中algorithmId未转换问题(issue#3806@Github)
|
||||||
|
* 【core 】 修复DateUtil.rangeContains未重置问题(issue#IB8OFS@Gitee)
|
||||||
|
* 【cache 】 修复StampedCache类get方法并发问题(issue#IBCIQG@Gitee)
|
||||||
|
* 【cache 】 修复FIFOCache类使用StampedCache导致并发读的并发问题(issue#IBCIQG@Gitee)
|
||||||
|
* 【cache 】 废弃StampedCache,可能造成Map循环调用导致死锁(issue#IBDGBZ@Gitee)
|
||||||
|
|
||||||
-------------------------------------------------------------------------------------------------------------
|
-------------------------------------------------------------------------------------------------------------
|
||||||
# 5.8.34(2024-11-25)
|
# 5.8.34(2024-11-25)
|
||||||
|
|
||||||
|
10
README-EN.md
10
README-EN.md
@ -36,6 +36,9 @@
|
|||||||
<a target="_blank" href='https://github.com/dromara/hutool'>
|
<a target="_blank" href='https://github.com/dromara/hutool'>
|
||||||
<img src="https://img.shields.io/github/stars/dromara/hutool.svg?style=social" alt="github star"/>
|
<img src="https://img.shields.io/github/stars/dromara/hutool.svg?style=social" alt="github star"/>
|
||||||
</a>
|
</a>
|
||||||
|
<a target="_blank" href='https://gitcode.com/dromara/hutool'>
|
||||||
|
<img src="https://gitcode.com/dromara/hutool/star/badge.svg" alt="gitcode star"/>
|
||||||
|
</a>
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<br/>
|
<br/>
|
||||||
@ -150,18 +153,18 @@ We provide the T-Shirt and Sweater with Hutool Logo, please visit the shop:
|
|||||||
<dependency>
|
<dependency>
|
||||||
<groupId>cn.hutool</groupId>
|
<groupId>cn.hutool</groupId>
|
||||||
<artifactId>hutool-all</artifactId>
|
<artifactId>hutool-all</artifactId>
|
||||||
<version>5.8.34</version>
|
<version>5.8.35</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
```
|
```
|
||||||
|
|
||||||
### 🍐Gradle
|
### 🍐Gradle
|
||||||
```
|
```
|
||||||
implementation 'cn.hutool:hutool-all:5.8.34'
|
implementation 'cn.hutool:hutool-all:5.8.35'
|
||||||
```
|
```
|
||||||
|
|
||||||
## 📥Download
|
## 📥Download
|
||||||
|
|
||||||
- [Maven Repo](https://repo1.maven.org/maven2/cn/hutool/hutool-all/5.8.34/)
|
- [Maven Repo](https://repo1.maven.org/maven2/cn/hutool/hutool-all/5.8.35/)
|
||||||
|
|
||||||
> 🔔️note:
|
> 🔔️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.
|
> Hutool 5.x supports JDK8+ and is not tested on Android platforms, and cannot guarantee that all tool classes or tool methods are available.
|
||||||
@ -199,6 +202,7 @@ When submitting feedback, please indicate which JDK version, Hutool version, and
|
|||||||
|
|
||||||
- [Gitee issue](https://gitee.com/dromara/hutool/issues)
|
- [Gitee issue](https://gitee.com/dromara/hutool/issues)
|
||||||
- [Github issue](https://github.com/dromara/hutool/issues)
|
- [Github issue](https://github.com/dromara/hutool/issues)
|
||||||
|
- [Gitcode issue](https://gitcode.com/dromara/hutool/issues)
|
||||||
|
|
||||||
### 🧬Principles of PR(pull request)
|
### 🧬Principles of PR(pull request)
|
||||||
|
|
||||||
|
14
README.md
14
README.md
@ -36,6 +36,9 @@
|
|||||||
<a target="_blank" href='https://github.com/dromara/hutool'>
|
<a target="_blank" href='https://github.com/dromara/hutool'>
|
||||||
<img src="https://img.shields.io/github/stars/dromara/hutool.svg?style=social" alt="github star"/>
|
<img src="https://img.shields.io/github/stars/dromara/hutool.svg?style=social" alt="github star"/>
|
||||||
</a>
|
</a>
|
||||||
|
<a target="_blank" href='https://gitcode.com/dromara/hutool'>
|
||||||
|
<img src="https://gitcode.com/dromara/hutool/star/badge.svg" alt="gitcode star"/>
|
||||||
|
</a>
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<br/>
|
<br/>
|
||||||
@ -143,20 +146,20 @@ Hutool = Hu + tool,是原公司项目底层代码剥离后的开源库,“Hu
|
|||||||
<dependency>
|
<dependency>
|
||||||
<groupId>cn.hutool</groupId>
|
<groupId>cn.hutool</groupId>
|
||||||
<artifactId>hutool-all</artifactId>
|
<artifactId>hutool-all</artifactId>
|
||||||
<version>5.8.34</version>
|
<version>5.8.35</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
```
|
```
|
||||||
|
|
||||||
### 🍐Gradle
|
### 🍐Gradle
|
||||||
```
|
```
|
||||||
implementation 'cn.hutool:hutool-all:5.8.34'
|
implementation 'cn.hutool:hutool-all:5.8.35'
|
||||||
```
|
```
|
||||||
|
|
||||||
### 📥下载jar
|
### 📥下载jar
|
||||||
|
|
||||||
点击以下链接,下载`hutool-all-X.X.X.jar`即可:
|
点击以下链接,下载`hutool-all-X.X.X.jar`即可:
|
||||||
|
|
||||||
- [Maven中央库](https://repo1.maven.org/maven2/cn/hutool/hutool-all/5.8.34/)
|
- [Maven中央库](https://repo1.maven.org/maven2/cn/hutool/hutool-all/5.8.35/)
|
||||||
|
|
||||||
> 🔔️注意
|
> 🔔️注意
|
||||||
> Hutool 5.x支持JDK8+,对Android平台没有测试,不能保证所有工具类或工具方法可用。
|
> Hutool 5.x支持JDK8+,对Android平台没有测试,不能保证所有工具类或工具方法可用。
|
||||||
@ -191,15 +194,16 @@ Hutool的源码分为两个分支,功能如下:
|
|||||||
|
|
||||||
- [Gitee issue](https://gitee.com/dromara/hutool/issues)
|
- [Gitee issue](https://gitee.com/dromara/hutool/issues)
|
||||||
- [Github issue](https://github.com/dromara/hutool/issues)
|
- [Github issue](https://github.com/dromara/hutool/issues)
|
||||||
|
- [Gitcode issue](https://gitcode.com/dromara/hutool/issues)
|
||||||
|
|
||||||
|
|
||||||
### 🧬贡献代码的步骤
|
### 🧬贡献代码的步骤
|
||||||
|
|
||||||
1. 在Gitee或者Github上fork项目到自己的repo
|
1. 在Gitee或者Github/Gitcode上fork项目到自己的repo
|
||||||
2. 把fork过去的项目也就是你的项目clone到你的本地
|
2. 把fork过去的项目也就是你的项目clone到你的本地
|
||||||
3. 修改代码(记得一定要修改v5-dev分支)
|
3. 修改代码(记得一定要修改v5-dev分支)
|
||||||
4. commit后push到自己的库(v5-dev分支)
|
4. commit后push到自己的库(v5-dev分支)
|
||||||
5. 登录Gitee或Github在你首页可以看到一个 pull request 按钮,点击它,填写一些说明信息,然后提交即可。
|
5. 登录Gitee或Github/Gitcode在你首页可以看到一个 pull request 按钮,点击它,填写一些说明信息,然后提交即可。
|
||||||
6. 等待维护者合并
|
6. 等待维护者合并
|
||||||
|
|
||||||
### 📐PR遵照的原则
|
### 📐PR遵照的原则
|
||||||
|
@ -3,7 +3,11 @@
|
|||||||
echo -e "\033[32mCheckout to v5-dev\033[0m"
|
echo -e "\033[32mCheckout to v5-dev\033[0m"
|
||||||
git checkout v5-dev
|
git checkout v5-dev
|
||||||
|
|
||||||
echo -e "\033[32mPush to origin v5-dev\033[0m"
|
echo -e "\033[32mPush to Github(origin) v5-dev\033[0m"
|
||||||
git push origin v5-dev
|
git push origin v5-dev
|
||||||
echo -e "\033[32mPush to osc v5-dev\033[0m"
|
|
||||||
|
echo -e "\033[32mPush to Gitee v5-dev\033[0m"
|
||||||
git push osc v5-dev
|
git push osc v5-dev
|
||||||
|
|
||||||
|
echo -e "\033[32mPush to Gitcode v5-dev\033[0m"
|
||||||
|
git push gitcode v5-dev
|
||||||
|
@ -6,7 +6,11 @@ git checkout v5-master
|
|||||||
echo -e "\033[32mMerge v5-dev branch\033[0m"
|
echo -e "\033[32mMerge v5-dev branch\033[0m"
|
||||||
git merge v5-dev -m 'Prepare release'
|
git merge v5-dev -m 'Prepare release'
|
||||||
|
|
||||||
echo -e "\033[32mPush to origin v5-master\033[0m"
|
echo -e "\033[32mPush to Github(origin) v5-master\033[0m"
|
||||||
git push origin v5-master
|
git push origin v5-master
|
||||||
echo -e "\033[32mPush to osc v5-master\033[0m"
|
|
||||||
|
echo -e "\033[32mPush to Gitee v5-master\033[0m"
|
||||||
git push osc v5-master
|
git push osc v5-master
|
||||||
|
|
||||||
|
echo -e "\033[32mPush to Gitcode v5-master\033[0m"
|
||||||
|
git push gitcode v5-master
|
||||||
|
@ -3,3 +3,4 @@
|
|||||||
git checkout v5-dev
|
git checkout v5-dev
|
||||||
git pull osc v5-dev
|
git pull osc v5-dev
|
||||||
git pull origin v5-dev
|
git pull origin v5-dev
|
||||||
|
git pull gitcode v5-dev
|
||||||
|
@ -1 +1 @@
|
|||||||
5.8.34
|
5.8.35
|
||||||
|
@ -1 +1 @@
|
|||||||
var version = '5.8.34'
|
var version = '5.8.35'
|
@ -9,7 +9,7 @@
|
|||||||
<parent>
|
<parent>
|
||||||
<groupId>cn.hutool</groupId>
|
<groupId>cn.hutool</groupId>
|
||||||
<artifactId>hutool-parent</artifactId>
|
<artifactId>hutool-parent</artifactId>
|
||||||
<version>5.8.34</version>
|
<version>5.8.35</version>
|
||||||
</parent>
|
</parent>
|
||||||
|
|
||||||
<artifactId>hutool-all</artifactId>
|
<artifactId>hutool-all</artifactId>
|
||||||
|
@ -9,7 +9,7 @@
|
|||||||
<parent>
|
<parent>
|
||||||
<groupId>cn.hutool</groupId>
|
<groupId>cn.hutool</groupId>
|
||||||
<artifactId>hutool-parent</artifactId>
|
<artifactId>hutool-parent</artifactId>
|
||||||
<version>5.8.34</version>
|
<version>5.8.35</version>
|
||||||
</parent>
|
</parent>
|
||||||
|
|
||||||
<artifactId>hutool-aop</artifactId>
|
<artifactId>hutool-aop</artifactId>
|
||||||
|
@ -9,7 +9,7 @@
|
|||||||
<parent>
|
<parent>
|
||||||
<groupId>cn.hutool</groupId>
|
<groupId>cn.hutool</groupId>
|
||||||
<artifactId>hutool-parent</artifactId>
|
<artifactId>hutool-parent</artifactId>
|
||||||
<version>5.8.34</version>
|
<version>5.8.35</version>
|
||||||
</parent>
|
</parent>
|
||||||
|
|
||||||
<artifactId>hutool-bloomFilter</artifactId>
|
<artifactId>hutool-bloomFilter</artifactId>
|
||||||
|
@ -9,7 +9,7 @@
|
|||||||
<parent>
|
<parent>
|
||||||
<groupId>cn.hutool</groupId>
|
<groupId>cn.hutool</groupId>
|
||||||
<artifactId>hutool-parent</artifactId>
|
<artifactId>hutool-parent</artifactId>
|
||||||
<version>5.8.34</version>
|
<version>5.8.35</version>
|
||||||
</parent>
|
</parent>
|
||||||
|
|
||||||
<artifactId>hutool-bom</artifactId>
|
<artifactId>hutool-bom</artifactId>
|
||||||
|
@ -9,7 +9,7 @@
|
|||||||
<parent>
|
<parent>
|
||||||
<groupId>cn.hutool</groupId>
|
<groupId>cn.hutool</groupId>
|
||||||
<artifactId>hutool-parent</artifactId>
|
<artifactId>hutool-parent</artifactId>
|
||||||
<version>5.8.34</version>
|
<version>5.8.35</version>
|
||||||
</parent>
|
</parent>
|
||||||
|
|
||||||
<artifactId>hutool-cache</artifactId>
|
<artifactId>hutool-cache</artifactId>
|
||||||
|
@ -16,7 +16,7 @@ import java.util.LinkedHashMap;
|
|||||||
* @param <V> 值类型
|
* @param <V> 值类型
|
||||||
* @author Looly
|
* @author Looly
|
||||||
*/
|
*/
|
||||||
public class FIFOCache<K, V> extends StampedCache<K, V> {
|
public class FIFOCache<K, V> extends ReentrantCache<K, V> {
|
||||||
private static final long serialVersionUID = 1L;
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -15,7 +15,7 @@ import java.util.Iterator;
|
|||||||
* @param <K> 键类型
|
* @param <K> 键类型
|
||||||
* @param <V> 值类型
|
* @param <V> 值类型
|
||||||
*/
|
*/
|
||||||
public class LFUCache<K, V> extends StampedCache<K, V> {
|
public class LFUCache<K, V> extends ReentrantCache<K, V> {
|
||||||
private static final long serialVersionUID = 1L;
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -12,7 +12,9 @@ import java.util.concurrent.locks.StampedLock;
|
|||||||
* @param <V> 值类型
|
* @param <V> 值类型
|
||||||
* @author looly
|
* @author looly
|
||||||
* @since 5.7.15
|
* @since 5.7.15
|
||||||
|
* @deprecated Map使用StampedLock可能造成数据不一致甚至Map循环调用,此缓存废弃
|
||||||
*/
|
*/
|
||||||
|
@Deprecated
|
||||||
public abstract class StampedCache<K, V> extends AbstractCache<K, V> {
|
public abstract class StampedCache<K, V> extends AbstractCache<K, V> {
|
||||||
private static final long serialVersionUID = 1L;
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
@ -88,7 +90,12 @@ public abstract class StampedCache<K, V> extends AbstractCache<K, V> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 获取值
|
* 获取值,使用乐观锁,但是此方法可能导致读取脏数据,但对于缓存业务可容忍。情况如下:
|
||||||
|
* <pre>
|
||||||
|
* 1. 读取时无写入,不冲突,直接获取值
|
||||||
|
* 2. 读取时无写入,但是乐观读时触发了并发异常,此时获取同步锁,获取新值
|
||||||
|
* 4. 读取时有写入,此时获取同步锁,获取新值
|
||||||
|
* </pre>
|
||||||
*
|
*
|
||||||
* @param key 键
|
* @param key 键
|
||||||
* @param isUpdateLastAccess 是否更新最后修改时间
|
* @param isUpdateLastAccess 是否更新最后修改时间
|
||||||
@ -97,10 +104,24 @@ public abstract class StampedCache<K, V> extends AbstractCache<K, V> {
|
|||||||
*/
|
*/
|
||||||
private V get(K key, boolean isUpdateLastAccess, boolean isUpdateCount) {
|
private V get(K key, boolean isUpdateLastAccess, boolean isUpdateCount) {
|
||||||
// 尝试读取缓存,使用乐观读锁
|
// 尝试读取缓存,使用乐观读锁
|
||||||
|
CacheObj<K, V> co = null;
|
||||||
long stamp = lock.tryOptimisticRead();
|
long stamp = lock.tryOptimisticRead();
|
||||||
CacheObj<K, V> co = getWithoutLock(key);
|
boolean isReadError = true;
|
||||||
if (false == lock.validate(stamp)) {
|
if(lock.validate(stamp)){
|
||||||
// 有写线程修改了此对象,悲观读
|
try{
|
||||||
|
// 乐观读,可能读取脏数据,在缓存中可容忍,分两种情况
|
||||||
|
// 1. 读取时无线程写入
|
||||||
|
// 2. 读取时有线程写入,导致数据不一致,此时读取未更新的缓存值
|
||||||
|
co = getWithoutLock(key);
|
||||||
|
isReadError = false;
|
||||||
|
} catch (final Exception ignore){
|
||||||
|
// ignore
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(isReadError){
|
||||||
|
// 转换为悲观读
|
||||||
|
// 原因可能为无锁读时触发并发异常,或者锁被占(正在写)
|
||||||
stamp = lock.readLock();
|
stamp = lock.readLock();
|
||||||
try {
|
try {
|
||||||
co = getWithoutLock(key);
|
co = getWithoutLock(key);
|
||||||
|
@ -17,7 +17,7 @@ import java.util.concurrent.ScheduledFuture;
|
|||||||
* @param <K> 键类型
|
* @param <K> 键类型
|
||||||
* @param <V> 值类型
|
* @param <V> 值类型
|
||||||
*/
|
*/
|
||||||
public class TimedCache<K, V> extends StampedCache<K, V> {
|
public class TimedCache<K, V> extends ReentrantCache<K, V> {
|
||||||
private static final long serialVersionUID = 1L;
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
/** 正在执行的定时任务 */
|
/** 正在执行的定时任务 */
|
||||||
|
@ -9,7 +9,7 @@
|
|||||||
<parent>
|
<parent>
|
||||||
<groupId>cn.hutool</groupId>
|
<groupId>cn.hutool</groupId>
|
||||||
<artifactId>hutool-parent</artifactId>
|
<artifactId>hutool-parent</artifactId>
|
||||||
<version>5.8.34</version>
|
<version>5.8.35</version>
|
||||||
</parent>
|
</parent>
|
||||||
|
|
||||||
<artifactId>hutool-captcha</artifactId>
|
<artifactId>hutool-captcha</artifactId>
|
||||||
|
@ -113,9 +113,7 @@ public class GifCaptcha extends AbstractCaptcha {
|
|||||||
* @return this
|
* @return this
|
||||||
*/
|
*/
|
||||||
public GifCaptcha setRepeat(int repeat) {
|
public GifCaptcha setRepeat(int repeat) {
|
||||||
if (repeat >= 0) {
|
this.repeat = Math.max(repeat, 0);
|
||||||
this.repeat = repeat;
|
|
||||||
}
|
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -0,0 +1,116 @@
|
|||||||
|
package cn.hutool.captcha;
|
||||||
|
|
||||||
|
import org.junit.jupiter.api.BeforeEach;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
|
import java.awt.*;
|
||||||
|
import java.awt.image.BufferedImage;
|
||||||
|
import java.io.ByteArrayOutputStream;
|
||||||
|
import java.lang.reflect.Field;
|
||||||
|
import java.lang.reflect.Method;
|
||||||
|
|
||||||
|
import static org.junit.jupiter.api.Assertions.*;
|
||||||
|
|
||||||
|
public class GifCaptchaUtilTest {
|
||||||
|
|
||||||
|
private GifCaptcha captcha;
|
||||||
|
|
||||||
|
@BeforeEach
|
||||||
|
public void setUp() {
|
||||||
|
// 初始化 GifCaptcha 类的实例
|
||||||
|
captcha = new GifCaptcha(200, 100, 4, 10); // width, height, codeCount, interfereCount
|
||||||
|
}
|
||||||
|
|
||||||
|
// 使用反射调用私有方法
|
||||||
|
private Object invokePrivateMethod(String methodName, Class<?>[] parameterTypes, Object[] parameters) throws Exception {
|
||||||
|
Method method = GifCaptcha.class.getDeclaredMethod(methodName, parameterTypes);
|
||||||
|
method.setAccessible(true); // 允许访问私有方法
|
||||||
|
return method.invoke(captcha, parameters);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 测试 setQuality() 方法
|
||||||
|
@Test
|
||||||
|
public void testSetQuality() throws Exception {
|
||||||
|
captcha.setQuality(20);
|
||||||
|
// 通过反射获取 quality 字段的值并进行断言
|
||||||
|
assertEquals(20, getPrivateField("quality"), "Quality 应该设置为 20");
|
||||||
|
|
||||||
|
captcha.setQuality(0); // 设置无效值,应该被设置为 1
|
||||||
|
assertEquals(1, getPrivateField("quality"), "Quality 应该设置为 1,如果小于 1");
|
||||||
|
}
|
||||||
|
|
||||||
|
// 测试 setRepeat() 方法
|
||||||
|
@Test
|
||||||
|
public void testSetRepeat() throws Exception {
|
||||||
|
captcha.setRepeat(5);
|
||||||
|
// 通过反射获取 repeat 字段的值并进行断言
|
||||||
|
assertEquals(5, getPrivateField("repeat"), "Repeat 应该设置为 5");
|
||||||
|
|
||||||
|
captcha.setRepeat(-1); // 设置无效值,应该保持为 0
|
||||||
|
assertEquals(0, getPrivateField("repeat"), "Repeat 应该设置为 0,如果设置了负值");
|
||||||
|
}
|
||||||
|
|
||||||
|
// 测试 setColorRange() 方法
|
||||||
|
@Test
|
||||||
|
public void testSetColorRange() throws Exception {
|
||||||
|
captcha.setMinColor(100).setMaxColor(200);
|
||||||
|
// 通过反射获取 minColor 和 maxColor 字段的值并进行断言
|
||||||
|
assertEquals(100, getPrivateField("minColor"), "Min color 应该设置为 100");
|
||||||
|
assertEquals(200, getPrivateField("maxColor"), "Max color 应该设置为 200");
|
||||||
|
}
|
||||||
|
|
||||||
|
// 测试生成验证码图像的方法 createCode()
|
||||||
|
@Test
|
||||||
|
public void testCreateCode() throws Exception {
|
||||||
|
captcha.createCode();
|
||||||
|
byte[] imageBytes = captcha.getImageBytes();
|
||||||
|
|
||||||
|
// 检查生成的图片字节是否不为 null 或空
|
||||||
|
assertNotNull(imageBytes, "生成的图片字节不应该为 null");
|
||||||
|
assertTrue(imageBytes.length > 0, "生成的图片字节不应该为空");
|
||||||
|
|
||||||
|
// 可选:你也可以通过解码图片字节,检查它是否是有效的 GIF 格式
|
||||||
|
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
||||||
|
out.write(imageBytes);
|
||||||
|
|
||||||
|
// 解码图片检查它是否为有效的 GIF(假设你有库可以解码 GIF)
|
||||||
|
// ImageIO.read(new ByteArrayInputStream(imageBytes)); // 可以取消注释来检查它是否是有效的 GIF
|
||||||
|
}
|
||||||
|
|
||||||
|
// 测试 graphicsImage() 方法
|
||||||
|
@Test
|
||||||
|
public void testGraphicsImage() throws Exception {
|
||||||
|
char[] chars = new char[]{'A', 'B', 'C', 'D'};
|
||||||
|
Color[] colors = new Color[]{
|
||||||
|
Color.RED, Color.GREEN, Color.BLUE, Color.YELLOW
|
||||||
|
};
|
||||||
|
|
||||||
|
// 使用反射调用 private 方法 graphicsImage
|
||||||
|
Object result = invokePrivateMethod("graphicsImage", new Class[]{char[].class, Color[].class, char[].class, int.class}, new Object[]{chars, colors, chars, 0});
|
||||||
|
|
||||||
|
assertNotNull(result, "生成的图片不应该为 null");
|
||||||
|
assertInstanceOf(BufferedImage.class, result, "返回的结果应该是 BufferedImage 类型");
|
||||||
|
}
|
||||||
|
|
||||||
|
// 测试 getRandomColor() 方法
|
||||||
|
@Test
|
||||||
|
public void testRandomColor() throws Exception {
|
||||||
|
// 使用反射调用 private 方法 getRandomColor
|
||||||
|
Object result = invokePrivateMethod("getRandomColor", new Class[]{int.class, int.class}, new Object[]{0, 255});
|
||||||
|
|
||||||
|
assertNotNull(result, "生成的颜色不应该为 null");
|
||||||
|
assertInstanceOf(Color.class, result, "返回的结果应该是 Color 类型");
|
||||||
|
|
||||||
|
Color color = (Color) result;
|
||||||
|
assertTrue(color.getRed() >= 0 && color.getRed() <= 255, "颜色的红色分量应该在 0 到 255 之间");
|
||||||
|
assertTrue(color.getGreen() >= 0 && color.getGreen() <= 255, "颜色的绿色分量应该在 0 到 255 之间");
|
||||||
|
assertTrue(color.getBlue() >= 0 && color.getBlue() <= 255, "颜色的蓝色分量应该在 0 到 255 之间");
|
||||||
|
}
|
||||||
|
|
||||||
|
// 辅助方法:通过反射获取私有字段的值
|
||||||
|
private Object getPrivateField(String fieldName) throws NoSuchFieldException, IllegalAccessException {
|
||||||
|
Field field = GifCaptcha.class.getDeclaredField(fieldName);
|
||||||
|
field.setAccessible(true); // 允许访问私有字段
|
||||||
|
return field.get(captcha);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,145 @@
|
|||||||
|
package cn.hutool.captcha;
|
||||||
|
|
||||||
|
import cn.hutool.captcha.generator.RandomGenerator;
|
||||||
|
import org.junit.jupiter.api.BeforeEach;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
|
import java.awt.*;
|
||||||
|
import java.awt.image.BufferedImage;
|
||||||
|
import java.lang.reflect.Method;
|
||||||
|
|
||||||
|
import static org.junit.jupiter.api.Assertions.*;
|
||||||
|
|
||||||
|
public class ShearCaptchaTest {
|
||||||
|
|
||||||
|
private ShearCaptcha captcha;
|
||||||
|
|
||||||
|
@BeforeEach
|
||||||
|
public void setUp() {
|
||||||
|
// 初始化 ShearCaptcha 实例
|
||||||
|
captcha = new ShearCaptcha(200, 100);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 测试构造函数和基本功能
|
||||||
|
@Test
|
||||||
|
public void testConstructor() {
|
||||||
|
assertNotNull(captcha, "Captcha 实例应该被成功创建");
|
||||||
|
}
|
||||||
|
|
||||||
|
// 测试生成验证码图片的功能
|
||||||
|
@Test
|
||||||
|
public void testCreateImage() {
|
||||||
|
String code = "ABCD";
|
||||||
|
Image image = captcha.createImage(code);
|
||||||
|
assertNotNull(image, "验证码图片不应该为 null");
|
||||||
|
assertInstanceOf(BufferedImage.class, image, "生成的图片应该是 BufferedImage 类型");
|
||||||
|
|
||||||
|
// 可选:进一步测试图像的内容
|
||||||
|
BufferedImage bufferedImage = (BufferedImage) image;
|
||||||
|
assertEquals(200, bufferedImage.getWidth(), "图像宽度应该为 200");
|
||||||
|
assertEquals(100, bufferedImage.getHeight(), "图像高度应该为 100");
|
||||||
|
}
|
||||||
|
|
||||||
|
// 测试绘制字符串的方法
|
||||||
|
@Test
|
||||||
|
public void testDrawString() throws Exception {
|
||||||
|
String code = "ABCD";
|
||||||
|
Method drawStringMethod = ShearCaptcha.class.getDeclaredMethod("drawString", Graphics2D.class, String.class);
|
||||||
|
drawStringMethod.setAccessible(true);
|
||||||
|
|
||||||
|
Graphics2D g2d = (Graphics2D) new BufferedImage(200, 100, BufferedImage.TYPE_INT_ARGB).getGraphics();
|
||||||
|
drawStringMethod.invoke(captcha, g2d, code);
|
||||||
|
|
||||||
|
assertNotNull(g2d, "Graphics2D 对象不应该为 null");
|
||||||
|
assertTrue(g2d.getRenderingHints().containsKey(RenderingHints.KEY_ANTIALIASING), "应该启用抗锯齿");
|
||||||
|
}
|
||||||
|
|
||||||
|
// 测试 shear() 方法
|
||||||
|
@Test
|
||||||
|
public void testShear() throws Exception {
|
||||||
|
// 使用反射测试 shear 方法
|
||||||
|
Method shearMethod = ShearCaptcha.class.getDeclaredMethod("shear", Graphics.class, int.class, int.class, Color.class);
|
||||||
|
shearMethod.setAccessible(true);
|
||||||
|
|
||||||
|
Graphics g = new BufferedImage(200, 100, BufferedImage.TYPE_INT_ARGB).getGraphics();
|
||||||
|
shearMethod.invoke(captcha, g, 200, 100, Color.WHITE);
|
||||||
|
|
||||||
|
// 假设没有明显的错误输出,认为测试通过
|
||||||
|
assertNotNull(g, "Graphics 对象不应该为 null");
|
||||||
|
}
|
||||||
|
|
||||||
|
// 测试 shearX() 方法
|
||||||
|
@Test
|
||||||
|
public void testShearX() throws Exception {
|
||||||
|
// 使用反射测试 shearX 方法
|
||||||
|
Method shearXMethod = ShearCaptcha.class.getDeclaredMethod("shearX", Graphics.class, int.class, int.class, Color.class);
|
||||||
|
shearXMethod.setAccessible(true);
|
||||||
|
|
||||||
|
Graphics g = new BufferedImage(200, 100, BufferedImage.TYPE_INT_ARGB).getGraphics();
|
||||||
|
shearXMethod.invoke(captcha, g, 200, 100, Color.RED);
|
||||||
|
|
||||||
|
// 假设没有明显的错误输出,认为测试通过
|
||||||
|
assertNotNull(g, "Graphics 对象不应该为 null");
|
||||||
|
}
|
||||||
|
|
||||||
|
// 测试 shearY() 方法
|
||||||
|
@Test
|
||||||
|
public void testShearY() throws Exception {
|
||||||
|
// 使用反射测试 shearY 方法
|
||||||
|
Method shearYMethod = ShearCaptcha.class.getDeclaredMethod("shearY", Graphics.class, int.class, int.class, Color.class);
|
||||||
|
shearYMethod.setAccessible(true);
|
||||||
|
|
||||||
|
Graphics g = new BufferedImage(200, 100, BufferedImage.TYPE_INT_ARGB).getGraphics();
|
||||||
|
shearYMethod.invoke(captcha, g, 200, 100, Color.BLUE);
|
||||||
|
|
||||||
|
// 假设没有明显的错误输出,认为测试通过
|
||||||
|
assertNotNull(g, "Graphics 对象不应该为 null");
|
||||||
|
}
|
||||||
|
|
||||||
|
// 测试 drawInterfere() 方法
|
||||||
|
@Test
|
||||||
|
public void testDrawInterfere() throws Exception {
|
||||||
|
// 使用反射测试 drawInterfere 方法
|
||||||
|
Method drawInterfereMethod = ShearCaptcha.class.getDeclaredMethod("drawInterfere", Graphics.class, int.class, int.class, int.class, int.class, int.class, Color.class);
|
||||||
|
drawInterfereMethod.setAccessible(true);
|
||||||
|
|
||||||
|
Graphics g = new BufferedImage(200, 100, BufferedImage.TYPE_INT_ARGB).getGraphics();
|
||||||
|
drawInterfereMethod.invoke(captcha, g, 0, 0, 200, 100, 4, Color.GREEN);
|
||||||
|
|
||||||
|
// 假设没有明显的错误输出,认为测试通过
|
||||||
|
assertNotNull(g, "Graphics 对象不应该为 null");
|
||||||
|
}
|
||||||
|
|
||||||
|
// 测试验证码生成时的干扰线
|
||||||
|
@Test
|
||||||
|
public void testDrawInterfereLines() {
|
||||||
|
// 设置干扰线数量
|
||||||
|
captcha = new ShearCaptcha(200, 100, 4);
|
||||||
|
Image image = captcha.createImage("ABCD");
|
||||||
|
|
||||||
|
// 检查图像内容,判断干扰线是否正确绘制
|
||||||
|
assertNotNull(image, "生成的验证码图片不应该为空");
|
||||||
|
}
|
||||||
|
|
||||||
|
// 测试验证码的尺寸
|
||||||
|
@Test
|
||||||
|
public void testCaptchaSize() {
|
||||||
|
captcha = new ShearCaptcha(300, 150);
|
||||||
|
|
||||||
|
String code = "XYZ";
|
||||||
|
Image image = captcha.createImage(code);
|
||||||
|
|
||||||
|
BufferedImage bufferedImage = (BufferedImage) image;
|
||||||
|
assertEquals(300, bufferedImage.getWidth(), "图像宽度应该为 300");
|
||||||
|
assertEquals(150, bufferedImage.getHeight(), "图像高度应该为 150");
|
||||||
|
}
|
||||||
|
|
||||||
|
// 测试生成随机验证码字符
|
||||||
|
@Test
|
||||||
|
public void testRandomGenerator() {
|
||||||
|
RandomGenerator randomGenerator = new RandomGenerator(4);
|
||||||
|
String code = randomGenerator.generate();
|
||||||
|
assertNotNull(code, "生成的验证码字符不应该为 null");
|
||||||
|
assertEquals(4, code.length(), "验证码字符长度应该为 4");
|
||||||
|
}
|
||||||
|
}
|
@ -9,7 +9,7 @@
|
|||||||
<parent>
|
<parent>
|
||||||
<groupId>cn.hutool</groupId>
|
<groupId>cn.hutool</groupId>
|
||||||
<artifactId>hutool-parent</artifactId>
|
<artifactId>hutool-parent</artifactId>
|
||||||
<version>5.8.34</version>
|
<version>5.8.35</version>
|
||||||
</parent>
|
</parent>
|
||||||
|
|
||||||
<artifactId>hutool-core</artifactId>
|
<artifactId>hutool-core</artifactId>
|
||||||
|
@ -844,16 +844,37 @@ public class DateUtil extends CalendarUtil {
|
|||||||
* @param utcString UTC时间
|
* @param utcString UTC时间
|
||||||
* @return 日期对象
|
* @return 日期对象
|
||||||
* @since 4.1.14
|
* @since 4.1.14
|
||||||
|
* @deprecated 方法歧义,带T的日期并不一定是UTC时间,请使用 {@link #parseISO8601(String)}
|
||||||
*/
|
*/
|
||||||
|
@Deprecated
|
||||||
public static DateTime parseUTC(String utcString) {
|
public static DateTime parseUTC(String utcString) {
|
||||||
if (utcString == null) {
|
return parseISO8601(utcString);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 解析ISO8601时间,格式:<br>
|
||||||
|
* <ol>
|
||||||
|
* <li>yyyy-MM-dd'T'HH:mm:ss'Z'</li>
|
||||||
|
* <li>yyyy-MM-dd'T'HH:mm:ss.SSS'Z'</li>
|
||||||
|
* <li>yyyy-MM-dd'T'HH:mm:ssZ</li>
|
||||||
|
* <li>yyyy-MM-dd'T'HH:mm:ss.SSSZ</li>
|
||||||
|
* <li>yyyy-MM-dd'T'HH:mm:ss+0800</li>
|
||||||
|
* <li>yyyy-MM-dd'T'HH:mm:ss+08:00</li>
|
||||||
|
* </ol>
|
||||||
|
*
|
||||||
|
* @param iso8601String ISO8601时间
|
||||||
|
* @return 日期对象
|
||||||
|
* @since 5.8.34
|
||||||
|
*/
|
||||||
|
public static DateTime parseISO8601(String iso8601String) {
|
||||||
|
if (iso8601String == null) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
final int length = utcString.length();
|
final int length = iso8601String.length();
|
||||||
if (StrUtil.contains(utcString, 'Z')) {
|
if (StrUtil.contains(iso8601String, 'Z')) {
|
||||||
if (length == DatePattern.UTC_PATTERN.length() - 4) {
|
if (length == DatePattern.UTC_PATTERN.length() - 4) {
|
||||||
// 格式类似:2018-09-13T05:34:31Z,-4表示减去4个单引号的长度
|
// 格式类似:2018-09-13T05:34:31Z,-4表示减去4个单引号的长度
|
||||||
return parse(utcString, DatePattern.UTC_FORMAT);
|
return parse(iso8601String, DatePattern.UTC_FORMAT);
|
||||||
}
|
}
|
||||||
|
|
||||||
final int patternLength = DatePattern.UTC_MS_PATTERN.length();
|
final int patternLength = DatePattern.UTC_MS_PATTERN.length();
|
||||||
@ -861,61 +882,61 @@ public class DateUtil extends CalendarUtil {
|
|||||||
// -4 ~ -6范围表示匹配毫秒1~3位的情况
|
// -4 ~ -6范围表示匹配毫秒1~3位的情况
|
||||||
if (length <= patternLength && length >= patternLength - 6) {
|
if (length <= patternLength && length >= patternLength - 6) {
|
||||||
// issue#I7H34N,支持最多6位毫秒
|
// issue#I7H34N,支持最多6位毫秒
|
||||||
return parse(utcString, DatePattern.UTC_MS_FORMAT);
|
return parse(iso8601String, DatePattern.UTC_MS_FORMAT);
|
||||||
}
|
}
|
||||||
} else if (StrUtil.contains(utcString, '+')) {
|
} else if (StrUtil.contains(iso8601String, '+')) {
|
||||||
// 去除类似2019-06-01T19:45:43 +08:00加号前的空格
|
// 去除类似2019-06-01T19:45:43 +08:00加号前的空格
|
||||||
utcString = utcString.replace(" +", "+");
|
iso8601String = iso8601String.replace(" +", "+");
|
||||||
final String zoneOffset = StrUtil.subAfter(utcString, '+', true);
|
final String zoneOffset = StrUtil.subAfter(iso8601String, '+', true);
|
||||||
if (StrUtil.isBlank(zoneOffset)) {
|
if (StrUtil.isBlank(zoneOffset)) {
|
||||||
throw new DateException("Invalid format: [{}]", utcString);
|
throw new DateException("Invalid format: [{}]", iso8601String);
|
||||||
}
|
}
|
||||||
if (false == StrUtil.contains(zoneOffset, ':')) {
|
if (false == StrUtil.contains(zoneOffset, ':')) {
|
||||||
// +0800转换为+08:00
|
// +0800转换为+08:00
|
||||||
final String pre = StrUtil.subBefore(utcString, '+', true);
|
final String pre = StrUtil.subBefore(iso8601String, '+', true);
|
||||||
utcString = pre + "+" + zoneOffset.substring(0, 2) + ":" + "00";
|
iso8601String = pre + "+" + zoneOffset.substring(0, 2) + ":" + "00";
|
||||||
}
|
}
|
||||||
|
|
||||||
if (StrUtil.contains(utcString, CharUtil.DOT)) {
|
if (StrUtil.contains(iso8601String, CharUtil.DOT)) {
|
||||||
// 带毫秒,格式类似:2018-09-13T05:34:31.999+08:00
|
// 带毫秒,格式类似:2018-09-13T05:34:31.999+08:00
|
||||||
utcString = normalizeMillSeconds(utcString, ".", "+");
|
iso8601String = normalizeMillSeconds(iso8601String, ".", "+");
|
||||||
return parse(utcString, DatePattern.UTC_MS_WITH_XXX_OFFSET_FORMAT);
|
return parse(iso8601String, DatePattern.UTC_MS_WITH_XXX_OFFSET_FORMAT);
|
||||||
} else {
|
} else {
|
||||||
// 格式类似:2018-09-13T05:34:31+08:00
|
// 格式类似:2018-09-13T05:34:31+08:00
|
||||||
return parse(utcString, DatePattern.UTC_WITH_XXX_OFFSET_FORMAT);
|
return parse(iso8601String, DatePattern.UTC_WITH_XXX_OFFSET_FORMAT);
|
||||||
}
|
}
|
||||||
} else if(ReUtil.contains("-\\d{2}:?00", utcString)){
|
} else if(ReUtil.contains("-\\d{2}:?00", iso8601String)){
|
||||||
// Issue#2612,类似 2022-09-14T23:59:00-08:00 或者 2022-09-14T23:59:00-0800
|
// Issue#2612,类似 2022-09-14T23:59:00-08:00 或者 2022-09-14T23:59:00-0800
|
||||||
|
|
||||||
// 去除类似2019-06-01T19:45:43 -08:00加号前的空格
|
// 去除类似2019-06-01T19:45:43 -08:00加号前的空格
|
||||||
utcString = utcString.replace(" -", "-");
|
iso8601String = iso8601String.replace(" -", "-");
|
||||||
if(':' != utcString.charAt(utcString.length() - 3)){
|
if(':' != iso8601String.charAt(iso8601String.length() - 3)){
|
||||||
utcString = utcString.substring(0, utcString.length() - 2) + ":00";
|
iso8601String = iso8601String.substring(0, iso8601String.length() - 2) + ":00";
|
||||||
}
|
}
|
||||||
|
|
||||||
if (StrUtil.contains(utcString, CharUtil.DOT)) {
|
if (StrUtil.contains(iso8601String, CharUtil.DOT)) {
|
||||||
// 带毫秒,格式类似:2018-09-13T05:34:31.999-08:00
|
// 带毫秒,格式类似:2018-09-13T05:34:31.999-08:00
|
||||||
utcString = normalizeMillSeconds(utcString, ".", "-");
|
iso8601String = normalizeMillSeconds(iso8601String, ".", "-");
|
||||||
return new DateTime(utcString, DatePattern.UTC_MS_WITH_XXX_OFFSET_FORMAT);
|
return new DateTime(iso8601String, DatePattern.UTC_MS_WITH_XXX_OFFSET_FORMAT);
|
||||||
} else {
|
} else {
|
||||||
// 格式类似:2018-09-13T05:34:31-08:00
|
// 格式类似:2018-09-13T05:34:31-08:00
|
||||||
return new DateTime(utcString, DatePattern.UTC_WITH_XXX_OFFSET_FORMAT);
|
return new DateTime(iso8601String, DatePattern.UTC_WITH_XXX_OFFSET_FORMAT);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (length == DatePattern.UTC_SIMPLE_PATTERN.length() - 2) {
|
if (length == DatePattern.UTC_SIMPLE_PATTERN.length() - 2) {
|
||||||
// 格式类似:2018-09-13T05:34:31
|
// 格式类似:2018-09-13T05:34:31
|
||||||
return parse(utcString, DatePattern.UTC_SIMPLE_FORMAT);
|
return parse(iso8601String, DatePattern.UTC_SIMPLE_FORMAT);
|
||||||
} else if (length == DatePattern.UTC_SIMPLE_PATTERN.length() - 5) {
|
} else if (length == DatePattern.UTC_SIMPLE_PATTERN.length() - 5) {
|
||||||
// 格式类似:2018-09-13T05:34
|
// 格式类似:2018-09-13T05:34
|
||||||
return parse(utcString + ":00", DatePattern.UTC_SIMPLE_FORMAT);
|
return parse(iso8601String + ":00", DatePattern.UTC_SIMPLE_FORMAT);
|
||||||
} else if (StrUtil.contains(utcString, CharUtil.DOT)) {
|
} else if (StrUtil.contains(iso8601String, CharUtil.DOT)) {
|
||||||
// 可能为: 2021-03-17T06:31:33.99
|
// 可能为: 2021-03-17T06:31:33.99
|
||||||
utcString = normalizeMillSeconds(utcString, ".", null);
|
iso8601String = normalizeMillSeconds(iso8601String, ".", null);
|
||||||
return parse(utcString, DatePattern.UTC_SIMPLE_MS_FORMAT);
|
return parse(iso8601String, DatePattern.UTC_SIMPLE_MS_FORMAT);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// 没有更多匹配的时间格式
|
// 没有更多匹配的时间格式
|
||||||
throw new DateException("No format fit for date String [{}] !", utcString);
|
throw new DateException("No format fit for date String [{}] !", iso8601String);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -1025,8 +1046,8 @@ public class DateUtil extends CalendarUtil {
|
|||||||
// Wed Aug 01 00:00:00 CST 2012
|
// Wed Aug 01 00:00:00 CST 2012
|
||||||
return parseRFC2822(dateStr);
|
return parseRFC2822(dateStr);
|
||||||
} else if (StrUtil.contains(dateStr, 'T')) {
|
} else if (StrUtil.contains(dateStr, 'T')) {
|
||||||
// UTC时间
|
// ISO8601时间
|
||||||
return parseUTC(dateStr);
|
return parseISO8601(dateStr);
|
||||||
}
|
}
|
||||||
|
|
||||||
//标准日期格式(包括单个数字的日期时间)
|
//标准日期格式(包括单个数字的日期时间)
|
||||||
@ -1975,8 +1996,8 @@ public class DateUtil extends CalendarUtil {
|
|||||||
* @since 5.7.21
|
* @since 5.7.21
|
||||||
*/
|
*/
|
||||||
public static List<DateTime> rangeContains(DateRange start, DateRange end) {
|
public static List<DateTime> rangeContains(DateRange start, DateRange end) {
|
||||||
List<DateTime> startDateTimes = CollUtil.newArrayList((Iterable<DateTime>) start);
|
List<DateTime> startDateTimes = CollUtil.newArrayList((Iterable<DateTime>) start.reset());
|
||||||
List<DateTime> endDateTimes = CollUtil.newArrayList((Iterable<DateTime>) end);
|
List<DateTime> endDateTimes = CollUtil.newArrayList((Iterable<DateTime>) end.reset());
|
||||||
return startDateTimes.stream().filter(endDateTimes::contains).collect(Collectors.toList());
|
return startDateTimes.stream().filter(endDateTimes::contains).collect(Collectors.toList());
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1990,8 +2011,8 @@ public class DateUtil extends CalendarUtil {
|
|||||||
* @since 5.7.21
|
* @since 5.7.21
|
||||||
*/
|
*/
|
||||||
public static List<DateTime> rangeNotContains(DateRange start, DateRange end) {
|
public static List<DateTime> rangeNotContains(DateRange start, DateRange end) {
|
||||||
List<DateTime> startDateTimes = CollUtil.newArrayList((Iterable<DateTime>) start);
|
List<DateTime> startDateTimes = CollUtil.newArrayList((Iterable<DateTime>) start.reset());
|
||||||
List<DateTime> endDateTimes = CollUtil.newArrayList((Iterable<DateTime>) end);
|
List<DateTime> endDateTimes = CollUtil.newArrayList((Iterable<DateTime>) end.reset());
|
||||||
return endDateTimes.stream().filter(item -> !startDateTimes.contains(item)).collect(Collectors.toList());
|
return endDateTimes.stream().filter(item -> !startDateTimes.contains(item)).collect(Collectors.toList());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -132,7 +132,8 @@ public interface RegexPool {
|
|||||||
/**
|
/**
|
||||||
* MAC地址正则
|
* MAC地址正则
|
||||||
*/
|
*/
|
||||||
String MAC_ADDRESS = "((?:[a-fA-F0-9]{1,2}[:-]){5}[a-fA-F0-9]{1,2})|0x(\\d{12}).+ETHER";
|
//String MAC_ADDRESS = "((?:[a-fA-F0-9]{1,2}[:-]){5}[a-fA-F0-9]{1,2})|0x(\\d{12}).+ETHER";
|
||||||
|
String MAC_ADDRESS = "((?:[a-fA-F0-9]{1,2}[:-]){5}[a-fA-F0-9]{1,2})|((?:[a-fA-F0-9]{1,4}[.]){2}[a-fA-F0-9]{1,4})|[a-fA-F0-9]{12}|0x(\\d{12}).+ETHER";
|
||||||
/**
|
/**
|
||||||
* 16进制字符串
|
* 16进制字符串
|
||||||
*/
|
*/
|
||||||
|
@ -238,11 +238,62 @@ public class EnumUtil {
|
|||||||
* @since 5.8.0
|
* @since 5.8.0
|
||||||
*/
|
*/
|
||||||
public static <E extends Enum<E>> E getBy(Class<E> enumClass, Predicate<? super E> predicate) {
|
public static <E extends Enum<E>> E getBy(Class<E> enumClass, Predicate<? super E> predicate) {
|
||||||
|
return getBy(enumClass, predicate, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 通过 某字段对应值 获取 枚举,获取不到时为 {@code defaultEnum}
|
||||||
|
*
|
||||||
|
* @param enumClass 枚举类
|
||||||
|
* @param predicate 条件
|
||||||
|
* @param defaultEnum 获取不到时的默认枚举值
|
||||||
|
* @param <E> 枚举类型
|
||||||
|
* @return 对应枚举 ,获取不到时为 {@code defaultEnum}
|
||||||
|
*/
|
||||||
|
public static <E extends Enum<E>> E getBy(Class<E> enumClass, Predicate<? super E> predicate, E defaultEnum) {
|
||||||
if (null == enumClass || null == predicate) {
|
if (null == enumClass || null == predicate) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
return Arrays.stream(enumClass.getEnumConstants())
|
return Arrays.stream(enumClass.getEnumConstants())
|
||||||
.filter(predicate).findFirst().orElse(null);
|
.filter(predicate).findFirst().orElse(defaultEnum);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 通过 某字段对应值 获取 枚举,获取不到时为 {@code null}
|
||||||
|
* <p>
|
||||||
|
* {@link LambdaUtil#getRealClass(Func1)}} 是相对耗时的
|
||||||
|
* 如果枚举值比较多,那么{@link EnumUtil#getBy(Func1, Object)} 方法
|
||||||
|
* 大部分时间都是被{@link LambdaUtil#getRealClass(Func1)}}所消耗的
|
||||||
|
* <br>
|
||||||
|
* 如果可以在编码过程中可以提供对应的枚举类 该方法与枚举的{@code Enum.values()}方法是差不多的。
|
||||||
|
*
|
||||||
|
* @param enumClass 枚举类, 为{@code null}返回{@code null}
|
||||||
|
* @param condition 条件字段,为{@code null}返回{@code null}
|
||||||
|
* @param value 条件字段值
|
||||||
|
* @param <E> 枚举类型
|
||||||
|
* @param <C> 字段类型
|
||||||
|
* @return 对应枚举 ,获取不到时为 {@code null}
|
||||||
|
*/
|
||||||
|
public static <E extends Enum<E>, C> E getBy(Class<E> enumClass, Func1<E, C> condition, C value) {
|
||||||
|
if (null == condition) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return getBy(enumClass, constant -> ObjUtil.equals(condition.callWithRuntimeException(constant), value));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 通过 某字段对应值 获取 枚举,获取不到时为 {@code defaultEnum}
|
||||||
|
*
|
||||||
|
* @param enumClass 枚举类, 为{@code null}返回{@code null}
|
||||||
|
* @param condition 条件字段,为{@code null}返回{@code null}
|
||||||
|
* @param value 条件字段值
|
||||||
|
* @param defaultEnum 获取不到时的默认枚举值
|
||||||
|
* @param <E> 枚举类型
|
||||||
|
* @param <C> 字段类型
|
||||||
|
* @return 对应枚举 ,获取不到时为 {@code defaultEnum}
|
||||||
|
*/
|
||||||
|
public static <E extends Enum<E>, C> E getBy(Class<E> enumClass, Func1<E, C> condition, C value, E defaultEnum) {
|
||||||
|
return ObjectUtil.defaultIfNull(getBy(enumClass, condition, value), defaultEnum);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -259,10 +310,7 @@ public class EnumUtil {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
final Class<E> implClass = LambdaUtil.getRealClass(condition);
|
final Class<E> implClass = LambdaUtil.getRealClass(condition);
|
||||||
return Arrays.stream(implClass.getEnumConstants())
|
return getBy(implClass, condition, value);
|
||||||
.filter(constant -> ObjUtil.equals(condition.callWithRuntimeException(constant), value))
|
|
||||||
.findAny()
|
|
||||||
.orElse(null);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -1,12 +1,13 @@
|
|||||||
package cn.hutool.core.date;
|
package cn.hutool.core.date;
|
||||||
|
|
||||||
import cn.hutool.core.date.BetweenFormatter.Level;
|
import cn.hutool.core.date.BetweenFormatter.Level;
|
||||||
import static org.junit.jupiter.api.Assertions.*;
|
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
import java.time.temporal.ChronoUnit;
|
import java.time.temporal.ChronoUnit;
|
||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
|
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||||
|
|
||||||
public class DateBetweenTest {
|
public class DateBetweenTest {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@ -36,6 +37,14 @@ public class DateBetweenTest {
|
|||||||
assertEquals(18, betweenYear);
|
assertEquals(18, betweenYear);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void betweenYearTest3() {
|
||||||
|
Date start = DateUtil.parse("20170301");
|
||||||
|
Date end = DateUtil.parse("2024-02-29 14:56:18");
|
||||||
|
long betweenYear = new DateBetween(start, end).betweenYear(false);
|
||||||
|
assertEquals(6, betweenYear);
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void betweenMonthTest() {
|
public void betweenMonthTest() {
|
||||||
Date start = DateUtil.parse("2017-02-01 12:23:46");
|
Date start = DateUtil.parse("2017-02-01 12:23:46");
|
||||||
|
@ -605,9 +605,9 @@ public class DateUtilTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void parseUTCTest() {
|
public void parseISO8601Test() {
|
||||||
String dateStr1 = "2018-09-13T05:34:31Z";
|
String dateStr1 = "2018-09-13T05:34:31Z";
|
||||||
DateTime dt = DateUtil.parseUTC(dateStr1);
|
DateTime dt = DateUtil.parseISO8601(dateStr1);
|
||||||
|
|
||||||
// parse方法支持UTC格式测试
|
// parse方法支持UTC格式测试
|
||||||
final DateTime dt2 = DateUtil.parse(dateStr1);
|
final DateTime dt2 = DateUtil.parse(dateStr1);
|
||||||
@ -622,12 +622,12 @@ public class DateUtilTest {
|
|||||||
assertEquals("2018-09-13 13:34:31", dateStr);
|
assertEquals("2018-09-13 13:34:31", dateStr);
|
||||||
|
|
||||||
dateStr1 = "2018-09-13T13:34:32+0800";
|
dateStr1 = "2018-09-13T13:34:32+0800";
|
||||||
dt = DateUtil.parseUTC(dateStr1);
|
dt = DateUtil.parseISO8601(dateStr1);
|
||||||
dateStr = dt.toString(TimeZone.getTimeZone("GMT+8:00"));
|
dateStr = dt.toString(TimeZone.getTimeZone("GMT+8:00"));
|
||||||
assertEquals("2018-09-13 13:34:32", dateStr);
|
assertEquals("2018-09-13 13:34:32", dateStr);
|
||||||
|
|
||||||
dateStr1 = "2018-09-13T13:34:33+08:00";
|
dateStr1 = "2018-09-13T13:34:33+08:00";
|
||||||
dt = DateUtil.parseUTC(dateStr1);
|
dt = DateUtil.parseISO8601(dateStr1);
|
||||||
dateStr = dt.toString(TimeZone.getTimeZone("GMT+8:00"));
|
dateStr = dt.toString(TimeZone.getTimeZone("GMT+8:00"));
|
||||||
assertEquals("2018-09-13 13:34:33", dateStr);
|
assertEquals("2018-09-13 13:34:33", dateStr);
|
||||||
|
|
||||||
@ -644,14 +644,14 @@ public class DateUtilTest {
|
|||||||
assertEquals("2018-09-13 13:34:35", dateStr);
|
assertEquals("2018-09-13 13:34:35", dateStr);
|
||||||
|
|
||||||
dateStr1 = "2018-09-13T13:34:36.999+0800";
|
dateStr1 = "2018-09-13T13:34:36.999+0800";
|
||||||
dt = DateUtil.parseUTC(dateStr1);
|
dt = DateUtil.parseISO8601(dateStr1);
|
||||||
final SimpleDateFormat simpleDateFormat = new SimpleDateFormat(DatePattern.NORM_DATETIME_MS_PATTERN);
|
final SimpleDateFormat simpleDateFormat = new SimpleDateFormat(DatePattern.NORM_DATETIME_MS_PATTERN);
|
||||||
simpleDateFormat.setTimeZone(TimeZone.getTimeZone("GMT+8:00"));
|
simpleDateFormat.setTimeZone(TimeZone.getTimeZone("GMT+8:00"));
|
||||||
dateStr = dt.toString(simpleDateFormat);
|
dateStr = dt.toString(simpleDateFormat);
|
||||||
assertEquals("2018-09-13 13:34:36.999", dateStr);
|
assertEquals("2018-09-13 13:34:36.999", dateStr);
|
||||||
|
|
||||||
dateStr1 = "2018-09-13T13:34:37.999+08:00";
|
dateStr1 = "2018-09-13T13:34:37.999+08:00";
|
||||||
dt = DateUtil.parseUTC(dateStr1);
|
dt = DateUtil.parseISO8601(dateStr1);
|
||||||
dateStr = dt.toString(simpleDateFormat);
|
dateStr = dt.toString(simpleDateFormat);
|
||||||
assertEquals("2018-09-13 13:34:37.999", dateStr);
|
assertEquals("2018-09-13 13:34:37.999", dateStr);
|
||||||
|
|
||||||
@ -676,19 +676,19 @@ public class DateUtilTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void parseUTCTest2() {
|
public void parseUTCTest() {
|
||||||
// issue1503@Github
|
// issue1503@Github
|
||||||
// 检查不同毫秒长度都可以正常匹配
|
// 检查不同毫秒长度都可以正常匹配
|
||||||
String utcTime = "2021-03-30T12:56:51.3Z";
|
String utcTime = "2021-03-30T12:56:51.3Z";
|
||||||
DateTime parse = DateUtil.parseUTC(utcTime);
|
DateTime parse = DateUtil.parseISO8601(utcTime);
|
||||||
assertEquals("2021-03-30 12:56:51", parse.toString());
|
assertEquals("2021-03-30 12:56:51", parse.toString());
|
||||||
|
|
||||||
utcTime = "2021-03-30T12:56:51.34Z";
|
utcTime = "2021-03-30T12:56:51.34Z";
|
||||||
parse = DateUtil.parseUTC(utcTime);
|
parse = DateUtil.parseISO8601(utcTime);
|
||||||
assertEquals("2021-03-30 12:56:51", parse.toString());
|
assertEquals("2021-03-30 12:56:51", parse.toString());
|
||||||
|
|
||||||
utcTime = "2021-03-30T12:56:51.345Z";
|
utcTime = "2021-03-30T12:56:51.345Z";
|
||||||
parse = DateUtil.parseUTC(utcTime);
|
parse = DateUtil.parseISO8601(utcTime);
|
||||||
assertEquals("2021-03-30 12:56:51", parse.toString());
|
assertEquals("2021-03-30 12:56:51", parse.toString());
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -994,7 +994,7 @@ public class DateUtilTest {
|
|||||||
|
|
||||||
@SuppressWarnings("ConstantConditions")
|
@SuppressWarnings("ConstantConditions")
|
||||||
@Test
|
@Test
|
||||||
public void parseISO8601Test() {
|
public void parseWithMilsTest() {
|
||||||
final String dt = "2020-06-03 12:32:12,333";
|
final String dt = "2020-06-03 12:32:12,333";
|
||||||
final DateTime parse = DateUtil.parse(dt);
|
final DateTime parse = DateUtil.parse(dt);
|
||||||
assertEquals("2020-06-03 12:32:12", parse.toString());
|
assertEquals("2020-06-03 12:32:12", parse.toString());
|
||||||
|
@ -0,0 +1,25 @@
|
|||||||
|
package cn.hutool.core.date;
|
||||||
|
|
||||||
|
import cn.hutool.core.lang.Console;
|
||||||
|
import org.junit.jupiter.api.Assertions;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public class IssueIB8OFSTest {
|
||||||
|
@Test
|
||||||
|
void rangeTest() {
|
||||||
|
DateRange startRange = DateUtil.range(
|
||||||
|
DateUtil.parse("2017-01-01"),
|
||||||
|
DateUtil.parse("2017-01-31"), DateField.DAY_OF_YEAR);
|
||||||
|
DateRange endRange = DateUtil.range(
|
||||||
|
DateUtil.parse("2017-01-31"),
|
||||||
|
DateUtil.parse("2017-02-02"), DateField.DAY_OF_YEAR);
|
||||||
|
|
||||||
|
List<DateTime> dateTimes = DateUtil.rangeContains(startRange, endRange);
|
||||||
|
Assertions.assertEquals(1, dateTimes.size());
|
||||||
|
|
||||||
|
List<DateTime> dateNotTimes = DateUtil.rangeNotContains(startRange, endRange);
|
||||||
|
Assertions.assertEquals(2, dateNotTimes.size());
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,12 @@
|
|||||||
|
package cn.hutool.core.date;
|
||||||
|
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
|
import java.text.SimpleDateFormat;
|
||||||
|
|
||||||
|
public class IssueIB9NPUTest {
|
||||||
|
@Test
|
||||||
|
void parseTest() {
|
||||||
|
DateUtil.parse("202409032400", new SimpleDateFormat("yyyyMMddHHmm"));
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,25 @@
|
|||||||
|
package cn.hutool.core.date;
|
||||||
|
|
||||||
|
import org.junit.jupiter.api.Assertions;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
|
import java.time.ZoneId;
|
||||||
|
import java.util.TimeZone;
|
||||||
|
|
||||||
|
public class IssueIBB6I5Test {
|
||||||
|
@Test
|
||||||
|
void parseISO8601Test() {
|
||||||
|
DateTime date = DateUtil.parseISO8601("2024-12-13T08:02:27Z");
|
||||||
|
TimeZone timeZone = TimeZone.getTimeZone(ZoneId.of("Asia/Shanghai"));
|
||||||
|
date.setTimeZone(timeZone);
|
||||||
|
Assertions.assertEquals("2024-12-13 16:02:27", date.toString());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void parseISO8601Test2() {
|
||||||
|
DateTime date = DateUtil.parseISO8601("2024-12-13T08:02:27");
|
||||||
|
TimeZone timeZone = TimeZone.getTimeZone(ZoneId.of("Asia/Shanghai"));
|
||||||
|
date.setTimeZone(timeZone);
|
||||||
|
Assertions.assertEquals("2024-12-13 08:02:27", date.toString());
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,16 @@
|
|||||||
|
package cn.hutool.core.util;
|
||||||
|
|
||||||
|
import org.junit.jupiter.api.Assertions;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
|
public class Issue3809Test {
|
||||||
|
@Test
|
||||||
|
void roundStrTest() {
|
||||||
|
Assertions.assertEquals("9999999999999999.99", NumberUtil.roundStr("9999999999999999.99", 2)); //输出结果不符合方法声明返回值规则
|
||||||
|
Assertions.assertEquals("11111111111111119.00", NumberUtil.roundStr("11111111111111119.00", 2));
|
||||||
|
Assertions.assertEquals("7999999999999999.99", NumberUtil.roundStr("7999999999999999.99", 2)); //输出结果不符合方法声明返回值规则
|
||||||
|
Assertions.assertEquals("699999999991999.92", NumberUtil.roundStr("699999999991999.92", 2)); //输出结果不符合方法声明返回值规则
|
||||||
|
Assertions.assertEquals("10.92", NumberUtil.roundStr("10.92", 2));
|
||||||
|
Assertions.assertEquals("10.99", NumberUtil.roundStr("10.99", 2));
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,16 @@
|
|||||||
|
package cn.hutool.core.util;
|
||||||
|
|
||||||
|
import cn.hutool.core.lang.PatternPool;
|
||||||
|
import org.junit.jupiter.api.Assertions;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
|
public class IssueIB95X4Test {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void isMacTest() {
|
||||||
|
Assertions.assertTrue(ReUtil.isMatch(PatternPool.MAC_ADDRESS, "ab1c.2d3e.f468"));
|
||||||
|
Assertions.assertTrue(ReUtil.isMatch(PatternPool.MAC_ADDRESS, "ab:1c:2d:3e:f4:68"));
|
||||||
|
Assertions.assertTrue(ReUtil.isMatch(PatternPool.MAC_ADDRESS, "ab-1c-2d-3e-f4-68"));
|
||||||
|
Assertions.assertTrue(ReUtil.isMatch(PatternPool.MAC_ADDRESS, "ab1c2d3ef468"));
|
||||||
|
}
|
||||||
|
}
|
@ -9,7 +9,7 @@
|
|||||||
<parent>
|
<parent>
|
||||||
<groupId>cn.hutool</groupId>
|
<groupId>cn.hutool</groupId>
|
||||||
<artifactId>hutool-parent</artifactId>
|
<artifactId>hutool-parent</artifactId>
|
||||||
<version>5.8.34</version>
|
<version>5.8.35</version>
|
||||||
</parent>
|
</parent>
|
||||||
|
|
||||||
<artifactId>hutool-cron</artifactId>
|
<artifactId>hutool-cron</artifactId>
|
||||||
|
@ -9,7 +9,7 @@
|
|||||||
<parent>
|
<parent>
|
||||||
<groupId>cn.hutool</groupId>
|
<groupId>cn.hutool</groupId>
|
||||||
<artifactId>hutool-parent</artifactId>
|
<artifactId>hutool-parent</artifactId>
|
||||||
<version>5.8.34</version>
|
<version>5.8.35</version>
|
||||||
</parent>
|
</parent>
|
||||||
|
|
||||||
<artifactId>hutool-crypto</artifactId>
|
<artifactId>hutool-crypto</artifactId>
|
||||||
|
@ -9,7 +9,7 @@
|
|||||||
<parent>
|
<parent>
|
||||||
<groupId>cn.hutool</groupId>
|
<groupId>cn.hutool</groupId>
|
||||||
<artifactId>hutool-parent</artifactId>
|
<artifactId>hutool-parent</artifactId>
|
||||||
<version>5.8.34</version>
|
<version>5.8.35</version>
|
||||||
</parent>
|
</parent>
|
||||||
|
|
||||||
<artifactId>hutool-db</artifactId>
|
<artifactId>hutool-db</artifactId>
|
||||||
|
@ -8,6 +8,7 @@ import cn.hutool.core.util.ArrayUtil;
|
|||||||
import cn.hutool.core.util.CharsetUtil;
|
import cn.hutool.core.util.CharsetUtil;
|
||||||
import cn.hutool.core.util.ReflectUtil;
|
import cn.hutool.core.util.ReflectUtil;
|
||||||
import cn.hutool.core.util.StrUtil;
|
import cn.hutool.core.util.StrUtil;
|
||||||
|
import cn.hutool.db.sql.Condition;
|
||||||
import cn.hutool.db.sql.SqlUtil;
|
import cn.hutool.db.sql.SqlUtil;
|
||||||
|
|
||||||
import java.nio.charset.Charset;
|
import java.nio.charset.Charset;
|
||||||
@ -266,6 +267,17 @@ public class Entity extends Dict {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// -------------------------------------------------------------------- Put and Set start
|
// -------------------------------------------------------------------- Put and Set start
|
||||||
|
/**
|
||||||
|
* 添加条件
|
||||||
|
*
|
||||||
|
* @param condition 条件
|
||||||
|
* @return this
|
||||||
|
* @since 5.8.34
|
||||||
|
*/
|
||||||
|
public Entity addCondition(final Condition condition) {
|
||||||
|
return set(condition.getField(), condition);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Entity set(String field, Object value) {
|
public Entity set(String field, Object value) {
|
||||||
return (Entity) super.set(field, value);
|
return (Entity) super.set(field, value);
|
||||||
|
@ -9,7 +9,7 @@
|
|||||||
<parent>
|
<parent>
|
||||||
<groupId>cn.hutool</groupId>
|
<groupId>cn.hutool</groupId>
|
||||||
<artifactId>hutool-parent</artifactId>
|
<artifactId>hutool-parent</artifactId>
|
||||||
<version>5.8.34</version>
|
<version>5.8.35</version>
|
||||||
</parent>
|
</parent>
|
||||||
|
|
||||||
<artifactId>hutool-dfa</artifactId>
|
<artifactId>hutool-dfa</artifactId>
|
||||||
|
@ -9,7 +9,7 @@
|
|||||||
<parent>
|
<parent>
|
||||||
<groupId>cn.hutool</groupId>
|
<groupId>cn.hutool</groupId>
|
||||||
<artifactId>hutool-parent</artifactId>
|
<artifactId>hutool-parent</artifactId>
|
||||||
<version>5.8.34</version>
|
<version>5.8.35</version>
|
||||||
</parent>
|
</parent>
|
||||||
|
|
||||||
<artifactId>hutool-extra</artifactId>
|
<artifactId>hutool-extra</artifactId>
|
||||||
|
@ -708,9 +708,11 @@ public class Ftp extends AbstractFtp {
|
|||||||
if (null != fileNameCharset) {
|
if (null != fileNameCharset) {
|
||||||
fileName = new String(fileName.getBytes(fileNameCharset), StandardCharsets.ISO_8859_1);
|
fileName = new String(fileName.getBytes(fileNameCharset), StandardCharsets.ISO_8859_1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
boolean isSuccess;
|
||||||
try {
|
try {
|
||||||
client.setFileType(FTPClient.BINARY_FILE_TYPE);
|
client.setFileType(FTPClient.BINARY_FILE_TYPE);
|
||||||
client.retrieveFile(fileName, out);
|
isSuccess = client.retrieveFile(fileName, out);
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
throw new IORuntimeException(e);
|
throw new IORuntimeException(e);
|
||||||
} finally {
|
} finally {
|
||||||
@ -718,6 +720,10 @@ public class Ftp extends AbstractFtp {
|
|||||||
cd(pwd);
|
cd(pwd);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if(false == isSuccess){
|
||||||
|
throw new FtpException("retrieveFile return false");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -169,7 +169,7 @@ public class Sftp extends AbstractFtp {
|
|||||||
*/
|
*/
|
||||||
public void init() {
|
public void init() {
|
||||||
// issue#IB69U8 如果用户传入Session对象,则不能使用配置初始化,而是尝试重新连接
|
// issue#IB69U8 如果用户传入Session对象,则不能使用配置初始化,而是尝试重新连接
|
||||||
if(StrUtil.isEmpty(this.ftpConfig.getHost()) && null != this.session){
|
if(null != this.session){
|
||||||
try {
|
try {
|
||||||
this.session.connect((int) this.ftpConfig.getConnectionTimeout());
|
this.session.connect((int) this.ftpConfig.getConnectionTimeout());
|
||||||
} catch (JSchException e) {
|
} catch (JSchException e) {
|
||||||
|
@ -9,7 +9,7 @@
|
|||||||
<parent>
|
<parent>
|
||||||
<groupId>cn.hutool</groupId>
|
<groupId>cn.hutool</groupId>
|
||||||
<artifactId>hutool-parent</artifactId>
|
<artifactId>hutool-parent</artifactId>
|
||||||
<version>5.8.34</version>
|
<version>5.8.35</version>
|
||||||
</parent>
|
</parent>
|
||||||
|
|
||||||
<artifactId>hutool-http</artifactId>
|
<artifactId>hutool-http</artifactId>
|
||||||
|
@ -0,0 +1,15 @@
|
|||||||
|
package cn.hutool.http;
|
||||||
|
|
||||||
|
import cn.hutool.core.lang.Console;
|
||||||
|
import org.junit.jupiter.api.Disabled;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
|
public class IssueIB7REWTest {
|
||||||
|
@Test
|
||||||
|
@Disabled
|
||||||
|
void getTest() {
|
||||||
|
System.setProperty("jdk.tls.namedCurves", "secp256r1,secp384r1,secp521r1");
|
||||||
|
final String s = HttpUtil.get("https://ebssec.boc.cn/");
|
||||||
|
Console.log(s);
|
||||||
|
}
|
||||||
|
}
|
@ -9,7 +9,7 @@
|
|||||||
<parent>
|
<parent>
|
||||||
<groupId>cn.hutool</groupId>
|
<groupId>cn.hutool</groupId>
|
||||||
<artifactId>hutool-parent</artifactId>
|
<artifactId>hutool-parent</artifactId>
|
||||||
<version>5.8.34</version>
|
<version>5.8.35</version>
|
||||||
</parent>
|
</parent>
|
||||||
|
|
||||||
<artifactId>hutool-json</artifactId>
|
<artifactId>hutool-json</artifactId>
|
||||||
|
@ -97,6 +97,32 @@ public interface JSON extends Cloneable, Serializable, IJSONTypeConverter {
|
|||||||
*/
|
*/
|
||||||
<T> T getByPath(String expression, Class<T> resultType);
|
<T> T getByPath(String expression, Class<T> resultType);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 通过表达式获取JSON中嵌套的对象<br>
|
||||||
|
* <ol>
|
||||||
|
* <li>.表达式,可以获取Bean对象中的属性(字段)值或者Map中key对应的值</li>
|
||||||
|
* <li>[]表达式,可以获取集合等对象中对应index的值</li>
|
||||||
|
* </ol>
|
||||||
|
* <p>
|
||||||
|
* 表达式栗子:
|
||||||
|
*
|
||||||
|
* <pre>
|
||||||
|
* persion
|
||||||
|
* persion.name
|
||||||
|
* persons[3]
|
||||||
|
* person.friends[5].name
|
||||||
|
* </pre>
|
||||||
|
* <p>
|
||||||
|
* 获取表达式对应值后转换为对应类型的值
|
||||||
|
*
|
||||||
|
* @param expression 表达式
|
||||||
|
* @param targetType 返回值类型
|
||||||
|
* @return 对象
|
||||||
|
* @see BeanPath#get(Object)
|
||||||
|
* @since 5.8.34
|
||||||
|
*/
|
||||||
|
<T> T getByPath(String expression, TypeReference<T> targetType);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 格式化打印JSON,缩进为4个空格
|
* 格式化打印JSON,缩进为4个空格
|
||||||
*
|
*
|
||||||
|
@ -3,6 +3,7 @@ package cn.hutool.json;
|
|||||||
import cn.hutool.core.bean.BeanPath;
|
import cn.hutool.core.bean.BeanPath;
|
||||||
import cn.hutool.core.collection.CollUtil;
|
import cn.hutool.core.collection.CollUtil;
|
||||||
import cn.hutool.core.lang.Filter;
|
import cn.hutool.core.lang.Filter;
|
||||||
|
import cn.hutool.core.lang.TypeReference;
|
||||||
import cn.hutool.core.lang.Validator;
|
import cn.hutool.core.lang.Validator;
|
||||||
import cn.hutool.core.lang.mutable.Mutable;
|
import cn.hutool.core.lang.mutable.Mutable;
|
||||||
import cn.hutool.core.lang.mutable.MutableObj;
|
import cn.hutool.core.lang.mutable.MutableObj;
|
||||||
@ -13,6 +14,7 @@ import cn.hutool.json.serialize.JSONWriter;
|
|||||||
|
|
||||||
import java.io.StringWriter;
|
import java.io.StringWriter;
|
||||||
import java.io.Writer;
|
import java.io.Writer;
|
||||||
|
import java.lang.reflect.Type;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.Iterator;
|
import java.util.Iterator;
|
||||||
@ -218,6 +220,11 @@ public class JSONArray implements JSON, JSONGetter<Integer>, List<Object>, Rando
|
|||||||
return JSONConverter.jsonConvert(resultType, getByPath(expression), getConfig());
|
return JSONConverter.jsonConvert(resultType, getByPath(expression), getConfig());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public <T> T getByPath(String expression, TypeReference<T> targetType) {
|
||||||
|
return JSONConverter.jsonConvert(targetType, getByPath(expression), getConfig());
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void putByPath(String expression, Object value) {
|
public void putByPath(String expression, Object value) {
|
||||||
BeanPath.create(expression).set(this, value);
|
BeanPath.create(expression).set(this, value);
|
||||||
|
@ -3,6 +3,7 @@ package cn.hutool.json;
|
|||||||
import cn.hutool.core.bean.BeanPath;
|
import cn.hutool.core.bean.BeanPath;
|
||||||
import cn.hutool.core.collection.CollectionUtil;
|
import cn.hutool.core.collection.CollectionUtil;
|
||||||
import cn.hutool.core.lang.Filter;
|
import cn.hutool.core.lang.Filter;
|
||||||
|
import cn.hutool.core.lang.TypeReference;
|
||||||
import cn.hutool.core.lang.mutable.MutablePair;
|
import cn.hutool.core.lang.mutable.MutablePair;
|
||||||
import cn.hutool.core.map.CaseInsensitiveMap;
|
import cn.hutool.core.map.CaseInsensitiveMap;
|
||||||
import cn.hutool.core.map.MapUtil;
|
import cn.hutool.core.map.MapUtil;
|
||||||
@ -14,6 +15,7 @@ import cn.hutool.json.serialize.JSONWriter;
|
|||||||
|
|
||||||
import java.io.StringWriter;
|
import java.io.StringWriter;
|
||||||
import java.io.Writer;
|
import java.io.Writer;
|
||||||
|
import java.lang.reflect.Type;
|
||||||
import java.math.BigDecimal;
|
import java.math.BigDecimal;
|
||||||
import java.math.BigInteger;
|
import java.math.BigInteger;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
@ -320,6 +322,11 @@ public class JSONObject extends MapWrapper<String, Object> implements JSON, JSON
|
|||||||
return JSONConverter.jsonConvert(resultType, getByPath(expression), getConfig());
|
return JSONConverter.jsonConvert(resultType, getByPath(expression), getConfig());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public <T> T getByPath(String expression, TypeReference<T> targetType) {
|
||||||
|
return JSONConverter.jsonConvert(targetType, getByPath(expression), getConfig());
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void putByPath(String expression, Object value) {
|
public void putByPath(String expression, Object value) {
|
||||||
BeanPath.create(expression).set(this, value);
|
BeanPath.create(expression).set(this, value);
|
||||||
|
@ -0,0 +1,38 @@
|
|||||||
|
package cn.hutool.json;
|
||||||
|
|
||||||
|
import cn.hutool.json.serialize.GlobalSerializeMapping;
|
||||||
|
import cn.hutool.json.serialize.JSONObjectSerializer;
|
||||||
|
import org.junit.jupiter.api.Assertions;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
|
public class IssueIB9MH0Test {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void parseTest() {
|
||||||
|
GlobalSerializeMapping.put(TabTypeEnum.class, (JSONObjectSerializer<TabTypeEnum>) (json, bean) -> json.set("code", bean.getCode())
|
||||||
|
.set("title", bean.getTitle()));
|
||||||
|
final JSON parse = JSONUtil.parse(TabTypeEnum._01);
|
||||||
|
Assertions.assertEquals("{\"code\":\"tab_people_home\",\"title\":\"首页\"}", parse.toString());
|
||||||
|
}
|
||||||
|
|
||||||
|
public enum TabTypeEnum {
|
||||||
|
_01("tab_people_home","首页"),
|
||||||
|
_02("tab_people_hospital","医院");
|
||||||
|
|
||||||
|
private String code;
|
||||||
|
private String title;
|
||||||
|
|
||||||
|
TabTypeEnum(String code, String title) {
|
||||||
|
this.code = code;
|
||||||
|
this.title = title;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getCode() {
|
||||||
|
return code;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getTitle() {
|
||||||
|
return title;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -1,8 +1,12 @@
|
|||||||
package cn.hutool.json;
|
package cn.hutool.json;
|
||||||
|
|
||||||
import static org.junit.jupiter.api.Assertions.*;
|
import static org.junit.jupiter.api.Assertions.*;
|
||||||
|
|
||||||
|
import cn.hutool.core.lang.TypeReference;
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* JSON路径单元测试
|
* JSON路径单元测试
|
||||||
*
|
*
|
||||||
@ -27,4 +31,23 @@ public class JSONPathTest {
|
|||||||
Long accountId = JSONUtil.getByPath(json, "$.accountId", 0L);
|
Long accountId = JSONUtil.getByPath(json, "$.accountId", 0L);
|
||||||
assertEquals(111L, accountId.longValue());
|
assertEquals(111L, accountId.longValue());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void getByPathTest3(){
|
||||||
|
String str = "[{'accountId':1},{'accountId':2},{'accountId':3}]";
|
||||||
|
JSON json = JSONUtil.parse(str);
|
||||||
|
// 返回指定泛型的对象 List<Long>
|
||||||
|
List<Long> accountIds = json.getByPath("$.accountId", new TypeReference<List<Long>>() {
|
||||||
|
});
|
||||||
|
assertNotNull(accountIds);
|
||||||
|
assertArrayEquals(new Long[]{1L, 2L, 3L}, accountIds.toArray());
|
||||||
|
|
||||||
|
str = "{\"accountInfos\": [{\"accountId\":1},{\"accountId\":2},{\"accountId\":3}]}";
|
||||||
|
json = JSONUtil.parse(str);
|
||||||
|
// 返回指定泛型的对象 List<Long>
|
||||||
|
accountIds = json.getByPath("$.accountInfos.accountId", new TypeReference<List<Long>>() {
|
||||||
|
});
|
||||||
|
assertNotNull(accountIds);
|
||||||
|
assertArrayEquals(new Long[]{1L, 2L, 3L}, accountIds.toArray());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -9,7 +9,7 @@
|
|||||||
<parent>
|
<parent>
|
||||||
<groupId>cn.hutool</groupId>
|
<groupId>cn.hutool</groupId>
|
||||||
<artifactId>hutool-parent</artifactId>
|
<artifactId>hutool-parent</artifactId>
|
||||||
<version>5.8.34</version>
|
<version>5.8.35</version>
|
||||||
</parent>
|
</parent>
|
||||||
|
|
||||||
<artifactId>hutool-jwt</artifactId>
|
<artifactId>hutool-jwt</artifactId>
|
||||||
|
@ -278,7 +278,7 @@ public class JWTSignerUtil {
|
|||||||
if (key instanceof PrivateKey || key instanceof PublicKey) {
|
if (key instanceof PrivateKey || key instanceof PublicKey) {
|
||||||
// issue3205@Github
|
// issue3205@Github
|
||||||
if(ReUtil.isMatch("ES\\d{3}", algorithmId)){
|
if(ReUtil.isMatch("ES\\d{3}", algorithmId)){
|
||||||
return new EllipticCurveJWTSigner(algorithmId, key);
|
return new EllipticCurveJWTSigner(AlgorithmUtil.getAlgorithm(algorithmId), key);
|
||||||
}
|
}
|
||||||
|
|
||||||
return new AsymmetricJWTSigner(AlgorithmUtil.getAlgorithm(algorithmId), key);
|
return new AsymmetricJWTSigner(AlgorithmUtil.getAlgorithm(algorithmId), key);
|
||||||
|
@ -9,7 +9,7 @@
|
|||||||
<parent>
|
<parent>
|
||||||
<groupId>cn.hutool</groupId>
|
<groupId>cn.hutool</groupId>
|
||||||
<artifactId>hutool-parent</artifactId>
|
<artifactId>hutool-parent</artifactId>
|
||||||
<version>5.8.34</version>
|
<version>5.8.35</version>
|
||||||
</parent>
|
</parent>
|
||||||
|
|
||||||
<artifactId>hutool-log</artifactId>
|
<artifactId>hutool-log</artifactId>
|
||||||
|
@ -9,7 +9,7 @@
|
|||||||
<parent>
|
<parent>
|
||||||
<groupId>cn.hutool</groupId>
|
<groupId>cn.hutool</groupId>
|
||||||
<artifactId>hutool-parent</artifactId>
|
<artifactId>hutool-parent</artifactId>
|
||||||
<version>5.8.34</version>
|
<version>5.8.35</version>
|
||||||
</parent>
|
</parent>
|
||||||
|
|
||||||
<artifactId>hutool-poi</artifactId>
|
<artifactId>hutool-poi</artifactId>
|
||||||
|
@ -865,12 +865,21 @@ public class ExcelWriter extends ExcelBase<ExcelWriter> {
|
|||||||
boolean isFirstRow = true;
|
boolean isFirstRow = true;
|
||||||
Map<?, ?> map;
|
Map<?, ?> map;
|
||||||
for (Object obj : data) {
|
for (Object obj : data) {
|
||||||
|
// 只第一行使用比较器排序
|
||||||
|
if (isFirstRow) {
|
||||||
if (obj instanceof Map) {
|
if (obj instanceof Map) {
|
||||||
map = new TreeMap<>(comparator);
|
map = new TreeMap<>(comparator);
|
||||||
map.putAll((Map) obj);
|
map.putAll((Map) obj);
|
||||||
} else {
|
} else {
|
||||||
map = BeanUtil.beanToMap(obj, new TreeMap<>(comparator), false, false);
|
map = BeanUtil.beanToMap(obj, new TreeMap<>(comparator), false, false);
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
if (obj instanceof Map) {
|
||||||
|
map = (Map) obj;
|
||||||
|
} else {
|
||||||
|
map = BeanUtil.beanToMap(obj, new HashMap<>(), false, false);
|
||||||
|
}
|
||||||
|
}
|
||||||
writeRow(map, isFirstRow);
|
writeRow(map, isFirstRow);
|
||||||
if (isFirstRow) {
|
if (isFirstRow) {
|
||||||
isFirstRow = false;
|
isFirstRow = false;
|
||||||
|
@ -147,6 +147,8 @@ public class Excel03SaxReader implements HSSFListener, ExcelSaxReader<Excel03Sax
|
|||||||
factory.processWorkbookEvents(request, fs);
|
factory.processWorkbookEvents(request, fs);
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
throw new POIException(e);
|
throw new POIException(e);
|
||||||
|
} catch (final StopReadException e) {
|
||||||
|
// issue#3820 跳过,用户抛出此异常,表示强制结束读取
|
||||||
} finally {
|
} finally {
|
||||||
IoUtil.close(fs);
|
IoUtil.close(fs);
|
||||||
}
|
}
|
||||||
|
@ -184,6 +184,8 @@ public class ExcelSaxUtil {
|
|||||||
throw new IORuntimeException(e);
|
throw new IORuntimeException(e);
|
||||||
} catch (SAXException e) {
|
} catch (SAXException e) {
|
||||||
throw new POIException(e);
|
throw new POIException(e);
|
||||||
|
} catch (final StopReadException e) {
|
||||||
|
// issue#3820 跳过,用户抛出此异常,表示强制结束读取
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -0,0 +1,33 @@
|
|||||||
|
package cn.hutool.poi.excel.sax;
|
||||||
|
|
||||||
|
import cn.hutool.poi.exceptions.POIException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 读取结束异常,用于标记读取结束<br>
|
||||||
|
* Sax方式读取时,如果用户在RowHandler中抛出此异常,表示读取结束,此时不再读取其他数据
|
||||||
|
*
|
||||||
|
* @author Looly
|
||||||
|
* @since 5.8.35
|
||||||
|
*/
|
||||||
|
public class StopReadException extends POIException {
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 构造
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public StopReadException() {
|
||||||
|
this("Stop read by user.");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 构造
|
||||||
|
*
|
||||||
|
* @param message 消息
|
||||||
|
*/
|
||||||
|
public StopReadException(final String message) {
|
||||||
|
super(message);
|
||||||
|
// 去除堆栈
|
||||||
|
setStackTrace(new StackTraceElement[0]);
|
||||||
|
}
|
||||||
|
}
|
@ -8,6 +8,7 @@ import cn.hutool.core.lang.Console;
|
|||||||
import cn.hutool.core.util.StrUtil;
|
import cn.hutool.core.util.StrUtil;
|
||||||
import cn.hutool.poi.excel.cell.FormulaCellValue;
|
import cn.hutool.poi.excel.cell.FormulaCellValue;
|
||||||
import cn.hutool.poi.excel.sax.Excel03SaxReader;
|
import cn.hutool.poi.excel.sax.Excel03SaxReader;
|
||||||
|
import cn.hutool.poi.excel.sax.StopReadException;
|
||||||
import cn.hutool.poi.excel.sax.handler.RowHandler;
|
import cn.hutool.poi.excel.sax.handler.RowHandler;
|
||||||
import cn.hutool.poi.exceptions.POIException;
|
import cn.hutool.poi.exceptions.POIException;
|
||||||
import org.apache.poi.ss.usermodel.CellStyle;
|
import org.apache.poi.ss.usermodel.CellStyle;
|
||||||
@ -33,6 +34,24 @@ public class ExcelSaxReadTest {
|
|||||||
ExcelUtil.readBySax("aaa.xlsx", 0, createRowHandler());
|
ExcelUtil.readBySax("aaa.xlsx", 0, createRowHandler());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void readEndByExceptionTest(){
|
||||||
|
ExcelUtil.readBySax("aaa.xlsx", 0, (sheetIndex, rowIndex, rowList) -> {
|
||||||
|
if (rowIndex == 1) {
|
||||||
|
throw new StopReadException();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void readEndByException03Test(){
|
||||||
|
ExcelUtil.readBySax("aaa.xls", 0, (sheetIndex, rowIndex, rowList) -> {
|
||||||
|
if (rowIndex == 1) {
|
||||||
|
throw new StopReadException();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void excel07ByNameTest() {
|
public void excel07ByNameTest() {
|
||||||
// 工具化快速读取
|
// 工具化快速读取
|
||||||
|
@ -2,6 +2,7 @@ package cn.hutool.poi.excel;
|
|||||||
|
|
||||||
import cn.hutool.core.collection.CollUtil;
|
import cn.hutool.core.collection.CollUtil;
|
||||||
import cn.hutool.core.collection.ListUtil;
|
import cn.hutool.core.collection.ListUtil;
|
||||||
|
import cn.hutool.core.comparator.IndexedComparator;
|
||||||
import cn.hutool.core.date.DateUtil;
|
import cn.hutool.core.date.DateUtil;
|
||||||
import cn.hutool.core.io.FileUtil;
|
import cn.hutool.core.io.FileUtil;
|
||||||
import cn.hutool.core.lang.Console;
|
import cn.hutool.core.lang.Console;
|
||||||
@ -904,4 +905,75 @@ public class ExcelWriteTest {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@Disabled
|
||||||
|
public void writeWithComparatorTest() {
|
||||||
|
// 生成测试数据, 10w行50列
|
||||||
|
List<Map<String, Object>> dataList = new ArrayList<>();
|
||||||
|
for (int i = 1; i <= 100000; i++) {
|
||||||
|
Map<String, Object> map = new HashMap<>();
|
||||||
|
map.put("test11", "test11_" + i);
|
||||||
|
map.put("test12", "test12_" + i);
|
||||||
|
map.put("test13", "test13_" + i);
|
||||||
|
map.put("test14", "test14_" + i);
|
||||||
|
map.put("test15", "test15_" + i);
|
||||||
|
map.put("test16", "test16_" + i);
|
||||||
|
map.put("test17", "test17_" + i);
|
||||||
|
map.put("test18", "test18_" + i);
|
||||||
|
map.put("test19", "test19_" + i);
|
||||||
|
map.put("test1", "test1_" + i);
|
||||||
|
map.put("test2", "test2_" + i);
|
||||||
|
map.put("test3", "test3_" + i);
|
||||||
|
map.put("test4", "test4_" + i);
|
||||||
|
map.put("test5", "test5_" + i);
|
||||||
|
map.put("test6", "test6_" + i);
|
||||||
|
map.put("test7", "test7_" + i);
|
||||||
|
map.put("test8", "test8_" + i);
|
||||||
|
map.put("test9", "test9_" + i);
|
||||||
|
map.put("test10", "test10_" + i);
|
||||||
|
map.put("test20", "test20_" + i);
|
||||||
|
map.put("test21", "test21_" + i);
|
||||||
|
map.put("test22", "test22_" + i);
|
||||||
|
map.put("test23", "test23_" + i);
|
||||||
|
map.put("test24", "test24_" + i);
|
||||||
|
map.put("test25", "test25_" + i);
|
||||||
|
map.put("test26", "test26_" + i);
|
||||||
|
map.put("test27", "test27_" + i);
|
||||||
|
map.put("test28", "test28_" + i);
|
||||||
|
map.put("test29", "test29_" + i);
|
||||||
|
map.put("test30", "test30_" + i);
|
||||||
|
map.put("test41", "test41_" + i);
|
||||||
|
map.put("test42", "test42_" + i);
|
||||||
|
map.put("test43", "test43_" + i);
|
||||||
|
map.put("test44", "test44_" + i);
|
||||||
|
map.put("test45", "test45_" + i);
|
||||||
|
map.put("test46", "test46_" + i);
|
||||||
|
map.put("test47", "test47_" + i);
|
||||||
|
map.put("test48", "test48_" + i);
|
||||||
|
map.put("test49", "test49_" + i);
|
||||||
|
map.put("test50", "test50_" + i);
|
||||||
|
map.put("test31", "test31_" + i);
|
||||||
|
map.put("test32", "test32_" + i);
|
||||||
|
map.put("test33", "test33_" + i);
|
||||||
|
map.put("test34", "test34_" + i);
|
||||||
|
map.put("test35", "test35_" + i);
|
||||||
|
map.put("test36", "test36_" + i);
|
||||||
|
map.put("test37", "test37_" + i);
|
||||||
|
map.put("test38", "test38_" + i);
|
||||||
|
map.put("test39", "test39_" + i);
|
||||||
|
map.put("test40", "test40_" + i);
|
||||||
|
dataList.add(map);
|
||||||
|
}
|
||||||
|
// 使用比较器写出
|
||||||
|
try (BigExcelWriter excelWriter = ExcelUtil.getBigWriter("d:/writeWithComparatorTest.xlsx")) {
|
||||||
|
excelWriter.write(dataList, new IndexedComparator<>(
|
||||||
|
"test1", "test2", "test3", "test4", "test5", "test6", "test7", "test8", "test9", "test10",
|
||||||
|
"test11", "test12", "test13", "test14", "test15", "test16", "test17", "test18", "test19", "test20",
|
||||||
|
"test21", "test22", "test23", "test24", "test25", "test26", "test27", "test28", "test29", "test30",
|
||||||
|
"test31", "test32", "test33", "test34", "test35", "test36", "test37", "test38", "test39", "test40",
|
||||||
|
"test41", "test42", "test43", "test44", "test45", "test46", "test47", "test48", "test49", "test50"
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -9,7 +9,7 @@
|
|||||||
<parent>
|
<parent>
|
||||||
<groupId>cn.hutool</groupId>
|
<groupId>cn.hutool</groupId>
|
||||||
<artifactId>hutool-parent</artifactId>
|
<artifactId>hutool-parent</artifactId>
|
||||||
<version>5.8.34</version>
|
<version>5.8.35</version>
|
||||||
</parent>
|
</parent>
|
||||||
|
|
||||||
<artifactId>hutool-script</artifactId>
|
<artifactId>hutool-script</artifactId>
|
||||||
|
@ -9,7 +9,7 @@
|
|||||||
<parent>
|
<parent>
|
||||||
<groupId>cn.hutool</groupId>
|
<groupId>cn.hutool</groupId>
|
||||||
<artifactId>hutool-parent</artifactId>
|
<artifactId>hutool-parent</artifactId>
|
||||||
<version>5.8.34</version>
|
<version>5.8.35</version>
|
||||||
</parent>
|
</parent>
|
||||||
|
|
||||||
<artifactId>hutool-setting</artifactId>
|
<artifactId>hutool-setting</artifactId>
|
||||||
|
@ -9,7 +9,7 @@
|
|||||||
<parent>
|
<parent>
|
||||||
<groupId>cn.hutool</groupId>
|
<groupId>cn.hutool</groupId>
|
||||||
<artifactId>hutool-parent</artifactId>
|
<artifactId>hutool-parent</artifactId>
|
||||||
<version>5.8.34</version>
|
<version>5.8.35</version>
|
||||||
</parent>
|
</parent>
|
||||||
|
|
||||||
<artifactId>hutool-socket</artifactId>
|
<artifactId>hutool-socket</artifactId>
|
||||||
|
@ -9,7 +9,7 @@
|
|||||||
<parent>
|
<parent>
|
||||||
<groupId>cn.hutool</groupId>
|
<groupId>cn.hutool</groupId>
|
||||||
<artifactId>hutool-parent</artifactId>
|
<artifactId>hutool-parent</artifactId>
|
||||||
<version>5.8.34</version>
|
<version>5.8.35</version>
|
||||||
</parent>
|
</parent>
|
||||||
|
|
||||||
<artifactId>hutool-system</artifactId>
|
<artifactId>hutool-system</artifactId>
|
||||||
|
2
pom.xml
2
pom.xml
@ -8,7 +8,7 @@
|
|||||||
|
|
||||||
<groupId>cn.hutool</groupId>
|
<groupId>cn.hutool</groupId>
|
||||||
<artifactId>hutool-parent</artifactId>
|
<artifactId>hutool-parent</artifactId>
|
||||||
<version>5.8.34</version>
|
<version>5.8.35</version>
|
||||||
<name>hutool</name>
|
<name>hutool</name>
|
||||||
<description>Hutool是一个小而全的Java工具类库,通过静态方法封装,降低相关API的学习成本,提高工作效率,使Java拥有函数式语言般的优雅,让Java语言也可以“甜甜的”。</description>
|
<description>Hutool是一个小而全的Java工具类库,通过静态方法封装,降低相关API的学习成本,提高工作效率,使Java拥有函数式语言般的优雅,让Java语言也可以“甜甜的”。</description>
|
||||||
<url>https://github.com/dromara/hutool</url>
|
<url>https://github.com/dromara/hutool</url>
|
||||||
|
Loading…
x
Reference in New Issue
Block a user