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
-------------------------------------------------------------------------------------------------------------
# 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)

View File

@ -47,8 +47,7 @@
-------------------------------------------------------------------------------
<p align="center">
<a href="#">
<img alt="" src="https://plus.hutool.cn/images/zanzhu.jpg"/></a>
<a href="#"><img style="width: 45%" alt="" src="https://plus.hutool.cn/images/zanzhu.jpg"/></a>
</p>
-------------------------------------------------------------------------------
@ -116,7 +115,7 @@ Each module can be introduced individually, or all modules can be introduced by
## 📝Doc
[📘Chinese documentation](https://www.hutool.cn/docs/)
[📘Chinese documentation](https://doc.hutool.cn/pages/index/)
[📘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>
<groupId>cn.hutool</groupId>
<artifactId>hutool-all</artifactId>
<version>5.8.23</version>
<version>5.8.24</version>
</dependency>
```
### 🍐Gradle
```
implementation 'cn.hutool:hutool-all:5.8.23'
implementation 'cn.hutool:hutool-all:5.8.24'
```
## 📥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:
> 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**'.
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

View File

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

View File

@ -9,7 +9,7 @@
<parent>
<groupId>cn.hutool</groupId>
<artifactId>hutool-parent</artifactId>
<version>5.8.23</version>
<version>5.8.24</version>
</parent>
<artifactId>hutool-aop</artifactId>

View File

@ -9,7 +9,7 @@
<parent>
<groupId>cn.hutool</groupId>
<artifactId>hutool-parent</artifactId>
<version>5.8.23</version>
<version>5.8.24</version>
</parent>
<artifactId>hutool-bloomFilter</artifactId>

View File

@ -9,7 +9,7 @@
<parent>
<groupId>cn.hutool</groupId>
<artifactId>hutool-parent</artifactId>
<version>5.8.23</version>
<version>5.8.24</version>
</parent>
<artifactId>hutool-bom</artifactId>

View File

@ -9,7 +9,7 @@
<parent>
<groupId>cn.hutool</groupId>
<artifactId>hutool-parent</artifactId>
<version>5.8.23</version>
<version>5.8.24</version>
</parent>
<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);
/**
* 从缓存中获得对象当对象不在缓存中或已经过期返回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}
* <p>

View File

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

View File

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

View File

@ -58,6 +58,11 @@ public class NoCache<K, V> implements Cache<K, V> {
@Override
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 {
return (null == supplier) ? null : supplier.call();
} catch (Exception e) {

View File

@ -33,49 +33,12 @@ public abstract class ReentrantCache<K, V> extends AbstractCache<K, V> {
@Override
public boolean containsKey(K key) {
lock.lock();
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;
return null != getOrRemoveExpired(key, false, false);
}
@Override
public V get(K key, boolean isUpdateLastAccess) {
CacheObj<K, V> co;
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;
return getOrRemoveExpired(key, isUpdateLastAccess, true);
}
@Override
@ -102,7 +65,16 @@ public abstract class ReentrantCache<K, V> extends AbstractCache<K, V> {
@Override
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
@ -126,21 +98,37 @@ public abstract class ReentrantCache<K, V> extends AbstractCache<K, V> {
}
/**
* 移除key对应的对象
*
* @param key
* @param withMissCount 是否计数丢失数
* 获得值或清除过期值
* @param key
* @param isUpdateLastAccess 是否更新最后访问时间
* @param isUpdateCount 是否更新计数器
* @return 值或null
*/
private void remove(K key, boolean withMissCount) {
lock.lock();
private V getOrRemoveExpired(final K key, final boolean isUpdateLastAccess, final boolean isUpdateCount) {
CacheObj<K, V> co;
lock.lock();
try {
co = removeWithoutLock(key, withMissCount);
co = getWithoutLock(key);
if(null != co && co.isExpired()){
//过期移除
removeWithoutLock(key);
co = null;
}
} finally {
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;
import cn.hutool.core.collection.CopiedIter;
import cn.hutool.core.thread.ThreadUtil;
import java.util.Iterator;
import java.util.concurrent.locks.StampedLock;
@ -13,7 +14,7 @@ import java.util.concurrent.locks.StampedLock;
* @author looly
* @since 5.7.15
*/
public abstract class StampedCache<K, V> extends AbstractCache<K, V>{
public abstract class StampedCache<K, V> extends AbstractCache<K, V> {
private static final long serialVersionUID = 1L;
// 乐观锁此处使用乐观锁解决读多写少的场景
@ -33,54 +34,12 @@ public abstract class StampedCache<K, V> extends AbstractCache<K, V>{
@Override
public boolean containsKey(K key) {
final long stamp = lock.readLock();
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;
return null != get(key, false, false);
}
@Override
public V get(K key, boolean isUpdateLastAccess) {
// 尝试读取缓存使用乐观读锁
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;
return get(key, isUpdateLastAccess, true);
}
@Override
@ -107,7 +66,16 @@ public abstract class StampedCache<K, V> extends AbstractCache<K, V>{
@Override
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
@ -121,21 +89,75 @@ public abstract class StampedCache<K, V> extends AbstractCache<K, V>{
}
/**
* 移除key对应的对象
* 获取值
*
* @param key
* @param isUpdateLastAccess 是否更新最后修改时间
* @param isUpdateCount 是否更新命中数get时更新contains时不更新
* @return 值或null
*/
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 withMissCount 是否计数丢失数
* @param isUpdateCount 是否更新命中数get时更新contains时不更新
* @return 有效值或null
*/
private void remove(K key, boolean withMissCount) {
private V getOrRemoveExpired(K key, boolean isUpdateCount) {
final long stamp = lock.writeLock();
CacheObj<K, V> co;
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 {
lock.unlockWrite(stamp);
}
if (null != co) {
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>
<groupId>cn.hutool</groupId>
<artifactId>hutool-parent</artifactId>
<version>5.8.23</version>
<version>5.8.24</version>
</parent>
<artifactId>hutool-captcha</artifactId>

View File

@ -9,7 +9,7 @@
<parent>
<groupId>cn.hutool</groupId>
<artifactId>hutool-parent</artifactId>
<version>5.8.23</version>
<version>5.8.24</version>
</parent>
<artifactId>hutool-core</artifactId>

View File

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

View File

@ -244,7 +244,7 @@ public class JavaSourceCompiler {
for (Resource resource : this.sourceList) {
if (resource instanceof FileResource) {
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 {
list.add(new JavaSourceFileObject(resource.getName(), resource.getStream()));
}

View File

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

View File

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

View File

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

View File

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

View File

@ -53,7 +53,7 @@ public class FileTypeUtil {
* @return 文件类型未找到为{@code null}
*/
public static String getType(String fileStreamHexHead) {
if(MapUtil.isNotEmpty(FILE_TYPE_MAP)){
if (MapUtil.isNotEmpty(FILE_TYPE_MAP)) {
for (final Entry<String, String> fileTypeEntry : FILE_TYPE_MAP.entrySet()) {
if (StrUtil.startWithIgnoreCase(fileStreamHexHead, fileTypeEntry.getKey())) {
return fileTypeEntry.getValue();
@ -67,39 +67,44 @@ public class FileTypeUtil {
/**
* 根据文件流的头部信息获得文件类型
*
* @param in 文件流
* @param in 文件流
* @param fileHeadSize 自定义读取文件头部的大小
* @return 文件类型未找到为{@code null}
*/
public static String getType(InputStream in,int fileHeadSize) throws IORuntimeException {
return getType((IoUtil.readHex(in, fileHeadSize,false)));
public static String getType(InputStream in, int fileHeadSize) throws IORuntimeException {
return getType((IoUtil.readHex(in, fileHeadSize, false)));
}
/**
* 根据文件流的头部信息获得文件类型<br>
* 注意此方法会读取头部一些bytes造成此流接下来读取时缺少部分bytes<br>
* 因此如果想复用此流流需支持{@link InputStream#reset()}方法
* @param in {@link InputStream}
*
* @param in {@link InputStream}
* @param isExact 是否精确匹配如果为false使用前64个bytes匹配如果为true使用前8192bytes匹配
* @return 类型文件的扩展名未找到为{@code null}
* @throws IORuntimeException 读取流引起的异常
* @return 类型文件的扩展名提供的in为{@code null}未找到为{@code null}
* @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
?getType(IoUtil.readHex8192Upper(in))
:getType(IoUtil.readHex64Upper(in));
? getType(IoUtil.readHex8192Upper(in))
: getType(IoUtil.readHex64Upper(in));
}
/**
* 根据文件流的头部信息获得文件类型<br>
* 注意此方法会读取头部64个bytes造成此流接下来读取时缺少部分bytes<br>
* 因此如果想复用此流流需支持{@link InputStream#reset()}方法
*
* @param in {@link InputStream}
* @return 类型文件的扩展名未找到为{@code null}
* @throws IORuntimeException 读取流引起的异常
* @throws IORuntimeException 读取流引起的异常
*/
public static String getType(InputStream in) throws IORuntimeException {
return getType(in,false);
public static String getType(InputStream in) throws IORuntimeException {
return getType(in, false);
}
/**
@ -116,10 +121,10 @@ public class FileTypeUtil {
* @param in {@link InputStream}
* @param filename 文件名
* @return 类型文件的扩展名未找到为{@code null}
* @throws IORuntimeException 读取流引起的异常
* @throws IORuntimeException 读取流引起的异常
*/
public static String getType(InputStream in, String filename) throws IORuntimeException {
return getType(in,filename,false);
public static String getType(InputStream in, String filename) throws IORuntimeException {
return getType(in, filename, false);
}
/**
@ -132,14 +137,15 @@ public class FileTypeUtil {
* 2xlsdocmsi头信息无法区分按照扩展名区分
* 3zip可能为docxxlsxpptxjarwarofd头信息无法区分按照扩展名区分
* </pre>
*
* @param in {@link InputStream}
* @param filename 文件名
* @param isExact 是否精确匹配如果为false使用前64个bytes匹配如果为true使用前8192bytes匹配
* @param isExact 是否精确匹配如果为false使用前64个bytes匹配如果为true使用前8192bytes匹配
* @return 类型文件的扩展名未找到为{@code null}
* @throws IORuntimeException 读取流引起的异常
* @throws IORuntimeException 读取流引起的异常
*/
public static String getType(InputStream in, String filename,boolean isExact) throws IORuntimeException {
String typeName = getType(in,isExact);
public static String getType(InputStream in, String filename, boolean isExact) throws IORuntimeException {
String typeName = getType(in, isExact);
if (null == typeName) {
// 未成功识别类型扩展名辅助识别
typeName = FileUtil.extName(filename);
@ -190,19 +196,19 @@ public class FileTypeUtil {
* 3zip可能为jarwar头信息无法区分按照扩展名区分
* </pre>
*
* @param file 文件 {@link File}
* @param file 文件 {@link File}
* @param isExact 是否精确匹配如果为false使用前64个bytes匹配如果为true使用前8192bytes匹配
* @return 类型文件的扩展名未找到为{@code null}
* @throws IORuntimeException 读取文件引起的异常
* @throws IORuntimeException 读取文件引起的异常
*/
public static String getType(File file,boolean isExact) throws IORuntimeException {
if(false == FileUtil.isFile(file)){
public static String getType(File file, boolean isExact) throws IORuntimeException {
if (false == FileUtil.isFile(file)) {
throw new IllegalArgumentException("Not a regular file!");
}
FileInputStream in = null;
try {
in = IoUtil.toStream(file);
return getType(in, file.getName(),isExact);
return getType(in, file.getName(), isExact);
} finally {
IoUtil.close(in);
}
@ -219,22 +225,22 @@ public class FileTypeUtil {
*
* @param file 文件 {@link File}
* @return 类型文件的扩展名未找到为{@code null}
* @throws IORuntimeException 读取文件引起的异常
* @throws IORuntimeException 读取文件引起的异常
*/
public static String getType(File file) throws IORuntimeException {
return getType(file,false);
public static String getType(File file) throws IORuntimeException {
return getType(file, false);
}
/**
* 通过路径获得文件类型
*
* @param path 路径绝对路径或相对ClassPath的路径
* @param path 路径绝对路径或相对ClassPath的路径
* @param isExact 是否精确匹配如果为false使用前64个bytes匹配如果为true使用前8192bytes匹配
* @return 类型
* @throws IORuntimeException 读取文件引起的异常
* @throws IORuntimeException 读取文件引起的异常
*/
public static String getTypeByPath(String path,boolean isExact) throws IORuntimeException {
return getType(FileUtil.file(path),isExact);
public static String getTypeByPath(String path, boolean isExact) throws IORuntimeException {
return getType(FileUtil.file(path), isExact);
}
/**
@ -242,10 +248,10 @@ public class FileTypeUtil {
*
* @param path 路径绝对路径或相对ClassPath的路径
* @return 类型
* @throws IORuntimeException 读取文件引起的异常
* @throws IORuntimeException 读取文件引起的异常
*/
public static String getTypeByPath(String path) throws IORuntimeException {
return getTypeByPath(path,false);
public static String getTypeByPath(String path) throws IORuntimeException {
return getTypeByPath(path, false);
}

View File

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

View File

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

View File

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

View File

@ -147,6 +147,14 @@ public class URLUtil extends URLEncodeUtil {
try {
return new URL(null, url, handler);
} 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 {
return new File(url).toURI().toURL();

View File

@ -30,7 +30,7 @@ public class TestIssueI8CLBJ {
Thread thread = new Thread(() -> {
try {
String valueFieldName = annotation.valueFieldName();
System.out.println("valueFieldName:" + valueFieldName);
//Console.log("valueFieldName:" + valueFieldName);
} catch (Exception e) {
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
public void isLastDayTest() {
DateTime dateTime = DateUtil.parse("2022-09-30");
int dayOfMonth = DateUtil.getLastDayOfMonth(dateTime);
final DateTime dateTime = DateUtil.parse("2022-09-30");
final int dayOfMonth = DateUtil.getLastDayOfMonth(dateTime);
Assert.assertEquals(dayOfMonth, dateTime.dayOfMonth());
Assert.assertTrue("not is last day of this month !!", DateUtil.isLastDayOfMonth(dateTime));
}
@ -1167,4 +1167,11 @@ public class DateUtilTest {
Assert.assertNotNull(parse);
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;
import cn.hutool.core.collection.ListUtil;
import cn.hutool.core.text.StrSplitter;
import org.junit.Assert;
import org.junit.Test;
@ -15,8 +16,8 @@ public class StrSplitterTest {
@Test
public void splitByCharTest(){
String str1 = "a, ,efedsfs, ddf";
List<String> split = StrSplitter.split(str1, ',', 0, true, true);
final String str1 = "a, ,efedsfs, ddf";
final List<String> split = StrSplitter.split(str1, ',', 0, true, true);
Assert.assertEquals("ddf", split.get(2));
Assert.assertEquals(3, split.size());
@ -24,32 +25,32 @@ public class StrSplitterTest {
@Test
public void splitByStrTest(){
String str1 = "aabbccaaddaaee";
List<String> split = StrSplitter.split(str1, "aa", 0, true, true);
final String str1 = "aabbccaaddaaee";
final List<String> split = StrSplitter.split(str1, "aa", 0, true, true);
Assert.assertEquals("ee", split.get(2));
Assert.assertEquals(3, split.size());
}
@Test
public void splitByBlankTest(){
String str1 = "aa bbccaa ddaaee";
List<String> split = StrSplitter.split(str1, 0);
final String str1 = "aa bbccaa ddaaee";
final List<String> split = StrSplitter.split(str1, 0);
Assert.assertEquals("ddaaee", split.get(2));
Assert.assertEquals(3, split.size());
}
@Test
public void splitPathTest(){
String str1 = "/use/local/bin";
List<String> split = StrSplitter.splitPath(str1, 0);
final String str1 = "/use/local/bin";
final List<String> split = StrSplitter.splitPath(str1, 0);
Assert.assertEquals("bin", split.get(2));
Assert.assertEquals(3, split.size());
}
@Test
public void splitMappingTest() {
String str = "1.2.";
List<Long> split = StrSplitter.split(str, '.', 0, true, true, Long::parseLong);
final String str = "1.2.";
final List<Long> split = StrSplitter.split(str, '.', 0, true, true, Long::parseLong);
Assert.assertEquals(2, split.size());
Assert.assertEquals(Long.valueOf(1L), split.get(0));
Assert.assertEquals(Long.valueOf(2L), split.get(1));
@ -57,7 +58,7 @@ public class StrSplitterTest {
@Test
public void splitEmptyTest(){
String str = "";
final String str = "";
final String[] split = str.split(",");
final String[] strings = StrSplitter.splitToArray(str, ",", -1, false, false);
Assert.assertNotNull(strings);
@ -66,7 +67,7 @@ public class StrSplitterTest {
@Test
public void splitNullTest(){
String str = null;
final String str = null;
final String[] strings = StrSplitter.splitToArray(str, ",", -1, false, false);
Assert.assertNotNull(strings);
Assert.assertEquals(0, strings.length);
@ -77,7 +78,7 @@ public class StrSplitterTest {
*/
@Test
public void splitByRegexTest(){
String text = "01 821 34567890182345617821";
final String text = "01 821 34567890182345617821";
List<String> strings = StrSplitter.splitByRegex(text, "21", 0, false, true);
Assert.assertEquals(2, strings.size());
Assert.assertEquals("01 8", strings.get(0));
@ -89,4 +90,19 @@ public class StrSplitterTest {
Assert.assertEquals(" 345678901823456178", strings.get(1));
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.util.List;
import cn.hutool.core.lang.Console;
import cn.hutool.core.lang.Dict;
import org.junit.Assert;
import org.junit.Test;
@ -670,6 +671,20 @@ public class StrUtilTest {
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
public void truncateByByteLengthTest() {
final String str = "This is English";

View File

@ -9,7 +9,7 @@
<parent>
<groupId>cn.hutool</groupId>
<artifactId>hutool-parent</artifactId>
<version>5.8.23</version>
<version>5.8.24</version>
</parent>
<artifactId>hutool-cron</artifactId>

View File

@ -9,7 +9,7 @@
<parent>
<groupId>cn.hutool</groupId>
<artifactId>hutool-parent</artifactId>
<version>5.8.23</version>
<version>5.8.24</version>
</parent>
<artifactId>hutool-crypto</artifactId>

View File

@ -9,7 +9,7 @@
<parent>
<groupId>cn.hutool</groupId>
<artifactId>hutool-parent</artifactId>
<version>5.8.23</version>
<version>5.8.24</version>
</parent>
<artifactId>hutool-db</artifactId>

View File

@ -1,8 +1,11 @@
package cn.hutool.db;
import cn.hutool.core.lang.Assert;
import cn.hutool.core.lang.PatternPool;
import cn.hutool.core.lang.RegexPool;
import cn.hutool.core.map.MapUtil;
import cn.hutool.core.util.ArrayUtil;
import cn.hutool.core.util.ReUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.db.dialect.Dialect;
import cn.hutool.db.dialect.DialectFactory;
@ -18,6 +21,8 @@ import java.io.Serializable;
import java.sql.Connection;
import java.sql.PreparedStatement;
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);
String selectSql = sqlBuilder.build();
// 去除order by 子句
final int orderByIndex = StrUtil.lastIndexOfIgnoreCase(selectSql, " order by");
if (orderByIndex > 0) {
selectSql = StrUtil.subPre(selectSql, orderByIndex);
final Pattern pattern = PatternPool.get("(.*?)[\\s]order[\\s]by[\\s][^\\s]+\\s(asc|desc)?", Pattern.CASE_INSENSITIVE);
final Matcher matcher = pattern.matcher(selectSql);
if (matcher.matches()) {
selectSql = matcher.group(1);
}
return SqlExecutor.queryAndClosePs(dialect.psForCount(conn,
SqlBuilder.of(selectSql).addParams(sqlBuilder.getParamValueArray())),
new NumberHandler()).longValue();

View File

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

View File

@ -108,5 +108,8 @@ public interface DriverNamePool {
* JDBC 驱动 Sybase
*/
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{
private static final long serialVersionUID = 3889210427543389642L;
/**
* 构造
*/
public PostgresqlDialect() {
wrapper = new Wrapper('"');
}
@ -53,7 +56,7 @@ public class PostgresqlDialect extends AnsiSqlDialect{
final String wrapedField = (null != wrapper) ? wrapper.wrap(field) : field;
fieldsPart.append(wrapedField);
updateHolder.append(wrapedField).append("=EXCLUDED.").append(field);
updateHolder.append(wrapedField).append("=EXCLUDED.").append(wrapedField);
placeHolder.append("?");
builder.addParams(value);
}

View File

@ -9,7 +9,7 @@
<parent>
<groupId>cn.hutool</groupId>
<artifactId>hutool-parent</artifactId>
<version>5.8.23</version>
<version>5.8.24</version>
</parent>
<artifactId>hutool-dfa</artifactId>

View File

@ -9,7 +9,7 @@
<parent>
<groupId>cn.hutool</groupId>
<artifactId>hutool-parent</artifactId>
<version>5.8.23</version>
<version>5.8.24</version>
</parent>
<artifactId>hutool-extra</artifactId>
@ -394,7 +394,7 @@
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>1.2.11</version>
<version>1.4.13</version>
<scope>test</scope>
<exclusions>
<exclusion>

View File

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

View File

@ -1,5 +1,7 @@
package cn.hutool.extra.pinyin.engine.tinypinyin;
import cn.hutool.core.lang.Opt;
import cn.hutool.core.util.StrUtil;
import cn.hutool.extra.pinyin.PinyinEngine;
import com.github.promeg.pinyinhelper.Pinyin;
@ -51,7 +53,8 @@ public class TinyPinyinEngine implements PinyinEngine {
@Override
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

@ -71,8 +71,8 @@ public class SpringUtil implements BeanFactoryPostProcessor, ApplicationContextA
* @since 5.7.0
*/
public static ListableBeanFactory getBeanFactory() {
final ListableBeanFactory factory = null == beanFactory ? applicationContext : beanFactory;
if(null == factory){
final ListableBeanFactory factory = null == beanFactory ? applicationContext : beanFactory;
if (null == factory) {
throw new UtilException("No ConfigurableListableBeanFactory or ApplicationContext injected, maybe not in the Spring environment?");
}
return factory;
@ -188,6 +188,38 @@ public class SpringUtil implements BeanFactoryPostProcessor, ApplicationContextA
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;
import org.wltea.analyzer.core.IKSegmenter;
import cn.hutool.core.util.StrUtil;
import cn.hutool.extra.tokenizer.TokenizerEngine;
import cn.hutool.extra.tokenizer.Result;
import cn.hutool.extra.tokenizer.TokenizerEngine;
import org.wltea.analyzer.core.IKSegmenter;
/**
* IKAnalyzer分词引擎实现<br>
* 项目地址https://github.com/yozhao/IKAnalyzer
*
* @author looly
*
* @author looly
*/
public class IKAnalyzerEngine implements TokenizerEngine {
private final IKSegmenter seg;
/**
* 构造
*
*/
public IKAnalyzerEngine() {
this(new IKSegmenter(null, true));
}
/**
* 构造
*
*
* @param seg {@link IKSegmenter}
* @deprecated 并发问题导致无法共用IKSegmenter因此废弃
*/
@Deprecated
public IKAnalyzerEngine(IKSegmenter seg) {
this.seg = seg;
}
@Override
public Result parse(CharSequence text) {
this.seg.reset(StrUtil.getReader(text));
return new IKAnalyzerResult(this.seg);
final IKSegmenter copySeg = new IKSegmenter(null, true);
copySeg.reset(StrUtil.getReader(text));
return new IKAnalyzerResult(copySeg);
}
}

View File

@ -72,4 +72,36 @@ public class ArchiverTest {
})
.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>
<groupId>cn.hutool</groupId>
<artifactId>hutool-parent</artifactId>
<version>5.8.23</version>
<version>5.8.24</version>
</parent>
<artifactId>hutool-http</artifactId>

View File

@ -137,6 +137,7 @@ public final class HTMLFilter {
vAllowed.put("strong", no_atts);
vAllowed.put("i", no_atts);
vAllowed.put("em", no_atts);
vAllowed.put("p", no_atts);
vSelfClosingTags = new String[]{"img"};
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.io.IORuntimeException;
import cn.hutool.core.io.IoUtil;
import cn.hutool.core.io.LimitedInputStream;
import cn.hutool.core.map.CaseInsensitiveMap;
import cn.hutool.core.map.multi.ListValueMap;
import cn.hutool.core.net.NetUtil;
@ -294,7 +295,24 @@ public class HttpServerRequest extends HttpServerBase {
* @return
*/
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);
if (file.exists() && file.isFile()) {
response.write(file);
return;
}
}
} else{
final String name = request.getParam("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>
<groupId>cn.hutool</groupId>
<artifactId>hutool-parent</artifactId>
<version>5.8.23</version>
<version>5.8.24</version>
</parent>
<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>
<groupId>cn.hutool</groupId>
<artifactId>hutool-parent</artifactId>
<version>5.8.23</version>
<version>5.8.24</version>
</parent>
<artifactId>hutool-jwt</artifactId>

View File

@ -302,7 +302,18 @@ public class JWT implements RegisteredPayload<JWT> {
* @return JWT字符串
*/
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,19 +323,33 @@ public class JWT implements RegisteredPayload<JWT> {
* @return JWT字符串
*/
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!"));
// 检查tye信息
final String type = (String) this.header.getClaim(JWTHeader.TYPE);
if (StrUtil.isBlank(type)) {
this.header.setClaim(JWTHeader.TYPE, "JWT");
if (addTypeIfNot) {
final String type = (String) this.header.getClaim(JWTHeader.TYPE);
if (StrUtil.isBlank(type)) {
this.header.setClaim(JWTHeader.TYPE, "JWT");
}
}
// 检查头信息中是否有算法信息
final String algorithm = (String) this.header.getClaim(JWTHeader.ALGORITHM);
if (StrUtil.isBlank(algorithm)) {
this.header.setClaim(JWTHeader.ALGORITHM,
AlgorithmUtil.getId(signer.getAlgorithm()));
AlgorithmUtil.getId(signer.getAlgorithm()));
}
final String headerBase64 = Base64.encodeUrlSafe(this.header.toString(), charset);

View File

@ -9,7 +9,7 @@
<parent>
<groupId>cn.hutool</groupId>
<artifactId>hutool-parent</artifactId>
<version>5.8.23</version>
<version>5.8.24</version>
</parent>
<artifactId>hutool-log</artifactId>
@ -21,7 +21,7 @@
<!-- versions -->
<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>
<log4j2.version>2.20.0</log4j2.version>
<commons-logging.version>1.2</commons-logging.version>

View File

@ -9,7 +9,7 @@
<parent>
<groupId>cn.hutool</groupId>
<artifactId>hutool-parent</artifactId>
<version>5.8.23</version>
<version>5.8.24</version>
</parent>
<artifactId>hutool-poi</artifactId>

View File

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

View File

@ -9,7 +9,7 @@
<parent>
<groupId>cn.hutool</groupId>
<artifactId>hutool-parent</artifactId>
<version>5.8.23</version>
<version>5.8.24</version>
</parent>
<artifactId>hutool-script</artifactId>

View File

@ -9,7 +9,7 @@
<parent>
<groupId>cn.hutool</groupId>
<artifactId>hutool-parent</artifactId>
<version>5.8.23</version>
<version>5.8.24</version>
</parent>
<artifactId>hutool-setting</artifactId>

View File

@ -9,7 +9,7 @@
<parent>
<groupId>cn.hutool</groupId>
<artifactId>hutool-parent</artifactId>
<version>5.8.23</version>
<version>5.8.24</version>
</parent>
<artifactId>hutool-socket</artifactId>

View File

@ -9,7 +9,7 @@
<parent>
<groupId>cn.hutool</groupId>
<artifactId>hutool-parent</artifactId>
<version>5.8.23</version>
<version>5.8.24</version>
</parent>
<artifactId>hutool-system</artifactId>

View File

@ -8,7 +8,7 @@
<groupId>cn.hutool</groupId>
<artifactId>hutool-parent</artifactId>
<version>5.8.23</version>
<version>5.8.24</version>
<name>hutool</name>
<description>Hutool是一个小而全的Java工具类库通过静态方法封装降低相关API的学习成本提高工作效率使Java拥有函数式语言般的优雅让Java语言也可以“甜甜的”。</description>
<url>https://github.com/dromara/hutool</url>