Prepare release

This commit is contained in:
Looly 2023-12-24 00:02:10 +08:00
commit 6b6c15b639
71 changed files with 942 additions and 552 deletions

View File

@ -1,6 +1,37 @@
# 🚀Changelog # 🚀Changelog
-------------------------------------------------------------------------------------------------------------
# 5.8.24(2023-12-23)
### 🐣新特性
* 【cache 】 Cache增加get重载可自定义超时时间issue#I8G0DL@Gitee
* 【cache 】 JWT#sign增加重载可选是否增加默认的typ参数issue#3386@Github
* 【db 】 增加识别OpenGauss的驱动类issue#I8K6C0@Gitee
* 【core 】 修复CharSequenceUtil注释和引用避免循环引用
* 【extra 】 SpringUtil增加getProperty重载pr#1122@Gitee
* 【core 】 FileTypeUtil增加null判断issue#3419@Github
* 【core 】 DateUtil.parse支持毫秒时间戳issue#I8NMP7@Gitee
* 【extra 】 优化TokenizerEngine使用IK分词器支持并发pr#3427@Github
* 【core 】 Opt.ofEmptyAble支持更多类型issue#I8OOSY@Gitee
* 【http 】 HTMLFilter保留p标签issue#3433@Gitee
### 🐞Bug修复
* 【core 】 修复LocalDateTime#parseDate未判断空问题问题issue#I8FN7F@Gitee
* 【http 】 修复RootAction send404 抛异常问题pr#1107@Gitee
* 【extra 】 修复Archiver 最后一个 Entry 为空文件夹时未关闭 Entry问题pr#1123@Gitee
* 【core 】 修复ImgUtil.convert png转jpg在jdk9+中失败问题issue#I8L8UA@Gitee
* 【cache 】 修复StampedCache的get方法非原子问题issue#I8MEIX@Gitee
* 【core 】 修复StrSplitter.splitByRegex使用空参数导致的OOM问题issue#3421@Github
* 【db 】 修复嵌套SQL中order by子句错误截断问题issue#I89RXV@Gitee
* 【http 】 修复graalvm编译后未读取Content-Length可能导致的读取时间过长问题issue#I6Q30X@Gitee
* 【core 】 修复JavaSourceCompiler.addSource目录处理错误问题issue#3425@Github
* 【core 】 修复时间戳转Bean时异常问题issue#I8NMP7@Gitee
* 【core 】 修复PostgreSQL使用upsert字段大小写问题问题issue#I8PB4X@Gitee
* 【extra 】 修复TinyPinyinEngine可能的空指针问题issue#3437@Github
* 【core 】 修复graalvm原生打包使用http工具被转为file协议问题issue#I8PY3Y@Gitee
* 【poi 】 修复cloneSheet参数错误导致非XSSFWorkbook错误命名问题issue#I8QIBB@Gitee
------------------------------------------------------------------------------------------------------------- -------------------------------------------------------------------------------------------------------------
# 5.8.23(2023-11-12) # 5.8.23(2023-11-12)

View File

@ -47,8 +47,7 @@
------------------------------------------------------------------------------- -------------------------------------------------------------------------------
<p align="center"> <p align="center">
<a href="#"> <a href="#"><img style="width: 45%" alt="" src="https://plus.hutool.cn/images/zanzhu.jpg"/></a>
<img alt="" src="https://plus.hutool.cn/images/zanzhu.jpg"/></a>
</p> </p>
------------------------------------------------------------------------------- -------------------------------------------------------------------------------
@ -116,7 +115,7 @@ Each module can be introduced individually, or all modules can be introduced by
## 📝Doc ## 📝Doc
[📘Chinese documentation](https://www.hutool.cn/docs/) [📘Chinese documentation](https://doc.hutool.cn/pages/index/)
[📘Chinese back-up documentation](https://plus.hutool.cn/) [📘Chinese back-up documentation](https://plus.hutool.cn/)
@ -151,18 +150,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.23</version> <version>5.8.24</version>
</dependency> </dependency>
``` ```
### 🍐Gradle ### 🍐Gradle
``` ```
implementation 'cn.hutool:hutool-all:5.8.23' implementation 'cn.hutool:hutool-all:5.8.24'
``` ```
## 📥Download ## 📥Download
- [Maven Repo](https://repo1.maven.org/maven2/cn/hutool/hutool-all/5.8.23/) - [Maven Repo](https://repo1.maven.org/maven2/cn/hutool/hutool-all/5.8.24/)
> 🔔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.
@ -210,6 +209,10 @@ Hutool welcomes anyone to contribute code to Hutool, but the author suffers from
3. Newly added methods do not use third-party library methodsUnless the method tool is add to the '**extra module**'. 3. Newly added methods do not use third-party library methodsUnless the method tool is add to the '**extra module**'.
4. Please pull request to the `v5-dev` branch. Hutool uses a new branch after 5.x: `v5-master` is the master branch, which indicates the version of the central library that has been released, and this branch does not allow pr or modifications. 4. Please pull request to the `v5-dev` branch. Hutool uses a new branch after 5.x: `v5-master` is the master branch, which indicates the version of the central library that has been released, and this branch does not allow pr or modifications.
### 📖 Documentation source code
[Documentation source code](https://gitee.com/loolly_admin/hutool-doc-handy)
------------------------------------------------------------------------------- -------------------------------------------------------------------------------
## ⭐Star Hutool ## ⭐Star Hutool

View File

@ -47,8 +47,7 @@
------------------------------------------------------------------------------- -------------------------------------------------------------------------------
<p align="center"> <p align="center">
<a href="#"> <a href="#"><img style="width: 45%" alt="" src="https://plus.hutool.cn/images/zanzhu.jpg"/></a>
<img alt="" src="https://plus.hutool.cn/images/zanzhu.jpg"/></a>
</p> </p>
------------------------------------------------------------------------------- -------------------------------------------------------------------------------
@ -107,7 +106,7 @@ Hutool = Hu + tool是原公司项目底层代码剥离后的开源库“Hu
## 📝文档 ## 📝文档
[📘中文文档](https://www.hutool.cn/docs/) [📘中文文档](https://doc.hutool.cn/pages/index/)
[📘中文备用文档](https://plus.hutool.cn/) [📘中文备用文档](https://plus.hutool.cn/)
@ -144,20 +143,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.23</version> <version>5.8.24</version>
</dependency> </dependency>
``` ```
### 🍐Gradle ### 🍐Gradle
``` ```
implementation 'cn.hutool:hutool-all:5.8.23' implementation 'cn.hutool:hutool-all:5.8.24'
``` ```
### 📥下载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.23/) - [Maven中央库](https://repo1.maven.org/maven2/cn/hutool/hutool-all/5.8.24/)
> 🔔️注意 > 🔔️注意
> Hutool 5.x支持JDK8+对Android平台没有测试不能保证所有工具类或工具方法可用。 > Hutool 5.x支持JDK8+对Android平台没有测试不能保证所有工具类或工具方法可用。
@ -213,6 +212,10 @@ Hutool欢迎任何人为Hutool添砖加瓦贡献代码不过维护者是
4. 请pull request到`v5-dev`分支。Hutool在5.x版本后使用了新的分支`v5-master`是主分支表示已经发布中央库的版本这个分支不允许pr也不允许修改。 4. 请pull request到`v5-dev`分支。Hutool在5.x版本后使用了新的分支`v5-master`是主分支表示已经发布中央库的版本这个分支不允许pr也不允许修改。
5. 我们如果关闭了你的issue或pr请不要诧异这是我们保持问题处理整洁的一种方式你依旧可以继续讨论当有讨论结果时我们会重新打开。 5. 我们如果关闭了你的issue或pr请不要诧异这是我们保持问题处理整洁的一种方式你依旧可以继续讨论当有讨论结果时我们会重新打开。
### 📖文档源码地址
[文档源码地址](https://gitee.com/loolly_admin/hutool-doc-handy) 点击前往添砖加瓦
------------------------------------------------------------------------------- -------------------------------------------------------------------------------
## ⭐Star Hutool ## ⭐Star Hutool

View File

@ -1 +1 @@
5.8.23 5.8.24

View File

@ -1 +1 @@
var version = '5.8.23' var version = '5.8.24'

View File

@ -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.23</version> <version>5.8.24</version>
</parent> </parent>
<artifactId>hutool-all</artifactId> <artifactId>hutool-all</artifactId>

View File

@ -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.23</version> <version>5.8.24</version>
</parent> </parent>
<artifactId>hutool-aop</artifactId> <artifactId>hutool-aop</artifactId>

View File

@ -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.23</version> <version>5.8.24</version>
</parent> </parent>
<artifactId>hutool-bloomFilter</artifactId> <artifactId>hutool-bloomFilter</artifactId>

View File

@ -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.23</version> <version>5.8.24</version>
</parent> </parent>
<artifactId>hutool-bom</artifactId> <artifactId>hutool-bom</artifactId>

View File

@ -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.23</version> <version>5.8.24</version>
</parent> </parent>
<artifactId>hutool-cache</artifactId> <artifactId>hutool-cache</artifactId>

View File

@ -92,6 +92,21 @@ public interface Cache<K, V> extends Iterable<V>, Serializable {
*/ */
V get(K key, boolean isUpdateLastAccess, Func0<V> supplier); V get(K key, boolean isUpdateLastAccess, Func0<V> supplier);
/**
* 从缓存中获得对象当对象不在缓存中或已经过期返回Func0回调产生的对象
* <p>
* 调用此方法时会检查上次调用时间如果与当前时间差值大于超时时间返回{@code null}否则返回值
* <p>
* 每次调用此方法会可选是否刷新最后访问时间{@code true}表示会重新计算超时时间
*
* @param key
* @param isUpdateLastAccess 是否更新最后访问时间即重新计算超时时间
* @param timeout 自定义超时时间
* @param supplier 如果不存在回调方法用于生产值对象
* @return 值对象
*/
V get(K key, boolean isUpdateLastAccess, long timeout, Func0<V> supplier);
/** /**
* 从缓存中获得对象当对象不在缓存中或已经过期返回{@code null} * 从缓存中获得对象当对象不在缓存中或已经过期返回{@code null}
* <p> * <p>

View File

@ -109,6 +109,11 @@ public abstract class AbstractCache<K, V> implements Cache<K, V> {
@Override @Override
public V get(K key, boolean isUpdateLastAccess, Func0<V> supplier) { public V get(K key, boolean isUpdateLastAccess, Func0<V> supplier) {
return get(key, isUpdateLastAccess, this.timeout, supplier);
}
@Override
public V get(K key, boolean isUpdateLastAccess, long timeout, Func0<V> supplier) {
V v = get(key, isUpdateLastAccess); V v = get(key, isUpdateLastAccess);
if (null == v && null != supplier) { if (null == v && null != supplier) {
//每个key单独获取一把锁降低锁的粒度提高并发能力see pr#1385@Github //每个key单独获取一把锁降低锁的粒度提高并发能力see pr#1385@Github
@ -125,7 +130,7 @@ public abstract class AbstractCache<K, V> implements Cache<K, V> {
throw ExceptionUtil.wrapRuntime(e); throw ExceptionUtil.wrapRuntime(e);
//throw new RuntimeException(e); //throw new RuntimeException(e);
} }
put(key, v, this.timeout); put(key, v, timeout);
} else { } else {
v = co.get(isUpdateLastAccess); v = co.get(isUpdateLastAccess);
} }
@ -249,16 +254,10 @@ public abstract class AbstractCache<K, V> implements Cache<K, V> {
* 移除key对应的对象不加锁 * 移除key对应的对象不加锁
* *
* @param key * @param key
* @param withMissCount 是否计数丢失数
* @return 移除的对象无返回null * @return 移除的对象无返回null
*/ */
protected CacheObj<K, V> removeWithoutLock(K key, boolean withMissCount) { protected CacheObj<K, V> removeWithoutLock(K key) {
final CacheObj<K, V> co = cacheMap.remove(MutableObj.of(key)); return cacheMap.remove(MutableObj.of(key));
if (withMissCount) {
// 在丢失计数有效的情况下移除一般为get时的超时操作此处应该丢失数+1
this.missCount.increment();
}
return co;
} }
/** /**

View File

@ -71,7 +71,7 @@ public class FIFOCache<K, V> extends StampedCache<K, V> {
// 清理结束后依旧是满的则删除第一个被缓存的对象 // 清理结束后依旧是满的则删除第一个被缓存的对象
if (isFull() && null != first) { if (isFull() && null != first) {
removeWithoutLock(first.key, false); removeWithoutLock(first.key);
onRemove(first.key, first.obj); onRemove(first.key, first.obj);
count++; count++;
} }

View File

@ -58,6 +58,11 @@ public class NoCache<K, V> implements Cache<K, V> {
@Override @Override
public V get(K key, boolean isUpdateLastAccess, Func0<V> supplier) { public V get(K key, boolean isUpdateLastAccess, Func0<V> supplier) {
return get(key, isUpdateLastAccess, 0, supplier);
}
@Override
public V get(K key, boolean isUpdateLastAccess, long timeout, Func0<V> supplier) {
try { try {
return (null == supplier) ? null : supplier.call(); return (null == supplier) ? null : supplier.call();
} catch (Exception e) { } catch (Exception e) {

View File

@ -33,49 +33,12 @@ public abstract class ReentrantCache<K, V> extends AbstractCache<K, V> {
@Override @Override
public boolean containsKey(K key) { public boolean containsKey(K key) {
lock.lock(); return null != getOrRemoveExpired(key, false, false);
try {
// 不存在或已移除
final CacheObj<K, V> co = getWithoutLock(key);
if (co == null) {
return false;
}
if (false == co.isExpired()) {
// 命中
return true;
}
} finally {
lock.unlock();
}
// 过期
remove(key, true);
return false;
} }
@Override @Override
public V get(K key, boolean isUpdateLastAccess) { public V get(K key, boolean isUpdateLastAccess) {
CacheObj<K, V> co; return getOrRemoveExpired(key, isUpdateLastAccess, true);
lock.lock();
try {
co = getWithoutLock(key);
} finally {
lock.unlock();
}
// 未命中
if (null == co) {
missCount.increment();
return null;
} else if (false == co.isExpired()) {
hitCount.increment();
return co.get(isUpdateLastAccess);
}
// 过期既不算命中也不算非命中
remove(key, true);
return null;
} }
@Override @Override
@ -102,7 +65,16 @@ public abstract class ReentrantCache<K, V> extends AbstractCache<K, V> {
@Override @Override
public void remove(K key) { public void remove(K key) {
remove(key, false); lock.lock();
CacheObj<K, V> co;
try {
co = removeWithoutLock(key);
} finally {
lock.unlock();
}
if (null != co) {
onRemove(co.key, co.obj);
}
} }
@Override @Override
@ -126,21 +98,37 @@ public abstract class ReentrantCache<K, V> extends AbstractCache<K, V> {
} }
/** /**
* 移除key对应的对象 * 获得值或清除过期值
*
* @param key * @param key
* @param withMissCount 是否计数丢失数 * @param isUpdateLastAccess 是否更新最后访问时间
* @param isUpdateCount 是否更新计数器
* @return 值或null
*/ */
private void remove(K key, boolean withMissCount) { private V getOrRemoveExpired(final K key, final boolean isUpdateLastAccess, final boolean isUpdateCount) {
lock.lock();
CacheObj<K, V> co; CacheObj<K, V> co;
lock.lock();
try { try {
co = removeWithoutLock(key, withMissCount); co = getWithoutLock(key);
if(null != co && co.isExpired()){
//过期移除
removeWithoutLock(key);
co = null;
}
} finally { } finally {
lock.unlock(); lock.unlock();
} }
if (null != co) {
onRemove(co.key, co.obj); // 未命中
} if (null == co) {
if(isUpdateCount){
missCount.increment();
}
return null;
}
if(isUpdateCount){
hitCount.increment();
}
return co.get(isUpdateLastAccess);
} }
} }

View File

@ -1,6 +1,7 @@
package cn.hutool.cache.impl; package cn.hutool.cache.impl;
import cn.hutool.core.collection.CopiedIter; import cn.hutool.core.collection.CopiedIter;
import cn.hutool.core.thread.ThreadUtil;
import java.util.Iterator; import java.util.Iterator;
import java.util.concurrent.locks.StampedLock; import java.util.concurrent.locks.StampedLock;
@ -33,54 +34,12 @@ public abstract class StampedCache<K, V> extends AbstractCache<K, V>{
@Override @Override
public boolean containsKey(K key) { public boolean containsKey(K key) {
final long stamp = lock.readLock(); return null != get(key, false, false);
try {
// 不存在或已移除
final CacheObj<K, V> co = getWithoutLock(key);
if (co == null) {
return false;
}
if (false == co.isExpired()) {
// 命中
return true;
}
} finally {
lock.unlockRead(stamp);
}
// 过期
remove(key, true);
return false;
} }
@Override @Override
public V get(K key, boolean isUpdateLastAccess) { public V get(K key, boolean isUpdateLastAccess) {
// 尝试读取缓存使用乐观读锁 return get(key, isUpdateLastAccess, true);
long stamp = lock.tryOptimisticRead();
CacheObj<K, V> co = getWithoutLock(key);
if(false == lock.validate(stamp)){
// 有写线程修改了此对象悲观读
stamp = lock.readLock();
try {
co = getWithoutLock(key);
} finally {
lock.unlockRead(stamp);
}
}
// 未命中
if (null == co) {
missCount.increment();
return null;
} else if (false == co.isExpired()) {
hitCount.increment();
return co.get(isUpdateLastAccess);
}
// 过期既不算命中也不算非命中
remove(key, true);
return null;
} }
@Override @Override
@ -107,7 +66,16 @@ public abstract class StampedCache<K, V> extends AbstractCache<K, V>{
@Override @Override
public void remove(K key) { public void remove(K key) {
remove(key, false); final long stamp = lock.writeLock();
CacheObj<K, V> co;
try {
co = removeWithoutLock(key);
} finally {
lock.unlockWrite(stamp);
}
if (null != co) {
onRemove(co.key, co.obj);
}
} }
@Override @Override
@ -121,21 +89,75 @@ public abstract class StampedCache<K, V> extends AbstractCache<K, V>{
} }
/** /**
* 移除key对应的对象 * 获取值
* *
* @param key * @param key
* @param withMissCount 是否计数丢失数 * @param isUpdateLastAccess 是否更新最后修改时间
* @param isUpdateCount 是否更新命中数get时更新contains时不更新
* @return 值或null
*/ */
private void remove(K key, boolean withMissCount) { private V get(K key, boolean isUpdateLastAccess, boolean isUpdateCount) {
// 尝试读取缓存使用乐观读锁
long stamp = lock.tryOptimisticRead();
CacheObj<K, V> co = getWithoutLock(key);
if (false == lock.validate(stamp)) {
// 有写线程修改了此对象悲观读
stamp = lock.readLock();
try {
co = getWithoutLock(key);
} finally {
lock.unlockRead(stamp);
}
}
// 未命中
if (null == co) {
if (isUpdateCount) {
missCount.increment();
}
return null;
} else if (false == co.isExpired()) {
if (isUpdateCount) {
hitCount.increment();
}
return co.get(isUpdateLastAccess);
}
// 悲观锁二次检查
return getOrRemoveExpired(key, isUpdateCount);
}
/**
* 同步获取值如果过期则移除之
*
* @param key
* @param isUpdateCount 是否更新命中数get时更新contains时不更新
* @return 有效值或null
*/
private V getOrRemoveExpired(K key, boolean isUpdateCount) {
final long stamp = lock.writeLock(); final long stamp = lock.writeLock();
CacheObj<K, V> co; CacheObj<K, V> co;
try { try {
co = removeWithoutLock(key, withMissCount); co = getWithoutLock(key);
if (null == co) {
return null;
}
if (false == co.isExpired()) {
// 首先尝试获取值如果值存在且有效返回之
if (isUpdateCount) {
hitCount.increment();
}
return co.getValue();
}
// 无效移除
co = removeWithoutLock(key);
} finally { } finally {
lock.unlockWrite(stamp); lock.unlockWrite(stamp);
} }
if (null != co) { if (null != co) {
onRemove(co.key, co.obj); onRemove(co.key, co.obj);
} }
return null;
} }
} }

View File

@ -0,0 +1,30 @@
package cn.hutool.cache;
import cn.hutool.cache.impl.TimedCache;
import cn.hutool.core.lang.Console;
import cn.hutool.core.thread.ThreadUtil;
import org.junit.Ignore;
import org.junit.Test;
public class IssueI8MEIXTest {
@Test
@Ignore
public void getRemoveTest() {
final TimedCache<String, String> cache = new TimedCache<>(200);
cache.put("a", "123");
ThreadUtil.sleep(300);
// 测试时在get后的remove前加sleep测试在读取过程中put新值的问题
ThreadUtil.execute(()->{
Console.log(cache.get("a"));
});
ThreadUtil.execute(()->{
cache.put("a", "456");
});
ThreadUtil.sleep(1000);
}
}

View File

@ -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.23</version> <version>5.8.24</version>
</parent> </parent>
<artifactId>hutool-captcha</artifactId> <artifactId>hutool-captcha</artifactId>

View File

@ -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.23</version> <version>5.8.24</version>
</parent> </parent>
<artifactId>hutool-core</artifactId> <artifactId>hutool-core</artifactId>

View File

@ -10,7 +10,7 @@ import cn.hutool.core.util.StrUtil;
/** /**
* 莫尔斯电码的编码和解码实现<br> * 莫尔斯电码的编码和解码实现<br>
* 参考https://github.com/TakWolf/Java-MorseCoder * 参考https://github.com/TakWolf-Deprecated/Java-MorseCoder
* *
* @author looly, TakWolf * @author looly, TakWolf
* @since 4.4.1 * @since 4.4.1

View File

@ -244,7 +244,7 @@ public class JavaSourceCompiler {
for (Resource resource : this.sourceList) { for (Resource resource : this.sourceList) {
if (resource instanceof FileResource) { if (resource instanceof FileResource) {
final File file = ((FileResource) resource).getFile(); final File file = ((FileResource) resource).getFile();
FileUtil.walkFiles(file, (subFile) -> list.addAll(JavaFileObjectUtil.getJavaFileObjects(file))); FileUtil.walkFiles(file, (subFile) -> list.addAll(JavaFileObjectUtil.getJavaFileObjects(subFile)));
} else { } else {
list.add(new JavaSourceFileObject(resource.getName(), resource.getStream())); list.add(new JavaSourceFileObject(resource.getName(), resource.getStream()));
} }

View File

@ -5,7 +5,8 @@ import cn.hutool.core.util.StrUtil;
/** /**
* 将浮点数类型的number转换成英语的表达方式 <br> * 将浮点数类型的number转换成英语的表达方式 <br>
* 参考博客http://blog.csdn.net/eric_sunah/article/details/8713226 * 参考博客http://blog.csdn.net/eric_sunah/article/details/8713226<br>
* 本质上此类为金额转英文表达因此没有四舍五入考虑小数点超过两位直接忽略
* *
* @author Looly,totalo * @author Looly,totalo
* @since 3.0.9 * @since 3.0.9

View File

@ -28,7 +28,7 @@ import java.util.function.Function;
import java.util.stream.Collectors; import java.util.stream.Collectors;
/** /**
* 时间工具类 * 日期时间工具类
* *
* @author xiaoleilu * @author xiaoleilu
* @see LocalDateTimeUtil java8日志工具类 * @see LocalDateTimeUtil java8日志工具类
@ -981,6 +981,9 @@ public class DateUtil extends CalendarUtil {
return parse(dateStr, DatePattern.PURE_DATE_FORMAT); return parse(dateStr, DatePattern.PURE_DATE_FORMAT);
} else if (length == DatePattern.PURE_TIME_PATTERN.length()) { } else if (length == DatePattern.PURE_TIME_PATTERN.length()) {
return parse(dateStr, DatePattern.PURE_TIME_FORMAT); return parse(dateStr, DatePattern.PURE_TIME_FORMAT);
}else if(length == 13){
// 时间戳
return date(NumberUtil.parseLong(dateStr));
} }
} else if (ReUtil.isMatch(PatternPool.TIME, dateStr)) { } else if (ReUtil.isMatch(PatternPool.TIME, dateStr)) {
// HH:mm:ss 或者 HH:mm 时间格式匹配单独解析 // HH:mm:ss 或者 HH:mm 时间格式匹配单独解析

View File

@ -318,7 +318,7 @@ public class LocalDateTimeUtil {
* @since 5.3.10 * @since 5.3.10
*/ */
public static LocalDate parseDate(CharSequence text, DateTimeFormatter formatter) { public static LocalDate parseDate(CharSequence text, DateTimeFormatter formatter) {
if (null == text) { if (StrUtil.isBlank(text)) {
return null; return null;
} }
if (null == formatter) { if (null == formatter) {

View File

@ -531,13 +531,7 @@ public class ImgUtil {
FileUtil.copy(srcImageFile, destImageFile, true); FileUtil.copy(srcImageFile, destImageFile, true);
} }
ImageOutputStream imageOutputStream = null; Img.from(srcImageFile).write(destImageFile);
try {
imageOutputStream = getImageOutputStream(destImageFile);
convert(read(srcImageFile), destExtName, imageOutputStream, StrUtil.equalsIgnoreCase(IMAGE_TYPE_PNG, srcExtName));
} finally {
IoUtil.close(imageOutputStream);
}
} }
/** /**
@ -560,16 +554,25 @@ public class ImgUtil {
* @param srcImage 源图像流 * @param srcImage 源图像流
* @param formatName 包含格式非正式名称的 String如JPGJPEGGIF等 * @param formatName 包含格式非正式名称的 String如JPGJPEGGIF等
* @param destImageStream 目标图像输出流 * @param destImageStream 目标图像输出流
* @param isSrcPng 源图片是否为PNG格式
* @since 4.1.14 * @since 4.1.14
*/ */
public static void convert(Image srcImage, String formatName, ImageOutputStream destImageStream, boolean isSrcPng) { public static void convert(Image srcImage, String formatName, ImageOutputStream destImageStream) {
final BufferedImage src = toBufferedImage(srcImage, isSrcPng ? BufferedImage.TYPE_INT_ARGB : BufferedImage.TYPE_INT_RGB); Img.from(srcImage).setTargetImageType(formatName).write(destImageStream);
try {
ImageIO.write(src, formatName, destImageStream);
} catch (IOException e) {
throw new IORuntimeException(e);
} }
/**
* 图像类型转换GIF=JPGGIF=PNGPNG=JPGPNG=GIF(X)BMP=PNG<br>
* 此方法并不关闭流
*
* @param srcImage 源图像流
* @param formatName 包含格式非正式名称的 String如JPGJPEGGIF等
* @param destImageStream 目标图像输出流
* @param isSrcPng 源图片是否为PNG格式参数无效
* @since 4.1.14
*/
@Deprecated
public static void convert(Image srcImage, String formatName, ImageOutputStream destImageStream, boolean isSrcPng) {
convert(srcImage, formatName, destImageStream);
} }
// ---------------------------------------------------------------------------------------------------------------------- grey // ---------------------------------------------------------------------------------------------------------------------- grey

View File

@ -79,12 +79,16 @@ public class FileTypeUtil {
* 根据文件流的头部信息获得文件类型<br> * 根据文件流的头部信息获得文件类型<br>
* 注意此方法会读取头部一些bytes造成此流接下来读取时缺少部分bytes<br> * 注意此方法会读取头部一些bytes造成此流接下来读取时缺少部分bytes<br>
* 因此如果想复用此流流需支持{@link InputStream#reset()}方法 * 因此如果想复用此流流需支持{@link InputStream#reset()}方法
*
* @param in {@link InputStream} * @param in {@link InputStream}
* @param isExact 是否精确匹配如果为false使用前64个bytes匹配如果为true使用前8192bytes匹配 * @param isExact 是否精确匹配如果为false使用前64个bytes匹配如果为true使用前8192bytes匹配
* @return 类型文件的扩展名未找到为{@code null} * @return 类型文件的扩展名提供的in为{@code null}未找到为{@code null}
* @throws IORuntimeException 读取流引起的异常 * @throws IORuntimeException 读取流引起的异常
*/ */
public static String getType(InputStream in, boolean isExact) throws IORuntimeException { public static String getType(InputStream in, boolean isExact) throws IORuntimeException {
if (null == in) {
return null;
}
return isExact return isExact
? getType(IoUtil.readHex8192Upper(in)) ? getType(IoUtil.readHex8192Upper(in))
: getType(IoUtil.readHex64Upper(in)); : getType(IoUtil.readHex64Upper(in));
@ -94,6 +98,7 @@ public class FileTypeUtil {
* 根据文件流的头部信息获得文件类型<br> * 根据文件流的头部信息获得文件类型<br>
* 注意此方法会读取头部64个bytes造成此流接下来读取时缺少部分bytes<br> * 注意此方法会读取头部64个bytes造成此流接下来读取时缺少部分bytes<br>
* 因此如果想复用此流流需支持{@link InputStream#reset()}方法 * 因此如果想复用此流流需支持{@link InputStream#reset()}方法
*
* @param in {@link InputStream} * @param in {@link InputStream}
* @return 类型文件的扩展名未找到为{@code null} * @return 类型文件的扩展名未找到为{@code null}
* @throws IORuntimeException 读取流引起的异常 * @throws IORuntimeException 读取流引起的异常
@ -132,6 +137,7 @@ public class FileTypeUtil {
* 2xlsdocmsi头信息无法区分按照扩展名区分 * 2xlsdocmsi头信息无法区分按照扩展名区分
* 3zip可能为docxxlsxpptxjarwarofd头信息无法区分按照扩展名区分 * 3zip可能为docxxlsxpptxjarwarofd头信息无法区分按照扩展名区分
* </pre> * </pre>
*
* @param in {@link InputStream} * @param in {@link InputStream}
* @param filename 文件名 * @param filename 文件名
* @param isExact 是否精确匹配如果为false使用前64个bytes匹配如果为true使用前8192bytes匹配 * @param isExact 是否精确匹配如果为false使用前64个bytes匹配如果为true使用前8192bytes匹配

View File

@ -1,8 +1,8 @@
package cn.hutool.core.lang; package cn.hutool.core.lang;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.lang.func.Func0; import cn.hutool.core.lang.func.Func0;
import cn.hutool.core.lang.func.VoidFunc0; import cn.hutool.core.lang.func.VoidFunc0;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil; import cn.hutool.core.util.StrUtil;
import java.util.Collection; import java.util.Collection;
@ -80,12 +80,12 @@ public class Opt<T> {
* *
* @param <T> 包裹里元素的类型 * @param <T> 包裹里元素的类型
* @param <R> 集合值类型 * @param <R> 集合值类型
* @param value 传入需要包裹的元素 * @param value 传入需要包裹的元素支持CharSequenceMapIterableIteratorArray类型
* @return 一个包裹里元素可能为空的 {@code Opt} * @return 一个包裹里元素可能为空的 {@code Opt}
* @since 5.7.17 * @since 5.7.17
*/ */
public static <T, R extends Collection<T>> Opt<R> ofEmptyAble(R value) { public static <T, R extends Collection<T>> Opt<R> ofEmptyAble(R value) {
return CollectionUtil.isEmpty(value) ? empty() : new Opt<>(value); return ObjectUtil.isEmpty(value) ? empty() : new Opt<>(value);
} }
/** /**

View File

@ -3,29 +3,18 @@ package cn.hutool.core.map;
import cn.hutool.core.collection.CollUtil; import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.convert.Convert; import cn.hutool.core.convert.Convert;
import cn.hutool.core.exceptions.UtilException; import cn.hutool.core.exceptions.UtilException;
import cn.hutool.core.lang.*; import cn.hutool.core.lang.Editor;
import cn.hutool.core.lang.Filter;
import cn.hutool.core.lang.Pair;
import cn.hutool.core.lang.TypeReference;
import cn.hutool.core.stream.CollectorUtil; import cn.hutool.core.stream.CollectorUtil;
import cn.hutool.core.util.ArrayUtil; import cn.hutool.core.util.ArrayUtil;
import cn.hutool.core.util.JdkUtil; import cn.hutool.core.util.JdkUtil;
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 java.util.AbstractMap; import java.util.*;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.Date;
import java.util.HashMap;
import java.util.IdentityHashMap;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry; import java.util.Map.Entry;
import java.util.NavigableMap;
import java.util.Set;
import java.util.SortedMap;
import java.util.TreeMap;
import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentHashMap;
import java.util.function.BiFunction; import java.util.function.BiFunction;
import java.util.function.Function; import java.util.function.Function;

View File

@ -10,13 +10,7 @@ import cn.hutool.core.lang.func.Func1;
import cn.hutool.core.text.finder.CharFinder; import cn.hutool.core.text.finder.CharFinder;
import cn.hutool.core.text.finder.Finder; import cn.hutool.core.text.finder.Finder;
import cn.hutool.core.text.finder.StrFinder; import cn.hutool.core.text.finder.StrFinder;
import cn.hutool.core.util.ArrayUtil; import cn.hutool.core.util.*;
import cn.hutool.core.util.CharUtil;
import cn.hutool.core.util.CharsetUtil;
import cn.hutool.core.util.DesensitizedUtil;
import cn.hutool.core.util.NumberUtil;
import cn.hutool.core.util.ReUtil;
import cn.hutool.core.util.StrUtil;
import java.nio.ByteBuffer; import java.nio.ByteBuffer;
import java.nio.charset.Charset; import java.nio.charset.Charset;
@ -65,10 +59,10 @@ public class CharSequenceUtil {
* *
* <p></p> * <p></p>
* <ul> * <ul>
* <li>{@code StrUtil.isBlank(null) // true}</li> * <li>{@code CharSequenceUtil.isBlank(null) // true}</li>
* <li>{@code StrUtil.isBlank("") // true}</li> * <li>{@code CharSequenceUtil.isBlank("") // true}</li>
* <li>{@code StrUtil.isBlank(" \t\n") // true}</li> * <li>{@code CharSequenceUtil.isBlank(" \t\n") // true}</li>
* <li>{@code StrUtil.isBlank("abc") // false}</li> * <li>{@code CharSequenceUtil.isBlank("abc") // false}</li>
* </ul> * </ul>
* *
* <p>注意该方法与 {@link #isEmpty(CharSequence)} 的区别是 * <p>注意该方法与 {@link #isEmpty(CharSequence)} 的区别是
@ -111,10 +105,10 @@ public class CharSequenceUtil {
* *
* <p></p> * <p></p>
* <ul> * <ul>
* <li>{@code StrUtil.isNotBlank(null) // false}</li> * <li>{@code CharSequenceUtil.isNotBlank(null) // false}</li>
* <li>{@code StrUtil.isNotBlank("") // false}</li> * <li>{@code CharSequenceUtil.isNotBlank("") // false}</li>
* <li>{@code StrUtil.isNotBlank(" \t\n") // false}</li> * <li>{@code CharSequenceUtil.isNotBlank(" \t\n") // false}</li>
* <li>{@code StrUtil.isNotBlank("abc") // true}</li> * <li>{@code CharSequenceUtil.isNotBlank("abc") // true}</li>
* </ul> * </ul>
* *
* <p>注意该方法与 {@link #isNotEmpty(CharSequence)} 的区别是 * <p>注意该方法与 {@link #isNotEmpty(CharSequence)} 的区别是
@ -136,10 +130,10 @@ public class CharSequenceUtil {
* *
* <p></p> * <p></p>
* <ul> * <ul>
* <li>{@code StrUtil.hasBlank() // true}</li> * <li>{@code CharSequenceUtil.hasBlank() // true}</li>
* <li>{@code StrUtil.hasBlank("", null, " ") // true}</li> * <li>{@code CharSequenceUtil.hasBlank("", null, " ") // true}</li>
* <li>{@code StrUtil.hasBlank("123", " ") // true}</li> * <li>{@code CharSequenceUtil.hasBlank("123", " ") // true}</li>
* <li>{@code StrUtil.hasBlank("123", "abc") // false}</li> * <li>{@code CharSequenceUtil.hasBlank("123", "abc") // false}</li>
* </ul> * </ul>
* *
* <p>注意该方法与 {@link #isAllBlank(CharSequence...)} 的区别在于</p> * <p>注意该方法与 {@link #isAllBlank(CharSequence...)} 的区别在于</p>
@ -171,10 +165,10 @@ public class CharSequenceUtil {
* *
* <p></p> * <p></p>
* <ul> * <ul>
* <li>{@code StrUtil.isAllBlank() // true}</li> * <li>{@code CharSequenceUtil.isAllBlank() // true}</li>
* <li>{@code StrUtil.isAllBlank("", null, " ") // true}</li> * <li>{@code CharSequenceUtil.isAllBlank("", null, " ") // true}</li>
* <li>{@code StrUtil.isAllBlank("123", " ") // false}</li> * <li>{@code CharSequenceUtil.isAllBlank("123", " ") // false}</li>
* <li>{@code StrUtil.isAllBlank("123", "abc") // false}</li> * <li>{@code CharSequenceUtil.isAllBlank("123", "abc") // false}</li>
* </ul> * </ul>
* *
* <p>注意该方法与 {@link #hasBlank(CharSequence...)} 的区别在于</p> * <p>注意该方法与 {@link #hasBlank(CharSequence...)} 的区别在于</p>
@ -208,10 +202,10 @@ public class CharSequenceUtil {
* *
* <p></p> * <p></p>
* <ul> * <ul>
* <li>{@code StrUtil.isEmpty(null) // true}</li> * <li>{@code CharSequenceUtil.isEmpty(null) // true}</li>
* <li>{@code StrUtil.isEmpty("") // true}</li> * <li>{@code CharSequenceUtil.isEmpty("") // true}</li>
* <li>{@code StrUtil.isEmpty(" \t\n") // false}</li> * <li>{@code CharSequenceUtil.isEmpty(" \t\n") // false}</li>
* <li>{@code StrUtil.isEmpty("abc") // false}</li> * <li>{@code CharSequenceUtil.isEmpty("abc") // false}</li>
* </ul> * </ul>
* *
* <p>注意该方法与 {@link #isBlank(CharSequence)} 的区别是该方法不校验空白字符</p> * <p>注意该方法与 {@link #isBlank(CharSequence)} 的区别是该方法不校验空白字符</p>
@ -238,10 +232,10 @@ public class CharSequenceUtil {
* *
* <p></p> * <p></p>
* <ul> * <ul>
* <li>{@code StrUtil.isNotEmpty(null) // false}</li> * <li>{@code CharSequenceUtil.isNotEmpty(null) // false}</li>
* <li>{@code StrUtil.isNotEmpty("") // false}</li> * <li>{@code CharSequenceUtil.isNotEmpty("") // false}</li>
* <li>{@code StrUtil.isNotEmpty(" \t\n") // true}</li> * <li>{@code CharSequenceUtil.isNotEmpty(" \t\n") // true}</li>
* <li>{@code StrUtil.isNotEmpty("abc") // true}</li> * <li>{@code CharSequenceUtil.isNotEmpty("abc") // true}</li>
* </ul> * </ul>
* *
* <p>注意该方法与 {@link #isNotBlank(CharSequence)} 的区别是该方法不校验空白字符</p> * <p>注意该方法与 {@link #isNotBlank(CharSequence)} 的区别是该方法不校验空白字符</p>
@ -350,11 +344,11 @@ public class CharSequenceUtil {
* *
* <p></p> * <p></p>
* <ul> * <ul>
* <li>{@code StrUtil.hasEmpty() // true}</li> * <li>{@code CharSequenceUtil.hasEmpty() // true}</li>
* <li>{@code StrUtil.hasEmpty("", null) // true}</li> * <li>{@code CharSequenceUtil.hasEmpty("", null) // true}</li>
* <li>{@code StrUtil.hasEmpty("123", "") // true}</li> * <li>{@code CharSequenceUtil.hasEmpty("123", "") // true}</li>
* <li>{@code StrUtil.hasEmpty("123", "abc") // false}</li> * <li>{@code CharSequenceUtil.hasEmpty("123", "abc") // false}</li>
* <li>{@code StrUtil.hasEmpty(" ", "\t", "\n") // false}</li> * <li>{@code CharSequenceUtil.hasEmpty(" ", "\t", "\n") // false}</li>
* </ul> * </ul>
* *
* <p>注意该方法与 {@link #isAllEmpty(CharSequence...)} 的区别在于</p> * <p>注意该方法与 {@link #isAllEmpty(CharSequence...)} 的区别在于</p>
@ -386,11 +380,11 @@ public class CharSequenceUtil {
* *
* <p></p> * <p></p>
* <ul> * <ul>
* <li>{@code StrUtil.isAllEmpty() // true}</li> * <li>{@code CharSequenceUtil.isAllEmpty() // true}</li>
* <li>{@code StrUtil.isAllEmpty("", null) // true}</li> * <li>{@code CharSequenceUtil.isAllEmpty("", null) // true}</li>
* <li>{@code StrUtil.isAllEmpty("123", "") // false}</li> * <li>{@code CharSequenceUtil.isAllEmpty("123", "") // false}</li>
* <li>{@code StrUtil.isAllEmpty("123", "abc") // false}</li> * <li>{@code CharSequenceUtil.isAllEmpty("123", "abc") // false}</li>
* <li>{@code StrUtil.isAllEmpty(" ", "\t", "\n") // false}</li> * <li>{@code CharSequenceUtil.isAllEmpty(" ", "\t", "\n") // false}</li>
* </ul> * </ul>
* *
* <p>注意该方法与 {@link #hasEmpty(CharSequence...)} 的区别在于</p> * <p>注意该方法与 {@link #hasEmpty(CharSequence...)} 的区别在于</p>
@ -422,11 +416,11 @@ public class CharSequenceUtil {
* *
* <p></p> * <p></p>
* <ul> * <ul>
* <li>{@code StrUtil.isAllNotEmpty() // false}</li> * <li>{@code CharSequenceUtil.isAllNotEmpty() // false}</li>
* <li>{@code StrUtil.isAllNotEmpty("", null) // false}</li> * <li>{@code CharSequenceUtil.isAllNotEmpty("", null) // false}</li>
* <li>{@code StrUtil.isAllNotEmpty("123", "") // false}</li> * <li>{@code CharSequenceUtil.isAllNotEmpty("123", "") // false}</li>
* <li>{@code StrUtil.isAllNotEmpty("123", "abc") // true}</li> * <li>{@code CharSequenceUtil.isAllNotEmpty("123", "abc") // true}</li>
* <li>{@code StrUtil.isAllNotEmpty(" ", "\t", "\n") // true}</li> * <li>{@code CharSequenceUtil.isAllNotEmpty(" ", "\t", "\n") // true}</li>
* </ul> * </ul>
* *
* <p>注意该方法与 {@link #isAllEmpty(CharSequence...)} 的区别在于</p> * <p>注意该方法与 {@link #isAllEmpty(CharSequence...)} 的区别在于</p>
@ -534,11 +528,11 @@ public class CharSequenceUtil {
* 除去字符串头尾部的空白如果字符串是{@code null}返回{@code ""} * 除去字符串头尾部的空白如果字符串是{@code null}返回{@code ""}
* *
* <pre> * <pre>
* StrUtil.trimToEmpty(null) = "" * CharSequenceUtil.trimToEmpty(null) = ""
* StrUtil.trimToEmpty("") = "" * CharSequenceUtil.trimToEmpty("") = ""
* StrUtil.trimToEmpty(" ") = "" * CharSequenceUtil.trimToEmpty(" ") = ""
* StrUtil.trimToEmpty("abc") = "abc" * CharSequenceUtil.trimToEmpty("abc") = "abc"
* StrUtil.trimToEmpty(" abc ") = "abc" * CharSequenceUtil.trimToEmpty(" abc ") = "abc"
* </pre> * </pre>
* *
* @param str 字符串 * @param str 字符串
@ -553,11 +547,11 @@ public class CharSequenceUtil {
* 除去字符串头尾部的空白如果字符串是{@code null}或者""返回{@code null} * 除去字符串头尾部的空白如果字符串是{@code null}或者""返回{@code null}
* *
* <pre> * <pre>
* StrUtil.trimToNull(null) = null * CharSequenceUtil.trimToNull(null) = null
* StrUtil.trimToNull("") = null * CharSequenceUtil.trimToNull("") = null
* StrUtil.trimToNull(" ") = null * CharSequenceUtil.trimToNull(" ") = null
* StrUtil.trimToNull("abc") = "abc" * CharSequenceUtil.trimToNull("abc") = "abc"
* StrUtil.trimToEmpty(" abc ") = "abc" * CharSequenceUtil.trimToEmpty(" abc ") = "abc"
* </pre> * </pre>
* *
* @param str 字符串 * @param str 字符串
@ -1169,17 +1163,17 @@ public class CharSequenceUtil {
* 指定范围内查找字符串忽略大小写<br> * 指定范围内查找字符串忽略大小写<br>
* *
* <pre> * <pre>
* StrUtil.indexOfIgnoreCase(null, *, *) = -1 * CharSequenceUtil.indexOfIgnoreCase(null, *, *) = -1
* StrUtil.indexOfIgnoreCase(*, null, *) = -1 * CharSequenceUtil.indexOfIgnoreCase(*, null, *) = -1
* StrUtil.indexOfIgnoreCase("", "", 0) = 0 * CharSequenceUtil.indexOfIgnoreCase("", "", 0) = 0
* StrUtil.indexOfIgnoreCase("aabaabaa", "A", 0) = 0 * CharSequenceUtil.indexOfIgnoreCase("aabaabaa", "A", 0) = 0
* StrUtil.indexOfIgnoreCase("aabaabaa", "B", 0) = 2 * CharSequenceUtil.indexOfIgnoreCase("aabaabaa", "B", 0) = 2
* StrUtil.indexOfIgnoreCase("aabaabaa", "AB", 0) = 1 * CharSequenceUtil.indexOfIgnoreCase("aabaabaa", "AB", 0) = 1
* StrUtil.indexOfIgnoreCase("aabaabaa", "B", 3) = 5 * CharSequenceUtil.indexOfIgnoreCase("aabaabaa", "B", 3) = 5
* StrUtil.indexOfIgnoreCase("aabaabaa", "B", 9) = -1 * CharSequenceUtil.indexOfIgnoreCase("aabaabaa", "B", 9) = -1
* StrUtil.indexOfIgnoreCase("aabaabaa", "B", -1) = 2 * CharSequenceUtil.indexOfIgnoreCase("aabaabaa", "B", -1) = 2
* StrUtil.indexOfIgnoreCase("aabaabaa", "", 2) = 2 * CharSequenceUtil.indexOfIgnoreCase("aabaabaa", "", 2) = 2
* StrUtil.indexOfIgnoreCase("abc", "", 9) = -1 * CharSequenceUtil.indexOfIgnoreCase("abc", "", 9) = -1
* </pre> * </pre>
* *
* @param str 字符串 * @param str 字符串
@ -1195,17 +1189,17 @@ public class CharSequenceUtil {
* 指定范围内查找字符串 * 指定范围内查找字符串
* *
* <pre> * <pre>
* StrUtil.indexOfIgnoreCase(null, *, *) = -1 * CharSequenceUtil.indexOfIgnoreCase(null, *, *) = -1
* StrUtil.indexOfIgnoreCase(*, null, *) = -1 * CharSequenceUtil.indexOfIgnoreCase(*, null, *) = -1
* StrUtil.indexOfIgnoreCase("", "", 0) = 0 * CharSequenceUtil.indexOfIgnoreCase("", "", 0) = 0
* StrUtil.indexOfIgnoreCase("aabaabaa", "A", 0) = 0 * CharSequenceUtil.indexOfIgnoreCase("aabaabaa", "A", 0) = 0
* StrUtil.indexOfIgnoreCase("aabaabaa", "B", 0) = 2 * CharSequenceUtil.indexOfIgnoreCase("aabaabaa", "B", 0) = 2
* StrUtil.indexOfIgnoreCase("aabaabaa", "AB", 0) = 1 * CharSequenceUtil.indexOfIgnoreCase("aabaabaa", "AB", 0) = 1
* StrUtil.indexOfIgnoreCase("aabaabaa", "B", 3) = 5 * CharSequenceUtil.indexOfIgnoreCase("aabaabaa", "B", 3) = 5
* StrUtil.indexOfIgnoreCase("aabaabaa", "B", 9) = -1 * CharSequenceUtil.indexOfIgnoreCase("aabaabaa", "B", 9) = -1
* StrUtil.indexOfIgnoreCase("aabaabaa", "B", -1) = 2 * CharSequenceUtil.indexOfIgnoreCase("aabaabaa", "B", -1) = 2
* StrUtil.indexOfIgnoreCase("aabaabaa", "", 2) = 2 * CharSequenceUtil.indexOfIgnoreCase("aabaabaa", "", 2) = 2
* StrUtil.indexOfIgnoreCase("abc", "", 9) = -1 * CharSequenceUtil.indexOfIgnoreCase("abc", "", 9) = -1
* </pre> * </pre>
* *
* @param str 字符串 * @param str 字符串
@ -1230,7 +1224,7 @@ public class CharSequenceUtil {
*/ */
public static int indexOf(CharSequence text, CharSequence searchStr, int from, boolean ignoreCase) { public static int indexOf(CharSequence text, CharSequence searchStr, int from, boolean ignoreCase) {
if (isEmpty(text) || isEmpty(searchStr)) { if (isEmpty(text) || isEmpty(searchStr)) {
if (StrUtil.equals(text, searchStr)) { if (CharSequenceUtil.equals(text, searchStr)) {
return 0; return 0;
} else { } else {
return INDEX_NOT_FOUND; return INDEX_NOT_FOUND;
@ -1278,7 +1272,7 @@ public class CharSequenceUtil {
*/ */
public static int lastIndexOf(CharSequence text, CharSequence searchStr, int from, boolean ignoreCase) { public static int lastIndexOf(CharSequence text, CharSequence searchStr, int from, boolean ignoreCase) {
if (isEmpty(text) || isEmpty(searchStr)) { if (isEmpty(text) || isEmpty(searchStr)) {
if (StrUtil.equals(text, searchStr)) { if (CharSequenceUtil.equals(text, searchStr)) {
return 0; return 0;
} else { } else {
return INDEX_NOT_FOUND; return INDEX_NOT_FOUND;
@ -1298,17 +1292,17 @@ public class CharSequenceUtil {
* 例子*代表任意字符 * 例子*代表任意字符
* *
* <pre> * <pre>
* StrUtil.ordinalIndexOf(null, *, *) = -1 * CharSequenceUtil.ordinalIndexOf(null, *, *) = -1
* StrUtil.ordinalIndexOf(*, null, *) = -1 * CharSequenceUtil.ordinalIndexOf(*, null, *) = -1
* StrUtil.ordinalIndexOf("", "", *) = 0 * CharSequenceUtil.ordinalIndexOf("", "", *) = 0
* StrUtil.ordinalIndexOf("aabaabaa", "a", 1) = 0 * CharSequenceUtil.ordinalIndexOf("aabaabaa", "a", 1) = 0
* StrUtil.ordinalIndexOf("aabaabaa", "a", 2) = 1 * CharSequenceUtil.ordinalIndexOf("aabaabaa", "a", 2) = 1
* StrUtil.ordinalIndexOf("aabaabaa", "b", 1) = 2 * CharSequenceUtil.ordinalIndexOf("aabaabaa", "b", 1) = 2
* StrUtil.ordinalIndexOf("aabaabaa", "b", 2) = 5 * CharSequenceUtil.ordinalIndexOf("aabaabaa", "b", 2) = 5
* StrUtil.ordinalIndexOf("aabaabaa", "ab", 1) = 1 * CharSequenceUtil.ordinalIndexOf("aabaabaa", "ab", 1) = 1
* StrUtil.ordinalIndexOf("aabaabaa", "ab", 2) = 4 * CharSequenceUtil.ordinalIndexOf("aabaabaa", "ab", 2) = 4
* StrUtil.ordinalIndexOf("aabaabaa", "", 1) = 0 * CharSequenceUtil.ordinalIndexOf("aabaabaa", "", 1) = 0
* StrUtil.ordinalIndexOf("aabaabaa", "", 2) = 0 * CharSequenceUtil.ordinalIndexOf("aabaabaa", "", 2) = 0
* </pre> * </pre>
* *
* @param str 被检查的字符串可以为null * @param str 被检查的字符串可以为null
@ -2106,13 +2100,13 @@ public class CharSequenceUtil {
* 切割指定长度的后部分的字符串 * 切割指定长度的后部分的字符串
* *
* <pre> * <pre>
* StrUtil.subSufByLength("abcde", 3) = "cde" * CharSequenceUtil.subSufByLength("abcde", 3) = "cde"
* StrUtil.subSufByLength("abcde", 0) = "" * CharSequenceUtil.subSufByLength("abcde", 0) = ""
* StrUtil.subSufByLength("abcde", -5) = "" * CharSequenceUtil.subSufByLength("abcde", -5) = ""
* StrUtil.subSufByLength("abcde", -1) = "" * CharSequenceUtil.subSufByLength("abcde", -1) = ""
* StrUtil.subSufByLength("abcde", 5) = "abcde" * CharSequenceUtil.subSufByLength("abcde", 5) = "abcde"
* StrUtil.subSufByLength("abcde", 10) = "abcde" * CharSequenceUtil.subSufByLength("abcde", 10) = "abcde"
* StrUtil.subSufByLength(null, 3) = null * CharSequenceUtil.subSufByLength(null, 3) = null
* </pre> * </pre>
* *
* @param string 字符串 * @param string 字符串
@ -2156,14 +2150,14 @@ public class CharSequenceUtil {
* 如果分隔字符串为空串""则返回空串如果分隔字符串未找到返回原字符串举例如下 * 如果分隔字符串为空串""则返回空串如果分隔字符串未找到返回原字符串举例如下
* *
* <pre> * <pre>
* StrUtil.subBefore(null, *, false) = null * CharSequenceUtil.subBefore(null, *, false) = null
* StrUtil.subBefore("", *, false) = "" * CharSequenceUtil.subBefore("", *, false) = ""
* StrUtil.subBefore("abc", "a", false) = "" * CharSequenceUtil.subBefore("abc", "a", false) = ""
* StrUtil.subBefore("abcba", "b", false) = "a" * CharSequenceUtil.subBefore("abcba", "b", false) = "a"
* StrUtil.subBefore("abc", "c", false) = "ab" * CharSequenceUtil.subBefore("abc", "c", false) = "ab"
* StrUtil.subBefore("abc", "d", false) = "abc" * CharSequenceUtil.subBefore("abc", "d", false) = "abc"
* StrUtil.subBefore("abc", "", false) = "" * CharSequenceUtil.subBefore("abc", "", false) = ""
* StrUtil.subBefore("abc", null, false) = "abc" * CharSequenceUtil.subBefore("abc", null, false) = "abc"
* </pre> * </pre>
* *
* @param string 被查找的字符串 * @param string 被查找的字符串
@ -2198,12 +2192,12 @@ public class CharSequenceUtil {
* 如果分隔字符串未找到返回原字符串举例如下 * 如果分隔字符串未找到返回原字符串举例如下
* *
* <pre> * <pre>
* StrUtil.subBefore(null, *, false) = null * CharSequenceUtil.subBefore(null, *, false) = null
* StrUtil.subBefore("", *, false) = "" * CharSequenceUtil.subBefore("", *, false) = ""
* StrUtil.subBefore("abc", 'a', false) = "" * CharSequenceUtil.subBefore("abc", 'a', false) = ""
* StrUtil.subBefore("abcba", 'b', false) = "a" * CharSequenceUtil.subBefore("abcba", 'b', false) = "a"
* StrUtil.subBefore("abc", 'c', false) = "ab" * CharSequenceUtil.subBefore("abc", 'c', false) = "ab"
* StrUtil.subBefore("abc", 'd', false) = "abc" * CharSequenceUtil.subBefore("abc", 'd', false) = "abc"
* </pre> * </pre>
* *
* @param string 被查找的字符串 * @param string 被查找的字符串
@ -2234,14 +2228,14 @@ public class CharSequenceUtil {
* 如果分隔字符串为空串null或""则返回空串如果分隔字符串未找到返回空串举例如下 * 如果分隔字符串为空串null或""则返回空串如果分隔字符串未找到返回空串举例如下
* *
* <pre> * <pre>
* StrUtil.subAfter(null, *, false) = null * CharSequenceUtil.subAfter(null, *, false) = null
* StrUtil.subAfter("", *, false) = "" * CharSequenceUtil.subAfter("", *, false) = ""
* StrUtil.subAfter(*, null, false) = "" * CharSequenceUtil.subAfter(*, null, false) = ""
* StrUtil.subAfter("abc", "a", false) = "bc" * CharSequenceUtil.subAfter("abc", "a", false) = "bc"
* StrUtil.subAfter("abcba", "b", false) = "cba" * CharSequenceUtil.subAfter("abcba", "b", false) = "cba"
* StrUtil.subAfter("abc", "c", false) = "" * CharSequenceUtil.subAfter("abc", "c", false) = ""
* StrUtil.subAfter("abc", "d", false) = "" * CharSequenceUtil.subAfter("abc", "d", false) = ""
* StrUtil.subAfter("abc", "", false) = "abc" * CharSequenceUtil.subAfter("abc", "", false) = "abc"
* </pre> * </pre>
* *
* @param string 被查找的字符串 * @param string 被查找的字符串
@ -2272,12 +2266,12 @@ public class CharSequenceUtil {
* 如果分隔字符串为空串null或""则返回空串如果分隔字符串未找到返回空串举例如下 * 如果分隔字符串为空串null或""则返回空串如果分隔字符串未找到返回空串举例如下
* *
* <pre> * <pre>
* StrUtil.subAfter(null, *, false) = null * CharSequenceUtil.subAfter(null, *, false) = null
* StrUtil.subAfter("", *, false) = "" * CharSequenceUtil.subAfter("", *, false) = ""
* StrUtil.subAfter("abc", 'a', false) = "bc" * CharSequenceUtil.subAfter("abc", 'a', false) = "bc"
* StrUtil.subAfter("abcba", 'b', false) = "cba" * CharSequenceUtil.subAfter("abcba", 'b', false) = "cba"
* StrUtil.subAfter("abc", 'c', false) = "" * CharSequenceUtil.subAfter("abc", 'c', false) = ""
* StrUtil.subAfter("abc", 'd', false) = "" * CharSequenceUtil.subAfter("abc", 'd', false) = ""
* </pre> * </pre>
* *
* @param string 被查找的字符串 * @param string 被查找的字符串
@ -2304,16 +2298,16 @@ public class CharSequenceUtil {
* 栗子 * 栗子
* *
* <pre> * <pre>
* StrUtil.subBetween("wx[b]yz", "[", "]") = "b" * CharSequenceUtil.subBetween("wx[b]yz", "[", "]") = "b"
* StrUtil.subBetween(null, *, *) = null * CharSequenceUtil.subBetween(null, *, *) = null
* StrUtil.subBetween(*, null, *) = null * CharSequenceUtil.subBetween(*, null, *) = null
* StrUtil.subBetween(*, *, null) = null * CharSequenceUtil.subBetween(*, *, null) = null
* StrUtil.subBetween("", "", "") = "" * CharSequenceUtil.subBetween("", "", "") = ""
* StrUtil.subBetween("", "", "]") = null * CharSequenceUtil.subBetween("", "", "]") = null
* StrUtil.subBetween("", "[", "]") = null * CharSequenceUtil.subBetween("", "[", "]") = null
* StrUtil.subBetween("yabcz", "", "") = "" * CharSequenceUtil.subBetween("yabcz", "", "") = ""
* StrUtil.subBetween("yabcz", "y", "z") = "abc" * CharSequenceUtil.subBetween("yabcz", "y", "z") = "abc"
* StrUtil.subBetween("yabczyabcz", "y", "z") = "abc" * CharSequenceUtil.subBetween("yabczyabcz", "y", "z") = "abc"
* </pre> * </pre>
* *
* @param str 被切割的字符串 * @param str 被切割的字符串
@ -2347,12 +2341,12 @@ public class CharSequenceUtil {
* 栗子 * 栗子
* *
* <pre> * <pre>
* StrUtil.subBetween(null, *) = null * CharSequenceUtil.subBetween(null, *) = null
* StrUtil.subBetween("", "") = "" * CharSequenceUtil.subBetween("", "") = ""
* StrUtil.subBetween("", "tag") = null * CharSequenceUtil.subBetween("", "tag") = null
* StrUtil.subBetween("tagabctag", null) = null * CharSequenceUtil.subBetween("tagabctag", null) = null
* StrUtil.subBetween("tagabctag", "") = "" * CharSequenceUtil.subBetween("tagabctag", "") = ""
* StrUtil.subBetween("tagabctag", "tag") = "abc" * CharSequenceUtil.subBetween("tagabctag", "tag") = "abc"
* </pre> * </pre>
* *
* @param str 被切割的字符串 * @param str 被切割的字符串
@ -2370,17 +2364,17 @@ public class CharSequenceUtil {
* 栗子 * 栗子
* *
* <pre> * <pre>
* StrUtil.subBetweenAll("wx[b]y[z]", "[", "]") = ["b","z"] * CharSequenceUtil.subBetweenAll("wx[b]y[z]", "[", "]") = ["b","z"]
* StrUtil.subBetweenAll(null, *, *) = [] * CharSequenceUtil.subBetweenAll(null, *, *) = []
* StrUtil.subBetweenAll(*, null, *) = [] * CharSequenceUtil.subBetweenAll(*, null, *) = []
* StrUtil.subBetweenAll(*, *, null) = [] * CharSequenceUtil.subBetweenAll(*, *, null) = []
* StrUtil.subBetweenAll("", "", "") = [] * CharSequenceUtil.subBetweenAll("", "", "") = []
* StrUtil.subBetweenAll("", "", "]") = [] * CharSequenceUtil.subBetweenAll("", "", "]") = []
* StrUtil.subBetweenAll("", "[", "]") = [] * CharSequenceUtil.subBetweenAll("", "[", "]") = []
* StrUtil.subBetweenAll("yabcz", "", "") = [] * CharSequenceUtil.subBetweenAll("yabcz", "", "") = []
* StrUtil.subBetweenAll("yabcz", "y", "z") = ["abc"] * CharSequenceUtil.subBetweenAll("yabcz", "y", "z") = ["abc"]
* StrUtil.subBetweenAll("yabczyabcz", "y", "z") = ["abc","abc"] * CharSequenceUtil.subBetweenAll("yabczyabcz", "y", "z") = ["abc","abc"]
* StrUtil.subBetweenAll("[yabc[zy]abcz]", "[", "]"); = ["zy"] 重叠时只截取内部 * CharSequenceUtil.subBetweenAll("[yabc[zy]abcz]", "[", "]"); = ["zy"] 重叠时只截取内部
* </pre> * </pre>
* *
* @param str 被切割的字符串 * @param str 被切割的字符串
@ -2425,15 +2419,15 @@ public class CharSequenceUtil {
* 栗子 * 栗子
* *
* <pre> * <pre>
* StrUtil.subBetweenAll(null, *) = [] * CharSequenceUtil.subBetweenAll(null, *) = []
* StrUtil.subBetweenAll(*, null) = [] * CharSequenceUtil.subBetweenAll(*, null) = []
* StrUtil.subBetweenAll(*, *) = [] * CharSequenceUtil.subBetweenAll(*, *) = []
* StrUtil.subBetweenAll("", "") = [] * CharSequenceUtil.subBetweenAll("", "") = []
* StrUtil.subBetweenAll("", "#") = [] * CharSequenceUtil.subBetweenAll("", "#") = []
* StrUtil.subBetweenAll("gotanks", "") = [] * CharSequenceUtil.subBetweenAll("gotanks", "") = []
* StrUtil.subBetweenAll("#gotanks#", "#") = ["gotanks"] * CharSequenceUtil.subBetweenAll("#gotanks#", "#") = ["gotanks"]
* StrUtil.subBetweenAll("#hello# #world#!", "#") = ["hello", "world"] * CharSequenceUtil.subBetweenAll("#hello# #world#!", "#") = ["hello", "world"]
* StrUtil.subBetweenAll("#hello# world#!", "#"); = ["hello"] * CharSequenceUtil.subBetweenAll("#hello# world#!", "#"); = ["hello"]
* </pre> * </pre>
* *
* @param str 被切割的字符串 * @param str 被切割的字符串
@ -2452,9 +2446,9 @@ public class CharSequenceUtil {
* 重复某个字符 * 重复某个字符
* *
* <pre> * <pre>
* StrUtil.repeat('e', 0) = "" * CharSequenceUtil.repeat('e', 0) = ""
* StrUtil.repeat('e', 3) = "eee" * CharSequenceUtil.repeat('e', 3) = "eee"
* StrUtil.repeat('e', -2) = "" * CharSequenceUtil.repeat('e', -2) = ""
* </pre> * </pre>
* *
* @param c 被重复的字符 * @param c 被重复的字符
@ -2522,7 +2516,7 @@ public class CharSequenceUtil {
return null; return null;
} }
if (padLen <= 0) { if (padLen <= 0) {
return StrUtil.EMPTY; return CharSequenceUtil.EMPTY;
} }
final int strLen = str.length(); final int strLen = str.length();
if (strLen == padLen) { if (strLen == padLen) {
@ -2543,9 +2537,9 @@ public class CharSequenceUtil {
* 重复某个字符串并通过分界符连接 * 重复某个字符串并通过分界符连接
* *
* <pre> * <pre>
* StrUtil.repeatAndJoin("?", 5, ",") = "?,?,?,?,?" * CharSequenceUtil.repeatAndJoin("?", 5, ",") = "?,?,?,?,?"
* StrUtil.repeatAndJoin("?", 0, ",") = "" * CharSequenceUtil.repeatAndJoin("?", 0, ",") = ""
* StrUtil.repeatAndJoin("?", 5, null) = "?????" * CharSequenceUtil.repeatAndJoin("?", 5, null) = "?????"
* </pre> * </pre>
* *
* @param str 被重复的字符串 * @param str 被重复的字符串
@ -3071,10 +3065,10 @@ public class CharSequenceUtil {
* leftPad (org.apache.commons.lang3.leftPad) * leftPad (org.apache.commons.lang3.leftPad)
* *
* <pre> * <pre>
* StrUtil.padPre(null, *, *);//null * CharSequenceUtil.padPre(null, *, *);//null
* StrUtil.padPre("1", 3, "ABC");//"AB1" * CharSequenceUtil.padPre("1", 3, "ABC");//"AB1"
* StrUtil.padPre("123", 2, "ABC");//"12" * CharSequenceUtil.padPre("123", 2, "ABC");//"12"
* StrUtil.padPre("1039", -1, "0");//"103" * CharSequenceUtil.padPre("1039", -1, "0");//"103"
* </pre> * </pre>
* *
* @param str 字符串 * @param str 字符串
@ -3102,9 +3096,9 @@ public class CharSequenceUtil {
* leftPad (org.apache.commons.lang3.leftPad) * leftPad (org.apache.commons.lang3.leftPad)
* *
* <pre> * <pre>
* StrUtil.padPre(null, *, *);//null * CharSequenceUtil.padPre(null, *, *);//null
* StrUtil.padPre("1", 3, '0');//"001" * CharSequenceUtil.padPre("1", 3, '0');//"001"
* StrUtil.padPre("123", 2, '0');//"12" * CharSequenceUtil.padPre("123", 2, '0');//"12"
* </pre> * </pre>
* *
* @param str 字符串 * @param str 字符串
@ -3131,10 +3125,10 @@ public class CharSequenceUtil {
* 补充字符串以满足最小长度如果提供的字符串大于指定长度截断之 * 补充字符串以满足最小长度如果提供的字符串大于指定长度截断之
* *
* <pre> * <pre>
* StrUtil.padAfter(null, *, *);//null * CharSequenceUtil.padAfter(null, *, *);//null
* StrUtil.padAfter("1", 3, '0');//"100" * CharSequenceUtil.padAfter("1", 3, '0');//"100"
* StrUtil.padAfter("123", 2, '0');//"23" * CharSequenceUtil.padAfter("123", 2, '0');//"23"
* StrUtil.padAfter("123", -1, '0')//"" 空串 * CharSequenceUtil.padAfter("123", -1, '0')//"" 空串
* </pre> * </pre>
* *
* @param str 字符串如果为{@code null}直接返回null * @param str 字符串如果为{@code null}直接返回null
@ -3161,9 +3155,9 @@ public class CharSequenceUtil {
* 补充字符串以满足最小长度 * 补充字符串以满足最小长度
* *
* <pre> * <pre>
* StrUtil.padAfter(null, *, *);//null * CharSequenceUtil.padAfter(null, *, *);//null
* StrUtil.padAfter("1", 3, "ABC");//"1AB" * CharSequenceUtil.padAfter("1", 3, "ABC");//"1AB"
* StrUtil.padAfter("123", 2, "ABC");//"23" * CharSequenceUtil.padAfter("123", 2, "ABC");//"23"
* </pre> * </pre>
* *
* @param str 字符串如果为{@code null}直接返回null * @param str 字符串如果为{@code null}直接返回null
@ -3193,12 +3187,12 @@ public class CharSequenceUtil {
* 居中字符串两边补充指定字符串如果指定长度小于字符串则返回原字符串 * 居中字符串两边补充指定字符串如果指定长度小于字符串则返回原字符串
* *
* <pre> * <pre>
* StrUtil.center(null, *) = null * CharSequenceUtil.center(null, *) = null
* StrUtil.center("", 4) = " " * CharSequenceUtil.center("", 4) = " "
* StrUtil.center("ab", -1) = "ab" * CharSequenceUtil.center("ab", -1) = "ab"
* StrUtil.center("ab", 4) = " ab " * CharSequenceUtil.center("ab", 4) = " ab "
* StrUtil.center("abcd", 2) = "abcd" * CharSequenceUtil.center("abcd", 2) = "abcd"
* StrUtil.center("a", 4) = " a " * CharSequenceUtil.center("a", 4) = " a "
* </pre> * </pre>
* *
* @param str 字符串 * @param str 字符串
@ -3214,14 +3208,14 @@ public class CharSequenceUtil {
* 居中字符串两边补充指定字符串如果指定长度小于字符串则返回原字符串 * 居中字符串两边补充指定字符串如果指定长度小于字符串则返回原字符串
* *
* <pre> * <pre>
* StrUtil.center(null, *, *) = null * CharSequenceUtil.center(null, *, *) = null
* StrUtil.center("", 4, ' ') = " " * CharSequenceUtil.center("", 4, ' ') = " "
* StrUtil.center("ab", -1, ' ') = "ab" * CharSequenceUtil.center("ab", -1, ' ') = "ab"
* StrUtil.center("ab", 4, ' ') = " ab " * CharSequenceUtil.center("ab", 4, ' ') = " ab "
* StrUtil.center("abcd", 2, ' ') = "abcd" * CharSequenceUtil.center("abcd", 2, ' ') = "abcd"
* StrUtil.center("a", 4, ' ') = " a " * CharSequenceUtil.center("a", 4, ' ') = " a "
* StrUtil.center("a", 4, 'y') = "yayy" * CharSequenceUtil.center("a", 4, 'y') = "yayy"
* StrUtil.center("abc", 7, ' ') = " abc " * CharSequenceUtil.center("abc", 7, ' ') = " abc "
* </pre> * </pre>
* *
* @param str 字符串 * @param str 字符串
@ -3248,15 +3242,15 @@ public class CharSequenceUtil {
* 居中字符串两边补充指定字符串如果指定长度小于字符串则返回原字符串 * 居中字符串两边补充指定字符串如果指定长度小于字符串则返回原字符串
* *
* <pre> * <pre>
* StrUtil.center(null, *, *) = null * CharSequenceUtil.center(null, *, *) = null
* StrUtil.center("", 4, " ") = " " * CharSequenceUtil.center("", 4, " ") = " "
* StrUtil.center("ab", -1, " ") = "ab" * CharSequenceUtil.center("ab", -1, " ") = "ab"
* StrUtil.center("ab", 4, " ") = " ab " * CharSequenceUtil.center("ab", 4, " ") = " ab "
* StrUtil.center("abcd", 2, " ") = "abcd" * CharSequenceUtil.center("abcd", 2, " ") = "abcd"
* StrUtil.center("a", 4, " ") = " a " * CharSequenceUtil.center("a", 4, " ") = " a "
* StrUtil.center("a", 4, "yz") = "yayz" * CharSequenceUtil.center("a", 4, "yz") = "yayz"
* StrUtil.center("abc", 7, null) = " abc " * CharSequenceUtil.center("abc", 7, null) = " abc "
* StrUtil.center("abc", 7, "") = " abc " * CharSequenceUtil.center("abc", 7, "") = " abc "
* </pre> * </pre>
* *
* @param str 字符串 * @param str 字符串
@ -3300,13 +3294,13 @@ public class CharSequenceUtil {
* 参数为 {@code null} 或者 "" 返回 {@code 0}. * 参数为 {@code null} 或者 "" 返回 {@code 0}.
* *
* <pre> * <pre>
* StrUtil.count(null, *) = 0 * CharSequenceUtil.count(null, *) = 0
* StrUtil.count("", *) = 0 * CharSequenceUtil.count("", *) = 0
* StrUtil.count("abba", null) = 0 * CharSequenceUtil.count("abba", null) = 0
* StrUtil.count("abba", "") = 0 * CharSequenceUtil.count("abba", "") = 0
* StrUtil.count("abba", "a") = 2 * CharSequenceUtil.count("abba", "a") = 2
* StrUtil.count("abba", "ab") = 1 * CharSequenceUtil.count("abba", "ab") = 1
* StrUtil.count("abba", "xxx") = 0 * CharSequenceUtil.count("abba", "xxx") = 0
* </pre> * </pre>
* *
* @param content 被查找的字符串 * @param content 被查找的字符串
@ -3356,16 +3350,16 @@ public class CharSequenceUtil {
* 比较两个字符串用于排序 * 比较两个字符串用于排序
* *
* <pre> * <pre>
* StrUtil.compare(null, null, *) = 0 * CharSequenceUtil.compare(null, null, *) = 0
* StrUtil.compare(null , "a", true) &lt; 0 * CharSequenceUtil.compare(null , "a", true) &lt; 0
* StrUtil.compare(null , "a", false) &gt; 0 * CharSequenceUtil.compare(null , "a", false) &gt; 0
* StrUtil.compare("a", null, true) &gt; 0 * CharSequenceUtil.compare("a", null, true) &gt; 0
* StrUtil.compare("a", null, false) &lt; 0 * CharSequenceUtil.compare("a", null, false) &lt; 0
* StrUtil.compare("abc", "abc", *) = 0 * CharSequenceUtil.compare("abc", "abc", *) = 0
* StrUtil.compare("a", "b", *) &lt; 0 * CharSequenceUtil.compare("a", "b", *) &lt; 0
* StrUtil.compare("b", "a", *) &gt; 0 * CharSequenceUtil.compare("b", "a", *) &gt; 0
* StrUtil.compare("a", "B", *) &gt; 0 * CharSequenceUtil.compare("a", "B", *) &gt; 0
* StrUtil.compare("ab", "abc", *) &lt; 0 * CharSequenceUtil.compare("ab", "abc", *) &lt; 0
* </pre> * </pre>
* *
* @param str1 字符串1 * @param str1 字符串1
@ -3390,18 +3384,18 @@ public class CharSequenceUtil {
* 比较两个字符串用于排序大小写不敏感 * 比较两个字符串用于排序大小写不敏感
* *
* <pre> * <pre>
* StrUtil.compareIgnoreCase(null, null, *) = 0 * CharSequenceUtil.compareIgnoreCase(null, null, *) = 0
* StrUtil.compareIgnoreCase(null , "a", true) &lt; 0 * CharSequenceUtil.compareIgnoreCase(null , "a", true) &lt; 0
* StrUtil.compareIgnoreCase(null , "a", false) &gt; 0 * CharSequenceUtil.compareIgnoreCase(null , "a", false) &gt; 0
* StrUtil.compareIgnoreCase("a", null, true) &gt; 0 * CharSequenceUtil.compareIgnoreCase("a", null, true) &gt; 0
* StrUtil.compareIgnoreCase("a", null, false) &lt; 0 * CharSequenceUtil.compareIgnoreCase("a", null, false) &lt; 0
* StrUtil.compareIgnoreCase("abc", "abc", *) = 0 * CharSequenceUtil.compareIgnoreCase("abc", "abc", *) = 0
* StrUtil.compareIgnoreCase("abc", "ABC", *) = 0 * CharSequenceUtil.compareIgnoreCase("abc", "ABC", *) = 0
* StrUtil.compareIgnoreCase("a", "b", *) &lt; 0 * CharSequenceUtil.compareIgnoreCase("a", "b", *) &lt; 0
* StrUtil.compareIgnoreCase("b", "a", *) &gt; 0 * CharSequenceUtil.compareIgnoreCase("b", "a", *) &gt; 0
* StrUtil.compareIgnoreCase("a", "B", *) &lt; 0 * CharSequenceUtil.compareIgnoreCase("a", "B", *) &lt; 0
* StrUtil.compareIgnoreCase("A", "b", *) &lt; 0 * CharSequenceUtil.compareIgnoreCase("A", "b", *) &lt; 0
* StrUtil.compareIgnoreCase("ab", "abc", *) &lt; 0 * CharSequenceUtil.compareIgnoreCase("ab", "abc", *) &lt; 0
* </pre> * </pre>
* *
* @param str1 字符串1 * @param str1 字符串1
@ -3427,14 +3421,14 @@ public class CharSequenceUtil {
* null版本排在最小 * null版本排在最小
* *
* <pre> * <pre>
* StrUtil.compareVersion(null, "v1") &lt; 0 * CharSequenceUtil.compareVersion(null, "v1") &lt; 0
* StrUtil.compareVersion("v1", "v1") = 0 * CharSequenceUtil.compareVersion("v1", "v1") = 0
* StrUtil.compareVersion(null, null) = 0 * CharSequenceUtil.compareVersion(null, null) = 0
* StrUtil.compareVersion("v1", null) &gt; 0 * CharSequenceUtil.compareVersion("v1", null) &gt; 0
* StrUtil.compareVersion("1.0.0", "1.0.2") &lt; 0 * CharSequenceUtil.compareVersion("1.0.0", "1.0.2") &lt; 0
* StrUtil.compareVersion("1.0.2", "1.0.2a") &lt; 0 * CharSequenceUtil.compareVersion("1.0.2", "1.0.2a") &lt; 0
* StrUtil.compareVersion("1.13.0", "1.12.1c") &gt; 0 * CharSequenceUtil.compareVersion("1.13.0", "1.12.1c") &gt; 0
* StrUtil.compareVersion("V0.0.20170102", "V0.0.20170101") &gt; 0 * CharSequenceUtil.compareVersion("V0.0.20170102", "V0.0.20170101") &gt; 0
* </pre> * </pre>
* *
* @param version1 版本1 * @param version1 版本1
@ -3828,13 +3822,13 @@ public class CharSequenceUtil {
* 俗称脱敏功能后面其他功能可以见DesensitizedUtil(脱敏工具类) * 俗称脱敏功能后面其他功能可以见DesensitizedUtil(脱敏工具类)
* *
* <pre> * <pre>
* StrUtil.hide(null,*,*)=null * CharSequenceUtil.hide(null,*,*)=null
* StrUtil.hide("",0,*)="" * CharSequenceUtil.hide("",0,*)=""
* StrUtil.hide("jackduan@163.com",-1,4) ****duan@163.com * CharSequenceUtil.hide("jackduan@163.com",-1,4) ****duan@163.com
* StrUtil.hide("jackduan@163.com",2,3) ja*kduan@163.com * CharSequenceUtil.hide("jackduan@163.com",2,3) ja*kduan@163.com
* StrUtil.hide("jackduan@163.com",3,2) jackduan@163.com * CharSequenceUtil.hide("jackduan@163.com",3,2) jackduan@163.com
* StrUtil.hide("jackduan@163.com",16,16) jackduan@163.com * CharSequenceUtil.hide("jackduan@163.com",16,16) jackduan@163.com
* StrUtil.hide("jackduan@163.com",16,17) jackduan@163.com * CharSequenceUtil.hide("jackduan@163.com",16,17) jackduan@163.com
* </pre> * </pre>
* *
* @param str 字符串 * @param str 字符串
@ -3851,16 +3845,16 @@ public class CharSequenceUtil {
* 脱敏使用默认的脱敏策略 * 脱敏使用默认的脱敏策略
* *
* <pre> * <pre>
* StrUtil.desensitized("100", DesensitizedUtil.DesensitizedType.USER_ID)) = "0" * CharSequenceUtil.desensitized("100", DesensitizedUtil.DesensitizedType.USER_ID)) = "0"
* StrUtil.desensitized("段正淳", DesensitizedUtil.DesensitizedType.CHINESE_NAME)) = "段**" * CharSequenceUtil.desensitized("段正淳", DesensitizedUtil.DesensitizedType.CHINESE_NAME)) = "段**"
* StrUtil.desensitized("51343620000320711X", DesensitizedUtil.DesensitizedType.ID_CARD)) = "5***************1X" * CharSequenceUtil.desensitized("51343620000320711X", DesensitizedUtil.DesensitizedType.ID_CARD)) = "5***************1X"
* StrUtil.desensitized("09157518479", DesensitizedUtil.DesensitizedType.FIXED_PHONE)) = "0915*****79" * CharSequenceUtil.desensitized("09157518479", DesensitizedUtil.DesensitizedType.FIXED_PHONE)) = "0915*****79"
* StrUtil.desensitized("18049531999", DesensitizedUtil.DesensitizedType.MOBILE_PHONE)) = "180****1999" * CharSequenceUtil.desensitized("18049531999", DesensitizedUtil.DesensitizedType.MOBILE_PHONE)) = "180****1999"
* StrUtil.desensitized("北京市海淀区马连洼街道289号", DesensitizedUtil.DesensitizedType.ADDRESS)) = "北京市海淀区马********" * CharSequenceUtil.desensitized("北京市海淀区马连洼街道289号", DesensitizedUtil.DesensitizedType.ADDRESS)) = "北京市海淀区马********"
* StrUtil.desensitized("duandazhi-jack@gmail.com.cn", DesensitizedUtil.DesensitizedType.EMAIL)) = "d*************@gmail.com.cn" * CharSequenceUtil.desensitized("duandazhi-jack@gmail.com.cn", DesensitizedUtil.DesensitizedType.EMAIL)) = "d*************@gmail.com.cn"
* StrUtil.desensitized("1234567890", DesensitizedUtil.DesensitizedType.PASSWORD)) = "**********" * CharSequenceUtil.desensitized("1234567890", DesensitizedUtil.DesensitizedType.PASSWORD)) = "**********"
* StrUtil.desensitized("苏D40000", DesensitizedUtil.DesensitizedType.CAR_LICENSE)) = "苏D4***0" * CharSequenceUtil.desensitized("苏D40000", DesensitizedUtil.DesensitizedType.CAR_LICENSE)) = "苏D4***0"
* StrUtil.desensitized("11011111222233333256", DesensitizedType.BANK_CARD)) = "1101 **** **** **** 3256" * CharSequenceUtil.desensitized("11011111222233333256", DesensitizedType.BANK_CARD)) = "1101 **** **** **** 3256"
* </pre> * </pre>
* *
* @param str 字符串 * @param str 字符串
@ -4005,7 +3999,7 @@ public class CharSequenceUtil {
*/ */
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
public static <T extends CharSequence> T firstNonEmpty(T... strs) { public static <T extends CharSequence> T firstNonEmpty(T... strs) {
return ArrayUtil.firstMatch(StrUtil::isNotEmpty, strs); return ArrayUtil.firstMatch(CharSequenceUtil::isNotEmpty, strs);
} }
/** /**
@ -4019,7 +4013,7 @@ public class CharSequenceUtil {
*/ */
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
public static <T extends CharSequence> T firstNonBlank(T... strs) { public static <T extends CharSequence> T firstNonBlank(T... strs) {
return ArrayUtil.firstMatch(StrUtil::isNotBlank, strs); return ArrayUtil.firstMatch(CharSequenceUtil::isNotBlank, strs);
} }
// ------------------------------------------------------------------------ lower and upper // ------------------------------------------------------------------------ lower and upper
@ -4161,9 +4155,9 @@ public class CharSequenceUtil {
* 切换给定字符串中的大小写大写转小写小写转大写 * 切换给定字符串中的大小写大写转小写小写转大写
* *
* <pre> * <pre>
* StrUtil.swapCase(null) = null * CharSequenceUtil.swapCase(null) = null
* StrUtil.swapCase("") = "" * CharSequenceUtil.swapCase("") = ""
* StrUtil.swapCase("The dog has a BONE") = "tHE DOG HAS A bone" * CharSequenceUtil.swapCase("The dog has a BONE") = "tHE DOG HAS A bone"
* </pre> * </pre>
* *
* @param str 字符串 * @param str 字符串
@ -4257,7 +4251,7 @@ public class CharSequenceUtil {
* @return 是否包围空串不包围 * @return 是否包围空串不包围
*/ */
public static boolean isSurround(CharSequence str, CharSequence prefix, CharSequence suffix) { public static boolean isSurround(CharSequence str, CharSequence prefix, CharSequence suffix) {
if (StrUtil.isBlank(str)) { if (CharSequenceUtil.isBlank(str)) {
return false; return false;
} }
if (str.length() < (prefix.length() + suffix.length())) { if (str.length() < (prefix.length() + suffix.length())) {
@ -4277,7 +4271,7 @@ public class CharSequenceUtil {
* @return 是否包围空串不包围 * @return 是否包围空串不包围
*/ */
public static boolean isSurround(CharSequence str, char prefix, char suffix) { public static boolean isSurround(CharSequence str, char prefix, char suffix) {
if (StrUtil.isBlank(str)) { if (CharSequenceUtil.isBlank(str)) {
return false; return false;
} }
if (str.length() < 2) { if (str.length() < 2) {
@ -4462,7 +4456,7 @@ public class CharSequenceUtil {
* @since 3.2.3 * @since 3.2.3
*/ */
public static boolean isAllCharMatch(CharSequence value, Matcher<Character> matcher) { public static boolean isAllCharMatch(CharSequence value, Matcher<Character> matcher) {
if (StrUtil.isBlank(value)) { if (CharSequenceUtil.isBlank(value)) {
return false; return false;
} }
for (int i = value.length(); --i >= 0; ) { for (int i = value.length(); --i >= 0; ) {

View File

@ -49,9 +49,16 @@ public class PatternFinder extends TextFinder {
@Override @Override
public int start(int from) { public int start(int from) {
if (matcher.find(from)) { if (matcher.find(from)) {
final int end = matcher.end();
// 只有匹配到的字符串结尾在limit范围内才算找到 // 只有匹配到的字符串结尾在limit范围内才算找到
if(matcher.end() <= getValidEndIndex()){ if(end <= getValidEndIndex()){
return matcher.start(); final int start = matcher.start();
if(start == end){
// issue#3421如果匹配空串按照未匹配对待避免死循环
return INDEX_NOT_FOUND;
}
return start;
} }
} }
return INDEX_NOT_FOUND; return INDEX_NOT_FOUND;

View File

@ -2321,80 +2321,80 @@ public class NumberUtil {
} }
/** /**
* 如果给定值为0返回1否则返回原值 * 如果给定值为{@code null}返回0否则返回原值
* *
* @param number * @param number
* @return 1或非0值 * @return 0或非0值
*/ */
public static int nullToZero(Integer number) { public static int nullToZero(Integer number) {
return number == null ? 0 : number; return number == null ? 0 : number;
} }
/** /**
* 如果给定值为0返回1否则返回原值 * 如果给定值为{@code null}返回0否则返回原值
* *
* @param number * @param number
* @return 1或非0值 * @return 0或非0值
*/ */
public static long nullToZero(Long number) { public static long nullToZero(Long number) {
return number == null ? 0L : number; return number == null ? 0L : number;
} }
/** /**
* 如果给定值为0返回1否则返回原值 * 如果给定值为{@code null}返回0否则返回原值
* *
* @param number * @param number
* @return 1或非0值 * @return 0或非0值
*/ */
public static double nullToZero(Double number) { public static double nullToZero(Double number) {
return number == null ? 0.0 : number; return number == null ? 0.0 : number;
} }
/** /**
* 如果给定值为0返回1否则返回原值 * 如果给定值为{@code null}返回0否则返回原值
* *
* @param number * @param number
* @return 1或非0值 * @return 0或非0值
*/ */
public static float nullToZero(Float number) { public static float nullToZero(Float number) {
return number == null ? 0.0f : number; return number == null ? 0.0f : number;
} }
/** /**
* 如果给定值为0返回1否则返回原值 * 如果给定值为{@code null}返回0否则返回原值
* *
* @param number * @param number
* @return 1或非0值 * @return 0或非0值
*/ */
public static short nullToZero(Short number) { public static short nullToZero(Short number) {
return number == null ? (short) 0 : number; return number == null ? (short) 0 : number;
} }
/** /**
* 如果给定值为0返回1否则返回原值 * 如果给定值为{@code null}返回0否则返回原值
* *
* @param number * @param number
* @return 1或非0值 * @return 0或非0值
*/ */
public static byte nullToZero(Byte number) { public static byte nullToZero(Byte number) {
return number == null ? (byte) 0 : number; return number == null ? (byte) 0 : number;
} }
/** /**
* 如果给定值为0返回1否则返回原值 * 如果给定值为{@code null}返回0否则返回原值
* *
* @param number * @param number
* @return 1或非0值 * @return 0或非0值
*/ */
public static BigDecimal nullToZero(BigDecimal number) { public static BigDecimal nullToZero(BigDecimal number) {
return number == null ? BigDecimal.ZERO : number; return number == null ? BigDecimal.ZERO : number;
} }
/** /**
* 如果给定值为0返回1否则返回原值 * 如果给定值为{@code null}返回0否则返回原值
* *
* @param number * @param number
* @return 1或非0值 * @return 0或非0值
*/ */
public static BigInteger nullToZero(BigInteger number) { public static BigInteger nullToZero(BigInteger number) {
return number == null ? BigInteger.ZERO : number; return number == null ? BigInteger.ZERO : number;

View File

@ -488,7 +488,9 @@ public class StrUtil extends CharSequenceUtil implements StrPool {
} }
/** /**
* 截断字符串使用其按照指定编码为字节后不超过maxBytes长度 * 截断字符串使用其按照指定编码为字节后不超过maxBytes长度<br>
* 此方法用于截取总bytes数不超过指定长度如果字符出没有超出原样输出如果超出了则截取掉超出部分并可选添加...
* 但是添加...后总长度也不超过限制长度
* *
* @param str 原始字符串 * @param str 原始字符串
* @param charset 指定编码 * @param charset 指定编码

View File

@ -147,6 +147,14 @@ public class URLUtil extends URLEncodeUtil {
try { try {
return new URL(null, url, handler); return new URL(null, url, handler);
} catch (MalformedURLException e) { } catch (MalformedURLException e) {
// issue#I8PY3Y
if(e.getMessage().contains("Accessing an URL protocol that was not enabled")){
// Graalvm打包需要手动指定参数开启协议
// --enable-url-protocols=http
// --enable-url-protocols=https
throw new UtilException(e);
}
// 尝试文件路径 // 尝试文件路径
try { try {
return new File(url).toURI().toURL(); return new File(url).toURI().toURL();

View File

@ -30,7 +30,7 @@ public class TestIssueI8CLBJ {
Thread thread = new Thread(() -> { Thread thread = new Thread(() -> {
try { try {
String valueFieldName = annotation.valueFieldName(); String valueFieldName = annotation.valueFieldName();
System.out.println("valueFieldName:" + valueFieldName); //Console.log("valueFieldName:" + valueFieldName);
} catch (Exception e) { } catch (Exception e) {
e.printStackTrace(); e.printStackTrace();
} }

View File

@ -0,0 +1,33 @@
package cn.hutool.core.bean;
import cn.hutool.core.annotation.Alias;
import lombok.Data;
import lombok.Setter;
import org.junit.Assert;
import org.junit.Test;
public class IssueI8JASOTest {
@Test
public void copyTest() {
final UserOne userOne = new UserOne();
userOne.setEmail("123@qq.com");
final UserTwo userTwo = new UserTwo();
BeanUtil.copyProperties(userOne, userTwo);
Assert.assertEquals(userOne.getEmail(), userTwo.getEmail());
}
@Data
public static class UserOne {
private Long id;
@Alias("邮箱")
private String email;
}
@Data
public static class UserTwo {
private Long id;
@Alias("邮箱")
private String email;
}
}

View File

@ -1126,8 +1126,8 @@ public class DateUtilTest {
@Test @Test
public void isLastDayTest() { public void isLastDayTest() {
DateTime dateTime = DateUtil.parse("2022-09-30"); final DateTime dateTime = DateUtil.parse("2022-09-30");
int dayOfMonth = DateUtil.getLastDayOfMonth(dateTime); final int dayOfMonth = DateUtil.getLastDayOfMonth(dateTime);
Assert.assertEquals(dayOfMonth, dateTime.dayOfMonth()); Assert.assertEquals(dayOfMonth, dateTime.dayOfMonth());
Assert.assertTrue("not is last day of this month !!", DateUtil.isLastDayOfMonth(dateTime)); Assert.assertTrue("not is last day of this month !!", DateUtil.isLastDayOfMonth(dateTime));
} }
@ -1167,4 +1167,11 @@ public class DateUtilTest {
Assert.assertNotNull(parse); Assert.assertNotNull(parse);
Assert.assertEquals("2019-10-22 09:56:03", parse.toString()); Assert.assertEquals("2019-10-22 09:56:03", parse.toString());
} }
@Test
public void issueI8NMP7Test() {
final String str = "1702262524444";
final DateTime parse = DateUtil.parse(str);
Assert.assertEquals("2023-12-11 10:42:04", Objects.requireNonNull(parse).toString());
}
} }

View File

@ -0,0 +1,15 @@
package cn.hutool.core.img;
import cn.hutool.core.io.FileUtil;
import org.junit.Ignore;
import org.junit.Test;
public class IssueI8L8UATest {
@Test
@Ignore
public void convertTest() {
ImgUtil.convert(
FileUtil.file("d:/test/1.png"),
FileUtil.file("d:/test/1.jpg"));
}
}

View File

@ -1,5 +1,6 @@
package cn.hutool.core.text.split; package cn.hutool.core.text.split;
import cn.hutool.core.collection.ListUtil;
import cn.hutool.core.text.StrSplitter; import cn.hutool.core.text.StrSplitter;
import org.junit.Assert; import org.junit.Assert;
import org.junit.Test; import org.junit.Test;
@ -15,8 +16,8 @@ public class StrSplitterTest {
@Test @Test
public void splitByCharTest(){ public void splitByCharTest(){
String str1 = "a, ,efedsfs, ddf"; final String str1 = "a, ,efedsfs, ddf";
List<String> split = StrSplitter.split(str1, ',', 0, true, true); final List<String> split = StrSplitter.split(str1, ',', 0, true, true);
Assert.assertEquals("ddf", split.get(2)); Assert.assertEquals("ddf", split.get(2));
Assert.assertEquals(3, split.size()); Assert.assertEquals(3, split.size());
@ -24,32 +25,32 @@ public class StrSplitterTest {
@Test @Test
public void splitByStrTest(){ public void splitByStrTest(){
String str1 = "aabbccaaddaaee"; final String str1 = "aabbccaaddaaee";
List<String> split = StrSplitter.split(str1, "aa", 0, true, true); final List<String> split = StrSplitter.split(str1, "aa", 0, true, true);
Assert.assertEquals("ee", split.get(2)); Assert.assertEquals("ee", split.get(2));
Assert.assertEquals(3, split.size()); Assert.assertEquals(3, split.size());
} }
@Test @Test
public void splitByBlankTest(){ public void splitByBlankTest(){
String str1 = "aa bbccaa ddaaee"; final String str1 = "aa bbccaa ddaaee";
List<String> split = StrSplitter.split(str1, 0); final List<String> split = StrSplitter.split(str1, 0);
Assert.assertEquals("ddaaee", split.get(2)); Assert.assertEquals("ddaaee", split.get(2));
Assert.assertEquals(3, split.size()); Assert.assertEquals(3, split.size());
} }
@Test @Test
public void splitPathTest(){ public void splitPathTest(){
String str1 = "/use/local/bin"; final String str1 = "/use/local/bin";
List<String> split = StrSplitter.splitPath(str1, 0); final List<String> split = StrSplitter.splitPath(str1, 0);
Assert.assertEquals("bin", split.get(2)); Assert.assertEquals("bin", split.get(2));
Assert.assertEquals(3, split.size()); Assert.assertEquals(3, split.size());
} }
@Test @Test
public void splitMappingTest() { public void splitMappingTest() {
String str = "1.2."; final String str = "1.2.";
List<Long> split = StrSplitter.split(str, '.', 0, true, true, Long::parseLong); final List<Long> split = StrSplitter.split(str, '.', 0, true, true, Long::parseLong);
Assert.assertEquals(2, split.size()); Assert.assertEquals(2, split.size());
Assert.assertEquals(Long.valueOf(1L), split.get(0)); Assert.assertEquals(Long.valueOf(1L), split.get(0));
Assert.assertEquals(Long.valueOf(2L), split.get(1)); Assert.assertEquals(Long.valueOf(2L), split.get(1));
@ -57,7 +58,7 @@ public class StrSplitterTest {
@Test @Test
public void splitEmptyTest(){ public void splitEmptyTest(){
String str = ""; final String str = "";
final String[] split = str.split(","); final String[] split = str.split(",");
final String[] strings = StrSplitter.splitToArray(str, ",", -1, false, false); final String[] strings = StrSplitter.splitToArray(str, ",", -1, false, false);
Assert.assertNotNull(strings); Assert.assertNotNull(strings);
@ -66,7 +67,7 @@ public class StrSplitterTest {
@Test @Test
public void splitNullTest(){ public void splitNullTest(){
String str = null; final String str = null;
final String[] strings = StrSplitter.splitToArray(str, ",", -1, false, false); final String[] strings = StrSplitter.splitToArray(str, ",", -1, false, false);
Assert.assertNotNull(strings); Assert.assertNotNull(strings);
Assert.assertEquals(0, strings.length); Assert.assertEquals(0, strings.length);
@ -77,7 +78,7 @@ public class StrSplitterTest {
*/ */
@Test @Test
public void splitByRegexTest(){ public void splitByRegexTest(){
String text = "01 821 34567890182345617821"; final String text = "01 821 34567890182345617821";
List<String> strings = StrSplitter.splitByRegex(text, "21", 0, false, true); List<String> strings = StrSplitter.splitByRegex(text, "21", 0, false, true);
Assert.assertEquals(2, strings.size()); Assert.assertEquals(2, strings.size());
Assert.assertEquals("01 8", strings.get(0)); Assert.assertEquals("01 8", strings.get(0));
@ -89,4 +90,19 @@ public class StrSplitterTest {
Assert.assertEquals(" 345678901823456178", strings.get(1)); Assert.assertEquals(" 345678901823456178", strings.get(1));
Assert.assertEquals("", strings.get(2)); Assert.assertEquals("", strings.get(2));
} }
@Test
public void issue3421Test() {
List<String> strings = StrSplitter.splitByRegex("", "", 0, false, false);
Assert.assertEquals(ListUtil.of(""), strings);
strings = StrSplitter.splitByRegex("aaa", "", 0, false, false);
Assert.assertEquals(ListUtil.of("aaa"), strings);
strings = StrSplitter.splitByRegex("", "aaa", 0, false, false);
Assert.assertEquals(ListUtil.of(""), strings);
strings = StrSplitter.splitByRegex("", "", 0, false, true);
Assert.assertEquals(ListUtil.of(), strings);
}
} }

View File

@ -3,6 +3,7 @@ package cn.hutool.core.util;
import java.nio.charset.StandardCharsets; import java.nio.charset.StandardCharsets;
import java.util.List; import java.util.List;
import cn.hutool.core.lang.Console;
import cn.hutool.core.lang.Dict; import cn.hutool.core.lang.Dict;
import org.junit.Assert; import org.junit.Assert;
import org.junit.Test; import org.junit.Test;
@ -670,6 +671,20 @@ public class StrUtilTest {
Assert.assertEquals(str, ret); Assert.assertEquals(str, ret);
} }
@Test
public void truncateUtf8Test2() {
final String str = "这是This一";
final String ret = StrUtil.truncateUtf8(str, 13);
Assert.assertEquals("这是This一", ret);
}
@Test
public void truncateUtf8Test3() {
final String str = "一二三四";
final String ret = StrUtil.truncateUtf8(str, 11);
Assert.assertEquals("一二...", ret);
}
@Test @Test
public void truncateByByteLengthTest() { public void truncateByByteLengthTest() {
final String str = "This is English"; final String str = "This is English";

View File

@ -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.23</version> <version>5.8.24</version>
</parent> </parent>
<artifactId>hutool-cron</artifactId> <artifactId>hutool-cron</artifactId>

View File

@ -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.23</version> <version>5.8.24</version>
</parent> </parent>
<artifactId>hutool-crypto</artifactId> <artifactId>hutool-crypto</artifactId>

View File

@ -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.23</version> <version>5.8.24</version>
</parent> </parent>
<artifactId>hutool-db</artifactId> <artifactId>hutool-db</artifactId>

View File

@ -1,8 +1,11 @@
package cn.hutool.db; package cn.hutool.db;
import cn.hutool.core.lang.Assert; import cn.hutool.core.lang.Assert;
import cn.hutool.core.lang.PatternPool;
import cn.hutool.core.lang.RegexPool;
import cn.hutool.core.map.MapUtil; import cn.hutool.core.map.MapUtil;
import cn.hutool.core.util.ArrayUtil; import cn.hutool.core.util.ArrayUtil;
import cn.hutool.core.util.ReUtil;
import cn.hutool.core.util.StrUtil; import cn.hutool.core.util.StrUtil;
import cn.hutool.db.dialect.Dialect; import cn.hutool.db.dialect.Dialect;
import cn.hutool.db.dialect.DialectFactory; import cn.hutool.db.dialect.DialectFactory;
@ -18,6 +21,8 @@ import java.io.Serializable;
import java.sql.Connection; import java.sql.Connection;
import java.sql.PreparedStatement; import java.sql.PreparedStatement;
import java.sql.SQLException; import java.sql.SQLException;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/** /**
* 提供基于方言的原始增删改查执行封装 * 提供基于方言的原始增删改查执行封装
@ -273,11 +278,14 @@ public class DialectRunner implements Serializable {
checkConn(conn); checkConn(conn);
String selectSql = sqlBuilder.build(); String selectSql = sqlBuilder.build();
// 去除order by 子句 // 去除order by 子句
final int orderByIndex = StrUtil.lastIndexOfIgnoreCase(selectSql, " order by"); final Pattern pattern = PatternPool.get("(.*?)[\\s]order[\\s]by[\\s][^\\s]+\\s(asc|desc)?", Pattern.CASE_INSENSITIVE);
if (orderByIndex > 0) { final Matcher matcher = pattern.matcher(selectSql);
selectSql = StrUtil.subPre(selectSql, orderByIndex); if (matcher.matches()) {
selectSql = matcher.group(1);
} }
return SqlExecutor.queryAndClosePs(dialect.psForCount(conn, return SqlExecutor.queryAndClosePs(dialect.psForCount(conn,
SqlBuilder.of(selectSql).addParams(sqlBuilder.getParamValueArray())), SqlBuilder.of(selectSql).addParams(sqlBuilder.getParamValueArray())),
new NumberHandler()).longValue(); new NumberHandler()).longValue();

View File

@ -164,6 +164,9 @@ public class DialectFactory implements DriverNamePool {
} else if (nameContainsProductInfo.contains("mariadb")) { } else if (nameContainsProductInfo.contains("mariadb")) {
// mariadb // mariadb
driver = DRIVER_MARIADB; driver = DRIVER_MARIADB;
} else if (nameContainsProductInfo.contains("opengauss")) {
// OpenGauss
driver = DRIVER_OPENGAUSS;
} }
return driver; return driver;

View File

@ -108,5 +108,8 @@ public interface DriverNamePool {
* JDBC 驱动 Sybase * JDBC 驱动 Sybase
*/ */
String DRIVER_SYBASE = "com.sybase.jdbc4.jdbc.SybDriver"; String DRIVER_SYBASE = "com.sybase.jdbc4.jdbc.SybDriver";
/**
* JDBC 驱动 OpenGauss
*/
String DRIVER_OPENGAUSS = "org.opengauss.Driver";
} }

View File

@ -22,6 +22,9 @@ import java.sql.SQLException;
public class PostgresqlDialect extends AnsiSqlDialect{ public class PostgresqlDialect extends AnsiSqlDialect{
private static final long serialVersionUID = 3889210427543389642L; private static final long serialVersionUID = 3889210427543389642L;
/**
* 构造
*/
public PostgresqlDialect() { public PostgresqlDialect() {
wrapper = new Wrapper('"'); wrapper = new Wrapper('"');
} }
@ -53,7 +56,7 @@ public class PostgresqlDialect extends AnsiSqlDialect{
final String wrapedField = (null != wrapper) ? wrapper.wrap(field) : field; final String wrapedField = (null != wrapper) ? wrapper.wrap(field) : field;
fieldsPart.append(wrapedField); fieldsPart.append(wrapedField);
updateHolder.append(wrapedField).append("=EXCLUDED.").append(field); updateHolder.append(wrapedField).append("=EXCLUDED.").append(wrapedField);
placeHolder.append("?"); placeHolder.append("?");
builder.addParams(value); builder.addParams(value);
} }

View File

@ -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.23</version> <version>5.8.24</version>
</parent> </parent>
<artifactId>hutool-dfa</artifactId> <artifactId>hutool-dfa</artifactId>

View File

@ -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.23</version> <version>5.8.24</version>
</parent> </parent>
<artifactId>hutool-extra</artifactId> <artifactId>hutool-extra</artifactId>
@ -394,7 +394,7 @@
<dependency> <dependency>
<groupId>ch.qos.logback</groupId> <groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId> <artifactId>logback-classic</artifactId>
<version>1.2.11</version> <version>1.4.13</version>
<scope>test</scope> <scope>test</scope>
<exclusions> <exclusions>
<exclusion> <exclusion>

View File

@ -177,6 +177,8 @@ public class StreamArchiver implements Archiver {
for (File childFile : files) { for (File childFile : files) {
addInternal(childFile, entryName, filter); addInternal(childFile, entryName, filter);
} }
} else {
out.closeArchiveEntry();
} }
} else { } else {
if (file.isFile()) { if (file.isFile()) {

View File

@ -1,5 +1,7 @@
package cn.hutool.extra.pinyin.engine.tinypinyin; package cn.hutool.extra.pinyin.engine.tinypinyin;
import cn.hutool.core.lang.Opt;
import cn.hutool.core.util.StrUtil;
import cn.hutool.extra.pinyin.PinyinEngine; import cn.hutool.extra.pinyin.PinyinEngine;
import com.github.promeg.pinyinhelper.Pinyin; import com.github.promeg.pinyinhelper.Pinyin;
@ -51,7 +53,8 @@ public class TinyPinyinEngine implements PinyinEngine {
@Override @Override
public String getPinyin(String str, String separator) { public String getPinyin(String str, String separator) {
return Pinyin.toPinyin(str, separator).toLowerCase(); final String pinyin = Pinyin.toPinyin(str, separator);
return StrUtil.isEmpty(pinyin) ? pinyin : pinyin.toLowerCase();
} }
} }

View File

@ -188,6 +188,38 @@ public class SpringUtil implements BeanFactoryPostProcessor, ApplicationContextA
return applicationContext.getEnvironment().getProperty(key); return applicationContext.getEnvironment().getProperty(key);
} }
/**
* 获取配置文件配置项的值
*
* @param key 配置项key
* @param defaultValue 默认值
* @return 属性值
* @since 5.8.24
*/
public static String getProperty(String key, String defaultValue) {
if (null == applicationContext) {
return null;
}
return applicationContext.getEnvironment().getProperty(key, defaultValue);
}
/**
* 获取配置文件配置项的值
*
* @param <T> 属性值类型
* @param key 配置项key
* @param targetType 配置项类型
* @param defaultValue 默认值
* @return 属性值
* @since 5.8.24
*/
public static <T> T getProperty(String key, Class<T> targetType, T defaultValue) {
if (null == applicationContext) {
return null;
}
return applicationContext.getEnvironment().getProperty(key, targetType, defaultValue);
}
/** /**
* 获取应用程序名称 * 获取应用程序名称
* *

View File

@ -1,42 +1,38 @@
package cn.hutool.extra.tokenizer.engine.ikanalyzer; package cn.hutool.extra.tokenizer.engine.ikanalyzer;
import org.wltea.analyzer.core.IKSegmenter;
import cn.hutool.core.util.StrUtil; import cn.hutool.core.util.StrUtil;
import cn.hutool.extra.tokenizer.TokenizerEngine;
import cn.hutool.extra.tokenizer.Result; import cn.hutool.extra.tokenizer.Result;
import cn.hutool.extra.tokenizer.TokenizerEngine;
import org.wltea.analyzer.core.IKSegmenter;
/** /**
* IKAnalyzer分词引擎实现<br> * IKAnalyzer分词引擎实现<br>
* 项目地址https://github.com/yozhao/IKAnalyzer * 项目地址https://github.com/yozhao/IKAnalyzer
* *
* @author looly * @author looly
*
*/ */
public class IKAnalyzerEngine implements TokenizerEngine { public class IKAnalyzerEngine implements TokenizerEngine {
private final IKSegmenter seg;
/** /**
* 构造 * 构造
*
*/ */
public IKAnalyzerEngine() { public IKAnalyzerEngine() {
this(new IKSegmenter(null, true));
} }
/** /**
* 构造 * 构造
* *
* @param seg {@link IKSegmenter} * @param seg {@link IKSegmenter}
* @deprecated 并发问题导致无法共用IKSegmenter因此废弃
*/ */
@Deprecated
public IKAnalyzerEngine(IKSegmenter seg) { public IKAnalyzerEngine(IKSegmenter seg) {
this.seg = seg;
} }
@Override @Override
public Result parse(CharSequence text) { public Result parse(CharSequence text) {
this.seg.reset(StrUtil.getReader(text)); final IKSegmenter copySeg = new IKSegmenter(null, true);
return new IKAnalyzerResult(this.seg); copySeg.reset(StrUtil.getReader(text));
return new IKAnalyzerResult(copySeg);
} }
} }

View File

@ -72,4 +72,36 @@ public class ArchiverTest {
}) })
.finish().close(); .finish().close();
} }
/**
* Add: D:\disk-all
* Add: D:\disk-all\els-app
* Add: D:\disk-all\els-app\db-backup
* Add: D:\disk-all\els-app\新建 文本文档.txt
* Add: D:\disk-all\新建 文本文档.txt
* Add: D:\disk-all\新建文件夹
*/
@Test
@Ignore
public void emptyTest(){
final File file = FileUtil.file("d:/disk-all.tgz");
CompressUtil.createArchiver(CharsetUtil.CHARSET_UTF_8, "tgz", file)
.add(FileUtil.file("D:\\disk-all"), (f)->{
Console.log("Add: {}", f.getPath());
return true;
})
.finish().close();
}
@Test
@Ignore
public void emptyZTest(){
final File file = FileUtil.file("d:/disk-all.7z");
CompressUtil.createArchiver(CharsetUtil.CHARSET_UTF_8, "7z", file)
.add(FileUtil.file("D:\\disk-all"), (f)->{
Console.log("Add: {}", f.getPath());
return true;
})
.finish().close();
}
} }

View File

@ -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.23</version> <version>5.8.24</version>
</parent> </parent>
<artifactId>hutool-http</artifactId> <artifactId>hutool-http</artifactId>

View File

@ -137,6 +137,7 @@ public final class HTMLFilter {
vAllowed.put("strong", no_atts); vAllowed.put("strong", no_atts);
vAllowed.put("i", no_atts); vAllowed.put("i", no_atts);
vAllowed.put("em", no_atts); vAllowed.put("em", no_atts);
vAllowed.put("p", no_atts);
vSelfClosingTags = new String[]{"img"}; vSelfClosingTags = new String[]{"img"};
vNeedClosingTags = new String[]{"a", "b", "strong", "i", "em"}; vNeedClosingTags = new String[]{"a", "b", "strong", "i", "em"};

View File

@ -3,6 +3,7 @@ package cn.hutool.http.server;
import cn.hutool.core.collection.CollUtil; import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.io.IORuntimeException; import cn.hutool.core.io.IORuntimeException;
import cn.hutool.core.io.IoUtil; import cn.hutool.core.io.IoUtil;
import cn.hutool.core.io.LimitedInputStream;
import cn.hutool.core.map.CaseInsensitiveMap; import cn.hutool.core.map.CaseInsensitiveMap;
import cn.hutool.core.map.multi.ListValueMap; import cn.hutool.core.map.multi.ListValueMap;
import cn.hutool.core.net.NetUtil; import cn.hutool.core.net.NetUtil;
@ -294,7 +295,24 @@ public class HttpServerRequest extends HttpServerBase {
* @return * @return
*/ */
public InputStream getBodyStream() { public InputStream getBodyStream() {
return this.httpExchange.getRequestBody(); InputStream bodyStream = this.httpExchange.getRequestBody();
//issue#I6Q30X读取body长度避免读取结束后无法正常结束问题
final String contentLengthStr = getHeader(Header.CONTENT_LENGTH);
long contentLength = 0;
if(StrUtil.isNotBlank(contentLengthStr)){
try{
contentLength = Long.parseLong(contentLengthStr);
} catch (final NumberFormatException ignore){
// ignore
}
}
if(contentLength > 0){
bodyStream = new LimitedInputStream(bodyStream, contentLength);
}
return bodyStream;
} }
/** /**

View File

@ -73,11 +73,13 @@ public class RootAction implements Action {
file = FileUtil.file(file, indexFileName); file = FileUtil.file(file, indexFileName);
if (file.exists() && file.isFile()) { if (file.exists() && file.isFile()) {
response.write(file); response.write(file);
return;
} }
} }
} else{ } else{
final String name = request.getParam("name"); final String name = request.getParam("name");
response.write(file, name); response.write(file, name);
return;
} }
} }

View File

@ -0,0 +1,19 @@
package cn.hutool.http;
import org.junit.Assert;
import org.junit.Test;
public class HTMLFilterTest {
@Test
public void issue3433Test() {
String p1 = "<p>a</p>";
String p2 = "<p onclick=\"bbbb\">a</p>";
final HTMLFilter htmlFilter = new HTMLFilter();
String filter = htmlFilter.filter(p1);
Assert.assertEquals("<p>a</p>", filter);
filter = htmlFilter.filter(p2);
Assert.assertEquals("<p>a</p>", filter);
}
}

View File

@ -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.23</version> <version>5.8.24</version>
</parent> </parent>
<artifactId>hutool-json</artifactId> <artifactId>hutool-json</artifactId>

View File

@ -0,0 +1,23 @@
package cn.hutool.json;
import lombok.Data;
import lombok.ToString;
import org.junit.Assert;
import org.junit.Test;
import java.util.Date;
public class IssueI8NMP7Test {
@Test
public void toBeanTest() {
final String jsonString = "{\"enableTime\":\"1702262524444\"}";
final DemoModel bean = JSONUtil.toBean(jsonString, JSONConfig.create(), DemoModel.class);
Assert.assertNotNull(bean.getEnableTime());
}
@Data
@ToString
static class DemoModel{
private Date enableTime;
}
}

View File

@ -0,0 +1,24 @@
package cn.hutool.json;
import lombok.Data;
import org.junit.Assert;
import org.junit.Test;
import java.util.Map;
public class IssueI8PC9FTest {
@Test
public void toBeanIgnoreErrorTest() {
final String testJson = "{\"testMap\":\"\"}";
final TestBean test = JSONUtil.parseObj(testJson, JSONConfig.create().setIgnoreError(true))
.toBean(TestBean.class);
Assert.assertNotNull(test);
Assert.assertNull(test.getTestMap());
}
@Data
static class TestBean{
private Map<?, ?> testMap;
}
}

View File

@ -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.23</version> <version>5.8.24</version>
</parent> </parent>
<artifactId>hutool-jwt</artifactId> <artifactId>hutool-jwt</artifactId>

View File

@ -302,7 +302,18 @@ public class JWT implements RegisteredPayload<JWT> {
* @return JWT字符串 * @return JWT字符串
*/ */
public String sign() { public String sign() {
return sign(this.signer); return sign(true);
}
/**
* 签名生成JWT字符串
*
* @param addTypeIfNot 如果'typ'头不存在是否赋值默认值
* @return JWT字符串
* @since 5.8.24
*/
public String sign(boolean addTypeIfNot) {
return sign(this.signer, addTypeIfNot);
} }
/** /**
@ -312,13 +323,27 @@ public class JWT implements RegisteredPayload<JWT> {
* @return JWT字符串 * @return JWT字符串
*/ */
public String sign(JWTSigner signer) { public String sign(JWTSigner signer) {
return sign(signer, true);
}
/**
* 签名生成JWT字符串
*
* @param signer JWT签名器
* @param addTypeIfNot 如果'typ'头不存在是否赋值默认值
* @return JWT字符串
* @since 5.8.24
*/
public String sign(JWTSigner signer, boolean addTypeIfNot) {
Assert.notNull(signer, () -> new JWTException("No Signer provided!")); Assert.notNull(signer, () -> new JWTException("No Signer provided!"));
// 检查tye信息 // 检查tye信息
if (addTypeIfNot) {
final String type = (String) this.header.getClaim(JWTHeader.TYPE); final String type = (String) this.header.getClaim(JWTHeader.TYPE);
if (StrUtil.isBlank(type)) { if (StrUtil.isBlank(type)) {
this.header.setClaim(JWTHeader.TYPE, "JWT"); this.header.setClaim(JWTHeader.TYPE, "JWT");
} }
}
// 检查头信息中是否有算法信息 // 检查头信息中是否有算法信息
final String algorithm = (String) this.header.getClaim(JWTHeader.ALGORITHM); final String algorithm = (String) this.header.getClaim(JWTHeader.ALGORITHM);

View File

@ -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.23</version> <version>5.8.24</version>
</parent> </parent>
<artifactId>hutool-log</artifactId> <artifactId>hutool-log</artifactId>
@ -21,7 +21,7 @@
<!-- versions --> <!-- versions -->
<slf4j.version>1.7.36</slf4j.version> <slf4j.version>1.7.36</slf4j.version>
<logback.version>1.4.6</logback.version> <logback.version>1.4.13</logback.version>
<log4j.version>1.2.17</log4j.version> <log4j.version>1.2.17</log4j.version>
<log4j2.version>2.20.0</log4j2.version> <log4j2.version>2.20.0</log4j2.version>
<commons-logging.version>1.2</commons-logging.version> <commons-logging.version>1.2</commons-logging.version>

View File

@ -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.23</version> <version>5.8.24</version>
</parent> </parent>
<artifactId>hutool-poi</artifactId> <artifactId>hutool-poi</artifactId>

View File

@ -189,7 +189,8 @@ public class ExcelBase<T extends ExcelBase<T>> implements Closeable {
sheet = workbook.cloneSheet(sheetIndex, newSheetName); sheet = workbook.cloneSheet(sheetIndex, newSheetName);
} else { } else {
sheet = this.workbook.cloneSheet(sheetIndex); sheet = this.workbook.cloneSheet(sheetIndex);
this.workbook.setSheetName(sheetIndex, newSheetName); // issue#I8QIBBclone后的sheet的index应该重新获取
this.workbook.setSheetName(workbook.getSheetIndex(sheet), newSheetName);
} }
if (setAsCurrentSheet) { if (setAsCurrentSheet) {
this.sheet = sheet; this.sheet = sheet;

View File

@ -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.23</version> <version>5.8.24</version>
</parent> </parent>
<artifactId>hutool-script</artifactId> <artifactId>hutool-script</artifactId>

View File

@ -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.23</version> <version>5.8.24</version>
</parent> </parent>
<artifactId>hutool-setting</artifactId> <artifactId>hutool-setting</artifactId>

View File

@ -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.23</version> <version>5.8.24</version>
</parent> </parent>
<artifactId>hutool-socket</artifactId> <artifactId>hutool-socket</artifactId>

View File

@ -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.23</version> <version>5.8.24</version>
</parent> </parent>
<artifactId>hutool-system</artifactId> <artifactId>hutool-system</artifactId>

View File

@ -8,7 +8,7 @@
<groupId>cn.hutool</groupId> <groupId>cn.hutool</groupId>
<artifactId>hutool-parent</artifactId> <artifactId>hutool-parent</artifactId>
<version>5.8.23</version> <version>5.8.24</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>