diff --git a/CHANGELOG.md b/CHANGELOG.md index db857215e..55470af25 100755 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -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) diff --git a/README-EN.md b/README-EN.md index 12c571594..2f7cc2c31 100755 --- a/README-EN.md +++ b/README-EN.md @@ -47,8 +47,7 @@ -------------------------------------------------------------------------------

- - +

------------------------------------------------------------------------------- @@ -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: cn.hutool hutool-all - 5.8.23 + 5.8.24 ``` ### 🍐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 methods,Unless 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 diff --git a/README.md b/README.md index c3df8cd0b..758f7ecce 100755 --- a/README.md +++ b/README.md @@ -47,8 +47,7 @@ -------------------------------------------------------------------------------

- - +

------------------------------------------------------------------------------- @@ -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 cn.hutool hutool-all - 5.8.23 + 5.8.24 ``` ### 🍐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 diff --git a/bin/version.txt b/bin/version.txt index e1ff3f5ba..2ee3614df 100755 --- a/bin/version.txt +++ b/bin/version.txt @@ -1 +1 @@ -5.8.23 +5.8.24 diff --git a/docs/js/version.js b/docs/js/version.js index 9096578c0..08f1b8dce 100755 --- a/docs/js/version.js +++ b/docs/js/version.js @@ -1 +1 @@ -var version = '5.8.23' \ No newline at end of file +var version = '5.8.24' \ No newline at end of file diff --git a/hutool-all/pom.xml b/hutool-all/pom.xml index 9ebe69fa5..812fd1d85 100755 --- a/hutool-all/pom.xml +++ b/hutool-all/pom.xml @@ -9,7 +9,7 @@ cn.hutool hutool-parent - 5.8.23 + 5.8.24 hutool-all diff --git a/hutool-aop/pom.xml b/hutool-aop/pom.xml index 116a07ea7..e6f2eafce 100755 --- a/hutool-aop/pom.xml +++ b/hutool-aop/pom.xml @@ -9,7 +9,7 @@ cn.hutool hutool-parent - 5.8.23 + 5.8.24 hutool-aop diff --git a/hutool-bloomFilter/pom.xml b/hutool-bloomFilter/pom.xml index 972014aa9..e4512d2aa 100755 --- a/hutool-bloomFilter/pom.xml +++ b/hutool-bloomFilter/pom.xml @@ -9,7 +9,7 @@ cn.hutool hutool-parent - 5.8.23 + 5.8.24 hutool-bloomFilter diff --git a/hutool-bom/pom.xml b/hutool-bom/pom.xml index 89acf6ca8..3c62f76ed 100755 --- a/hutool-bom/pom.xml +++ b/hutool-bom/pom.xml @@ -9,7 +9,7 @@ cn.hutool hutool-parent - 5.8.23 + 5.8.24 hutool-bom diff --git a/hutool-cache/pom.xml b/hutool-cache/pom.xml index 2f4d809cd..84b8181ce 100755 --- a/hutool-cache/pom.xml +++ b/hutool-cache/pom.xml @@ -9,7 +9,7 @@ cn.hutool hutool-parent - 5.8.23 + 5.8.24 hutool-cache diff --git a/hutool-cache/src/main/java/cn/hutool/cache/Cache.java b/hutool-cache/src/main/java/cn/hutool/cache/Cache.java index ec021402e..0e07162eb 100755 --- a/hutool-cache/src/main/java/cn/hutool/cache/Cache.java +++ b/hutool-cache/src/main/java/cn/hutool/cache/Cache.java @@ -92,6 +92,21 @@ public interface Cache extends Iterable, Serializable { */ V get(K key, boolean isUpdateLastAccess, Func0 supplier); + /** + * 从缓存中获得对象,当对象不在缓存中或已经过期返回Func0回调产生的对象 + *

+ * 调用此方法时,会检查上次调用时间,如果与当前时间差值大于超时时间返回{@code null},否则返回值。 + *

+ * 每次调用此方法会可选是否刷新最后访问时间,{@code true}表示会重新计算超时时间。 + * + * @param key 键 + * @param isUpdateLastAccess 是否更新最后访问时间,即重新计算超时时间。 + * @param timeout 自定义超时时间 + * @param supplier 如果不存在回调方法,用于生产值对象 + * @return 值对象 + */ + V get(K key, boolean isUpdateLastAccess, long timeout, Func0 supplier); + /** * 从缓存中获得对象,当对象不在缓存中或已经过期返回{@code null} *

diff --git a/hutool-cache/src/main/java/cn/hutool/cache/impl/AbstractCache.java b/hutool-cache/src/main/java/cn/hutool/cache/impl/AbstractCache.java index 4ea05b1ff..56f4675e2 100755 --- a/hutool-cache/src/main/java/cn/hutool/cache/impl/AbstractCache.java +++ b/hutool-cache/src/main/java/cn/hutool/cache/impl/AbstractCache.java @@ -109,6 +109,11 @@ public abstract class AbstractCache implements Cache { @Override public V get(K key, boolean isUpdateLastAccess, Func0 supplier) { + return get(key, isUpdateLastAccess, this.timeout, supplier); + } + + @Override + public V get(K key, boolean isUpdateLastAccess, long timeout, Func0 supplier) { V v = get(key, isUpdateLastAccess); if (null == v && null != supplier) { //每个key单独获取一把锁,降低锁的粒度提高并发能力,see pr#1385@Github @@ -125,7 +130,7 @@ public abstract class AbstractCache implements Cache { 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 implements Cache { * 移除key对应的对象,不加锁 * * @param key 键 - * @param withMissCount 是否计数丢失数 * @return 移除的对象,无返回null */ - protected CacheObj removeWithoutLock(K key, boolean withMissCount) { - final CacheObj co = cacheMap.remove(MutableObj.of(key)); - if (withMissCount) { - // 在丢失计数有效的情况下,移除一般为get时的超时操作,此处应该丢失数+1 - this.missCount.increment(); - } - return co; + protected CacheObj removeWithoutLock(K key) { + return cacheMap.remove(MutableObj.of(key)); } /** diff --git a/hutool-cache/src/main/java/cn/hutool/cache/impl/FIFOCache.java b/hutool-cache/src/main/java/cn/hutool/cache/impl/FIFOCache.java index a3fc35085..071ac5067 100755 --- a/hutool-cache/src/main/java/cn/hutool/cache/impl/FIFOCache.java +++ b/hutool-cache/src/main/java/cn/hutool/cache/impl/FIFOCache.java @@ -71,7 +71,7 @@ public class FIFOCache extends StampedCache { // 清理结束后依旧是满的,则删除第一个被缓存的对象 if (isFull() && null != first) { - removeWithoutLock(first.key, false); + removeWithoutLock(first.key); onRemove(first.key, first.obj); count++; } diff --git a/hutool-cache/src/main/java/cn/hutool/cache/impl/NoCache.java b/hutool-cache/src/main/java/cn/hutool/cache/impl/NoCache.java index 553efd5e2..96bc03b98 100755 --- a/hutool-cache/src/main/java/cn/hutool/cache/impl/NoCache.java +++ b/hutool-cache/src/main/java/cn/hutool/cache/impl/NoCache.java @@ -58,6 +58,11 @@ public class NoCache implements Cache { @Override public V get(K key, boolean isUpdateLastAccess, Func0 supplier) { + return get(key, isUpdateLastAccess, 0, supplier); + } + + @Override + public V get(K key, boolean isUpdateLastAccess, long timeout, Func0 supplier) { try { return (null == supplier) ? null : supplier.call(); } catch (Exception e) { diff --git a/hutool-cache/src/main/java/cn/hutool/cache/impl/ReentrantCache.java b/hutool-cache/src/main/java/cn/hutool/cache/impl/ReentrantCache.java index e45474040..8974c4d12 100755 --- a/hutool-cache/src/main/java/cn/hutool/cache/impl/ReentrantCache.java +++ b/hutool-cache/src/main/java/cn/hutool/cache/impl/ReentrantCache.java @@ -33,49 +33,12 @@ public abstract class ReentrantCache extends AbstractCache { @Override public boolean containsKey(K key) { - lock.lock(); - try { - // 不存在或已移除 - final CacheObj 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 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 extends AbstractCache { @Override public void remove(K key) { - remove(key, false); + lock.lock(); + CacheObj 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 extends AbstractCache { } /** - * 移除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 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); } } diff --git a/hutool-cache/src/main/java/cn/hutool/cache/impl/StampedCache.java b/hutool-cache/src/main/java/cn/hutool/cache/impl/StampedCache.java index deaec6641..7727a1fbd 100755 --- a/hutool-cache/src/main/java/cn/hutool/cache/impl/StampedCache.java +++ b/hutool-cache/src/main/java/cn/hutool/cache/impl/StampedCache.java @@ -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 extends AbstractCache{ +public abstract class StampedCache extends AbstractCache { private static final long serialVersionUID = 1L; // 乐观锁,此处使用乐观锁解决读多写少的场景 @@ -33,54 +34,12 @@ public abstract class StampedCache extends AbstractCache{ @Override public boolean containsKey(K key) { - final long stamp = lock.readLock(); - try { - // 不存在或已移除 - final CacheObj 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 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 extends AbstractCache{ @Override public void remove(K key) { - remove(key, false); + final long stamp = lock.writeLock(); + CacheObj 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 extends AbstractCache{ } /** - * 移除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 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 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; } } diff --git a/hutool-cache/src/test/java/cn/hutool/cache/IssueI8MEIXTest.java b/hutool-cache/src/test/java/cn/hutool/cache/IssueI8MEIXTest.java new file mode 100644 index 000000000..4126f292f --- /dev/null +++ b/hutool-cache/src/test/java/cn/hutool/cache/IssueI8MEIXTest.java @@ -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 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); + } +} diff --git a/hutool-captcha/pom.xml b/hutool-captcha/pom.xml index 0f358a72b..433f96f48 100755 --- a/hutool-captcha/pom.xml +++ b/hutool-captcha/pom.xml @@ -9,7 +9,7 @@ cn.hutool hutool-parent - 5.8.23 + 5.8.24 hutool-captcha diff --git a/hutool-core/pom.xml b/hutool-core/pom.xml index 6403f7c59..ab1788362 100755 --- a/hutool-core/pom.xml +++ b/hutool-core/pom.xml @@ -9,7 +9,7 @@ cn.hutool hutool-parent - 5.8.23 + 5.8.24 hutool-core diff --git a/hutool-core/src/main/java/cn/hutool/core/codec/Morse.java b/hutool-core/src/main/java/cn/hutool/core/codec/Morse.java index 403ba21c3..0ed10b05b 100644 --- a/hutool-core/src/main/java/cn/hutool/core/codec/Morse.java +++ b/hutool-core/src/main/java/cn/hutool/core/codec/Morse.java @@ -10,7 +10,7 @@ import cn.hutool.core.util.StrUtil; /** * 莫尔斯电码的编码和解码实现
- * 参考:https://github.com/TakWolf/Java-MorseCoder + * 参考:https://github.com/TakWolf-Deprecated/Java-MorseCoder * * @author looly, TakWolf * @since 4.4.1 diff --git a/hutool-core/src/main/java/cn/hutool/core/compiler/JavaSourceCompiler.java b/hutool-core/src/main/java/cn/hutool/core/compiler/JavaSourceCompiler.java index b14907160..5a9f84b53 100644 --- a/hutool-core/src/main/java/cn/hutool/core/compiler/JavaSourceCompiler.java +++ b/hutool-core/src/main/java/cn/hutool/core/compiler/JavaSourceCompiler.java @@ -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())); } diff --git a/hutool-core/src/main/java/cn/hutool/core/convert/NumberWordFormatter.java b/hutool-core/src/main/java/cn/hutool/core/convert/NumberWordFormatter.java index e835593e4..e93978015 100644 --- a/hutool-core/src/main/java/cn/hutool/core/convert/NumberWordFormatter.java +++ b/hutool-core/src/main/java/cn/hutool/core/convert/NumberWordFormatter.java @@ -5,7 +5,8 @@ import cn.hutool.core.util.StrUtil; /** * 将浮点数类型的number转换成英语的表达方式
- * 参考博客:http://blog.csdn.net/eric_sunah/article/details/8713226 + * 参考博客:http://blog.csdn.net/eric_sunah/article/details/8713226
+ * 本质上此类为金额转英文表达,因此没有四舍五入考虑,小数点超过两位直接忽略。 * * @author Looly,totalo * @since 3.0.9 diff --git a/hutool-core/src/main/java/cn/hutool/core/date/DateUtil.java b/hutool-core/src/main/java/cn/hutool/core/date/DateUtil.java index a29962c3a..91badecb4 100755 --- a/hutool-core/src/main/java/cn/hutool/core/date/DateUtil.java +++ b/hutool-core/src/main/java/cn/hutool/core/date/DateUtil.java @@ -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 时间格式匹配单独解析 diff --git a/hutool-core/src/main/java/cn/hutool/core/date/LocalDateTimeUtil.java b/hutool-core/src/main/java/cn/hutool/core/date/LocalDateTimeUtil.java index bdc7de543..6a08abf9d 100755 --- a/hutool-core/src/main/java/cn/hutool/core/date/LocalDateTimeUtil.java +++ b/hutool-core/src/main/java/cn/hutool/core/date/LocalDateTimeUtil.java @@ -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) { diff --git a/hutool-core/src/main/java/cn/hutool/core/img/ImgUtil.java b/hutool-core/src/main/java/cn/hutool/core/img/ImgUtil.java index a27238a39..6c9a5e0f8 100755 --- a/hutool-core/src/main/java/cn/hutool/core/img/ImgUtil.java +++ b/hutool-core/src/main/java/cn/hutool/core/img/ImgUtil.java @@ -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:如JPG、JPEG、GIF等 * @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=》JPG、GIF=》PNG、PNG=》JPG、PNG=》GIF(X)、BMP=》PNG
+ * 此方法并不关闭流 + * + * @param srcImage 源图像流 + * @param formatName 包含格式非正式名称的 String:如JPG、JPEG、GIF等 + * @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 diff --git a/hutool-core/src/main/java/cn/hutool/core/io/FileTypeUtil.java b/hutool-core/src/main/java/cn/hutool/core/io/FileTypeUtil.java index 99bf5a9c9..cc2a83883 100644 --- a/hutool-core/src/main/java/cn/hutool/core/io/FileTypeUtil.java +++ b/hutool-core/src/main/java/cn/hutool/core/io/FileTypeUtil.java @@ -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 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))); } /** * 根据文件流的头部信息获得文件类型
* 注意此方法会读取头部一些bytes,造成此流接下来读取时缺少部分bytes
* 因此如果想复用此流,流需支持{@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)); } /** * 根据文件流的头部信息获得文件类型
* 注意此方法会读取头部64个bytes,造成此流接下来读取时缺少部分bytes
* 因此如果想复用此流,流需支持{@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 { * 2、xls、doc、msi头信息无法区分,按照扩展名区分 * 3、zip可能为docx、xlsx、pptx、jar、war、ofd头信息无法区分,按照扩展名区分 * + * * @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 { * 3、zip可能为jar、war头信息无法区分,按照扩展名区分 * * - * @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); } diff --git a/hutool-core/src/main/java/cn/hutool/core/lang/Opt.java b/hutool-core/src/main/java/cn/hutool/core/lang/Opt.java index 4dd92e76a..562bd8fc6 100644 --- a/hutool-core/src/main/java/cn/hutool/core/lang/Opt.java +++ b/hutool-core/src/main/java/cn/hutool/core/lang/Opt.java @@ -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 { * * @param 包裹里元素的类型 * @param 集合值类型 - * @param value 传入需要包裹的元素 + * @param value 传入需要包裹的元素,支持CharSequence、Map、Iterable、Iterator、Array类型 * @return 一个包裹里元素可能为空的 {@code Opt} * @since 5.7.17 */ public static > Opt ofEmptyAble(R value) { - return CollectionUtil.isEmpty(value) ? empty() : new Opt<>(value); + return ObjectUtil.isEmpty(value) ? empty() : new Opt<>(value); } /** diff --git a/hutool-core/src/main/java/cn/hutool/core/map/MapUtil.java b/hutool-core/src/main/java/cn/hutool/core/map/MapUtil.java index b76a5f514..62aa933ee 100755 --- a/hutool-core/src/main/java/cn/hutool/core/map/MapUtil.java +++ b/hutool-core/src/main/java/cn/hutool/core/map/MapUtil.java @@ -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; diff --git a/hutool-core/src/main/java/cn/hutool/core/text/CharSequenceUtil.java b/hutool-core/src/main/java/cn/hutool/core/text/CharSequenceUtil.java index 495fcc9e6..6b48d9120 100755 --- a/hutool-core/src/main/java/cn/hutool/core/text/CharSequenceUtil.java +++ b/hutool-core/src/main/java/cn/hutool/core/text/CharSequenceUtil.java @@ -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 { * *

例:

*
    - *
  • {@code StrUtil.isBlank(null) // true}
  • - *
  • {@code StrUtil.isBlank("") // true}
  • - *
  • {@code StrUtil.isBlank(" \t\n") // true}
  • - *
  • {@code StrUtil.isBlank("abc") // false}
  • + *
  • {@code CharSequenceUtil.isBlank(null) // true}
  • + *
  • {@code CharSequenceUtil.isBlank("") // true}
  • + *
  • {@code CharSequenceUtil.isBlank(" \t\n") // true}
  • + *
  • {@code CharSequenceUtil.isBlank("abc") // false}
  • *
* *

注意:该方法与 {@link #isEmpty(CharSequence)} 的区别是: @@ -111,10 +105,10 @@ public class CharSequenceUtil { * *

例:

*
    - *
  • {@code StrUtil.isNotBlank(null) // false}
  • - *
  • {@code StrUtil.isNotBlank("") // false}
  • - *
  • {@code StrUtil.isNotBlank(" \t\n") // false}
  • - *
  • {@code StrUtil.isNotBlank("abc") // true}
  • + *
  • {@code CharSequenceUtil.isNotBlank(null) // false}
  • + *
  • {@code CharSequenceUtil.isNotBlank("") // false}
  • + *
  • {@code CharSequenceUtil.isNotBlank(" \t\n") // false}
  • + *
  • {@code CharSequenceUtil.isNotBlank("abc") // true}
  • *
* *

注意:该方法与 {@link #isNotEmpty(CharSequence)} 的区别是: @@ -136,10 +130,10 @@ public class CharSequenceUtil { * *

例:

*
    - *
  • {@code StrUtil.hasBlank() // true}
  • - *
  • {@code StrUtil.hasBlank("", null, " ") // true}
  • - *
  • {@code StrUtil.hasBlank("123", " ") // true}
  • - *
  • {@code StrUtil.hasBlank("123", "abc") // false}
  • + *
  • {@code CharSequenceUtil.hasBlank() // true}
  • + *
  • {@code CharSequenceUtil.hasBlank("", null, " ") // true}
  • + *
  • {@code CharSequenceUtil.hasBlank("123", " ") // true}
  • + *
  • {@code CharSequenceUtil.hasBlank("123", "abc") // false}
  • *
* *

注意:该方法与 {@link #isAllBlank(CharSequence...)} 的区别在于:

@@ -171,10 +165,10 @@ public class CharSequenceUtil { * *

例:

*
    - *
  • {@code StrUtil.isAllBlank() // true}
  • - *
  • {@code StrUtil.isAllBlank("", null, " ") // true}
  • - *
  • {@code StrUtil.isAllBlank("123", " ") // false}
  • - *
  • {@code StrUtil.isAllBlank("123", "abc") // false}
  • + *
  • {@code CharSequenceUtil.isAllBlank() // true}
  • + *
  • {@code CharSequenceUtil.isAllBlank("", null, " ") // true}
  • + *
  • {@code CharSequenceUtil.isAllBlank("123", " ") // false}
  • + *
  • {@code CharSequenceUtil.isAllBlank("123", "abc") // false}
  • *
* *

注意:该方法与 {@link #hasBlank(CharSequence...)} 的区别在于:

@@ -208,10 +202,10 @@ public class CharSequenceUtil { * *

例:

*
    - *
  • {@code StrUtil.isEmpty(null) // true}
  • - *
  • {@code StrUtil.isEmpty("") // true}
  • - *
  • {@code StrUtil.isEmpty(" \t\n") // false}
  • - *
  • {@code StrUtil.isEmpty("abc") // false}
  • + *
  • {@code CharSequenceUtil.isEmpty(null) // true}
  • + *
  • {@code CharSequenceUtil.isEmpty("") // true}
  • + *
  • {@code CharSequenceUtil.isEmpty(" \t\n") // false}
  • + *
  • {@code CharSequenceUtil.isEmpty("abc") // false}
  • *
* *

注意:该方法与 {@link #isBlank(CharSequence)} 的区别是:该方法不校验空白字符。

@@ -238,10 +232,10 @@ public class CharSequenceUtil { * *

例:

*
    - *
  • {@code StrUtil.isNotEmpty(null) // false}
  • - *
  • {@code StrUtil.isNotEmpty("") // false}
  • - *
  • {@code StrUtil.isNotEmpty(" \t\n") // true}
  • - *
  • {@code StrUtil.isNotEmpty("abc") // true}
  • + *
  • {@code CharSequenceUtil.isNotEmpty(null) // false}
  • + *
  • {@code CharSequenceUtil.isNotEmpty("") // false}
  • + *
  • {@code CharSequenceUtil.isNotEmpty(" \t\n") // true}
  • + *
  • {@code CharSequenceUtil.isNotEmpty("abc") // true}
  • *
* *

注意:该方法与 {@link #isNotBlank(CharSequence)} 的区别是:该方法不校验空白字符。

@@ -350,11 +344,11 @@ public class CharSequenceUtil { * *

例:

*
    - *
  • {@code StrUtil.hasEmpty() // true}
  • - *
  • {@code StrUtil.hasEmpty("", null) // true}
  • - *
  • {@code StrUtil.hasEmpty("123", "") // true}
  • - *
  • {@code StrUtil.hasEmpty("123", "abc") // false}
  • - *
  • {@code StrUtil.hasEmpty(" ", "\t", "\n") // false}
  • + *
  • {@code CharSequenceUtil.hasEmpty() // true}
  • + *
  • {@code CharSequenceUtil.hasEmpty("", null) // true}
  • + *
  • {@code CharSequenceUtil.hasEmpty("123", "") // true}
  • + *
  • {@code CharSequenceUtil.hasEmpty("123", "abc") // false}
  • + *
  • {@code CharSequenceUtil.hasEmpty(" ", "\t", "\n") // false}
  • *
* *

注意:该方法与 {@link #isAllEmpty(CharSequence...)} 的区别在于:

@@ -386,11 +380,11 @@ public class CharSequenceUtil { * *

例:

*
    - *
  • {@code StrUtil.isAllEmpty() // true}
  • - *
  • {@code StrUtil.isAllEmpty("", null) // true}
  • - *
  • {@code StrUtil.isAllEmpty("123", "") // false}
  • - *
  • {@code StrUtil.isAllEmpty("123", "abc") // false}
  • - *
  • {@code StrUtil.isAllEmpty(" ", "\t", "\n") // false}
  • + *
  • {@code CharSequenceUtil.isAllEmpty() // true}
  • + *
  • {@code CharSequenceUtil.isAllEmpty("", null) // true}
  • + *
  • {@code CharSequenceUtil.isAllEmpty("123", "") // false}
  • + *
  • {@code CharSequenceUtil.isAllEmpty("123", "abc") // false}
  • + *
  • {@code CharSequenceUtil.isAllEmpty(" ", "\t", "\n") // false}
  • *
* *

注意:该方法与 {@link #hasEmpty(CharSequence...)} 的区别在于:

@@ -422,11 +416,11 @@ public class CharSequenceUtil { * *

例:

*
    - *
  • {@code StrUtil.isAllNotEmpty() // false}
  • - *
  • {@code StrUtil.isAllNotEmpty("", null) // false}
  • - *
  • {@code StrUtil.isAllNotEmpty("123", "") // false}
  • - *
  • {@code StrUtil.isAllNotEmpty("123", "abc") // true}
  • - *
  • {@code StrUtil.isAllNotEmpty(" ", "\t", "\n") // true}
  • + *
  • {@code CharSequenceUtil.isAllNotEmpty() // false}
  • + *
  • {@code CharSequenceUtil.isAllNotEmpty("", null) // false}
  • + *
  • {@code CharSequenceUtil.isAllNotEmpty("123", "") // false}
  • + *
  • {@code CharSequenceUtil.isAllNotEmpty("123", "abc") // true}
  • + *
  • {@code CharSequenceUtil.isAllNotEmpty(" ", "\t", "\n") // true}
  • *
* *

注意:该方法与 {@link #isAllEmpty(CharSequence...)} 的区别在于:

@@ -534,11 +528,11 @@ public class CharSequenceUtil { * 除去字符串头尾部的空白,如果字符串是{@code null},返回{@code ""}。 * *
-	 * 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"
 	 * 
* * @param str 字符串 @@ -553,11 +547,11 @@ public class CharSequenceUtil { * 除去字符串头尾部的空白,如果字符串是{@code null}或者"",返回{@code null}。 * *
-	 * 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"
 	 * 
* * @param str 字符串 @@ -1169,17 +1163,17 @@ public class CharSequenceUtil { * 指定范围内查找字符串,忽略大小写
* *
-	 * 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
 	 * 
* * @param str 字符串 @@ -1195,17 +1189,17 @@ public class CharSequenceUtil { * 指定范围内查找字符串 * *
-	 * 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
 	 * 
* * @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 { * 例子(*代表任意字符): * *
-	 * 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
 	 * 
* * @param str 被检查的字符串,可以为null @@ -2106,13 +2100,13 @@ public class CharSequenceUtil { * 切割指定长度的后部分的字符串 * *
-	 * 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
 	 * 
* * @param string 字符串 @@ -2156,14 +2150,14 @@ public class CharSequenceUtil { * 如果分隔字符串为空串"",则返回空串,如果分隔字符串未找到,返回原字符串,举例如下: * *
-	 * 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"
 	 * 
* * @param string 被查找的字符串 @@ -2198,12 +2192,12 @@ public class CharSequenceUtil { * 如果分隔字符串未找到,返回原字符串,举例如下: * *
-	 * 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"
 	 * 
* * @param string 被查找的字符串 @@ -2234,14 +2228,14 @@ public class CharSequenceUtil { * 如果分隔字符串为空串(null或""),则返回空串,如果分隔字符串未找到,返回空串,举例如下: * *
-	 * 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"
 	 * 
* * @param string 被查找的字符串 @@ -2272,12 +2266,12 @@ public class CharSequenceUtil { * 如果分隔字符串为空串(null或""),则返回空串,如果分隔字符串未找到,返回空串,举例如下: * *
-	 * 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)   = ""
 	 * 
* * @param string 被查找的字符串 @@ -2304,16 +2298,16 @@ public class CharSequenceUtil { * 栗子: * *
-	 * 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"
 	 * 
* * @param str 被切割的字符串 @@ -2347,12 +2341,12 @@ public class CharSequenceUtil { * 栗子: * *
-	 * 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"
 	 * 
* * @param str 被切割的字符串 @@ -2370,17 +2364,17 @@ public class CharSequenceUtil { * 栗子: * *
-	 * 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"]           重叠时只截取内部,
 	 * 
* * @param str 被切割的字符串 @@ -2425,15 +2419,15 @@ public class CharSequenceUtil { * 栗子: * *
-	 * 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"]
 	 * 
* * @param str 被切割的字符串 @@ -2452,9 +2446,9 @@ public class CharSequenceUtil { * 重复某个字符 * *
-	 * 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) = ""
 	 * 
* * @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 { * 重复某个字符串并通过分界符连接 * *
-	 * StrUtil.repeatAndJoin("?", 5, ",")   = "?,?,?,?,?"
-	 * StrUtil.repeatAndJoin("?", 0, ",")   = ""
-	 * StrUtil.repeatAndJoin("?", 5, null) = "?????"
+	 * CharSequenceUtil.repeatAndJoin("?", 5, ",")   = "?,?,?,?,?"
+	 * CharSequenceUtil.repeatAndJoin("?", 0, ",")   = ""
+	 * CharSequenceUtil.repeatAndJoin("?", 5, null) = "?????"
 	 * 
* * @param str 被重复的字符串 @@ -3071,10 +3065,10 @@ public class CharSequenceUtil { * 同:leftPad (org.apache.commons.lang3.leftPad) * *
-	 * 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"
 	 * 
* * @param str 字符串 @@ -3102,9 +3096,9 @@ public class CharSequenceUtil { * 同:leftPad (org.apache.commons.lang3.leftPad) * *
-	 * 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"
 	 * 
* * @param str 字符串 @@ -3131,10 +3125,10 @@ public class CharSequenceUtil { * 补充字符串以满足最小长度,如果提供的字符串大于指定长度,截断之 * *
-	 * 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')//"" 空串
 	 * 
* * @param str 字符串,如果为{@code null},直接返回null @@ -3161,9 +3155,9 @@ public class CharSequenceUtil { * 补充字符串以满足最小长度 * *
-	 * 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"
 	 * 
* * @param str 字符串,如果为{@code null},直接返回null @@ -3193,12 +3187,12 @@ public class CharSequenceUtil { * 居中字符串,两边补充指定字符串,如果指定长度小于字符串,则返回原字符串 * *
-	 * 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  "
 	 * 
* * @param str 字符串 @@ -3214,14 +3208,14 @@ public class CharSequenceUtil { * 居中字符串,两边补充指定字符串,如果指定长度小于字符串,则返回原字符串 * *
-	 * 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  "
 	 * 
* * @param str 字符串 @@ -3248,15 +3242,15 @@ public class CharSequenceUtil { * 居中字符串,两边补充指定字符串,如果指定长度小于字符串,则返回原字符串 * *
-	 * 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  "
 	 * 
* * @param str 字符串 @@ -3300,13 +3294,13 @@ public class CharSequenceUtil { * 参数为 {@code null} 或者 "" 返回 {@code 0}. * *
-	 * 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
 	 * 
* * @param content 被查找的字符串 @@ -3356,16 +3350,16 @@ public class CharSequenceUtil { * 比较两个字符串,用于排序 * *
-	 * StrUtil.compare(null, null, *)     = 0
-	 * StrUtil.compare(null , "a", true)  < 0
-	 * StrUtil.compare(null , "a", false) > 0
-	 * StrUtil.compare("a", null, true)   > 0
-	 * StrUtil.compare("a", null, false)  < 0
-	 * StrUtil.compare("abc", "abc", *)   = 0
-	 * StrUtil.compare("a", "b", *)       < 0
-	 * StrUtil.compare("b", "a", *)       > 0
-	 * StrUtil.compare("a", "B", *)       > 0
-	 * StrUtil.compare("ab", "abc", *)    < 0
+	 * CharSequenceUtil.compare(null, null, *)     = 0
+	 * CharSequenceUtil.compare(null , "a", true)  < 0
+	 * CharSequenceUtil.compare(null , "a", false) > 0
+	 * CharSequenceUtil.compare("a", null, true)   > 0
+	 * CharSequenceUtil.compare("a", null, false)  < 0
+	 * CharSequenceUtil.compare("abc", "abc", *)   = 0
+	 * CharSequenceUtil.compare("a", "b", *)       < 0
+	 * CharSequenceUtil.compare("b", "a", *)       > 0
+	 * CharSequenceUtil.compare("a", "B", *)       > 0
+	 * CharSequenceUtil.compare("ab", "abc", *)    < 0
 	 * 
* * @param str1 字符串1 @@ -3390,18 +3384,18 @@ public class CharSequenceUtil { * 比较两个字符串,用于排序,大小写不敏感 * *
-	 * StrUtil.compareIgnoreCase(null, null, *)     = 0
-	 * StrUtil.compareIgnoreCase(null , "a", true)  < 0
-	 * StrUtil.compareIgnoreCase(null , "a", false) > 0
-	 * StrUtil.compareIgnoreCase("a", null, true)   > 0
-	 * StrUtil.compareIgnoreCase("a", null, false)  < 0
-	 * StrUtil.compareIgnoreCase("abc", "abc", *)   = 0
-	 * StrUtil.compareIgnoreCase("abc", "ABC", *)   = 0
-	 * StrUtil.compareIgnoreCase("a", "b", *)       < 0
-	 * StrUtil.compareIgnoreCase("b", "a", *)       > 0
-	 * StrUtil.compareIgnoreCase("a", "B", *)       < 0
-	 * StrUtil.compareIgnoreCase("A", "b", *)       < 0
-	 * StrUtil.compareIgnoreCase("ab", "abc", *)    < 0
+	 * CharSequenceUtil.compareIgnoreCase(null, null, *)     = 0
+	 * CharSequenceUtil.compareIgnoreCase(null , "a", true)  < 0
+	 * CharSequenceUtil.compareIgnoreCase(null , "a", false) > 0
+	 * CharSequenceUtil.compareIgnoreCase("a", null, true)   > 0
+	 * CharSequenceUtil.compareIgnoreCase("a", null, false)  < 0
+	 * CharSequenceUtil.compareIgnoreCase("abc", "abc", *)   = 0
+	 * CharSequenceUtil.compareIgnoreCase("abc", "ABC", *)   = 0
+	 * CharSequenceUtil.compareIgnoreCase("a", "b", *)       < 0
+	 * CharSequenceUtil.compareIgnoreCase("b", "a", *)       > 0
+	 * CharSequenceUtil.compareIgnoreCase("a", "B", *)       < 0
+	 * CharSequenceUtil.compareIgnoreCase("A", "b", *)       < 0
+	 * CharSequenceUtil.compareIgnoreCase("ab", "abc", *)    < 0
 	 * 
* * @param str1 字符串1 @@ -3427,14 +3421,14 @@ public class CharSequenceUtil { * null版本排在最小:即: * *
-	 * StrUtil.compareVersion(null, "v1") < 0
-	 * StrUtil.compareVersion("v1", "v1")  = 0
-	 * StrUtil.compareVersion(null, null)   = 0
-	 * StrUtil.compareVersion("v1", null) > 0
-	 * StrUtil.compareVersion("1.0.0", "1.0.2") < 0
-	 * StrUtil.compareVersion("1.0.2", "1.0.2a") < 0
-	 * StrUtil.compareVersion("1.13.0", "1.12.1c") > 0
-	 * StrUtil.compareVersion("V0.0.20170102", "V0.0.20170101") > 0
+	 * CharSequenceUtil.compareVersion(null, "v1") < 0
+	 * CharSequenceUtil.compareVersion("v1", "v1")  = 0
+	 * CharSequenceUtil.compareVersion(null, null)   = 0
+	 * CharSequenceUtil.compareVersion("v1", null) > 0
+	 * CharSequenceUtil.compareVersion("1.0.0", "1.0.2") < 0
+	 * CharSequenceUtil.compareVersion("1.0.2", "1.0.2a") < 0
+	 * CharSequenceUtil.compareVersion("1.13.0", "1.12.1c") > 0
+	 * CharSequenceUtil.compareVersion("V0.0.20170102", "V0.0.20170101") > 0
 	 * 
* * @param version1 版本1 @@ -3828,13 +3822,13 @@ public class CharSequenceUtil { * 俗称:脱敏功能,后面其他功能,可以见:DesensitizedUtil(脱敏工具类) * *
-	 * 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
 	 * 
* * @param str 字符串 @@ -3851,16 +3845,16 @@ public class CharSequenceUtil { * 脱敏,使用默认的脱敏策略 * *
-	 * 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"
 	 * 
* * @param str 字符串 @@ -4005,7 +3999,7 @@ public class CharSequenceUtil { */ @SuppressWarnings("unchecked") public static 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 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 { * 切换给定字符串中的大小写。大写转小写,小写转大写。 * *
-	 * 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"
 	 * 
* * @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 matcher) { - if (StrUtil.isBlank(value)) { + if (CharSequenceUtil.isBlank(value)) { return false; } for (int i = value.length(); --i >= 0; ) { diff --git a/hutool-core/src/main/java/cn/hutool/core/text/finder/PatternFinder.java b/hutool-core/src/main/java/cn/hutool/core/text/finder/PatternFinder.java index c258daed0..397993086 100644 --- a/hutool-core/src/main/java/cn/hutool/core/text/finder/PatternFinder.java +++ b/hutool-core/src/main/java/cn/hutool/core/text/finder/PatternFinder.java @@ -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; diff --git a/hutool-core/src/main/java/cn/hutool/core/util/NumberUtil.java b/hutool-core/src/main/java/cn/hutool/core/util/NumberUtil.java index 87040d6cb..e5d748821 100755 --- a/hutool-core/src/main/java/cn/hutool/core/util/NumberUtil.java +++ b/hutool-core/src/main/java/cn/hutool/core/util/NumberUtil.java @@ -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; diff --git a/hutool-core/src/main/java/cn/hutool/core/util/StrUtil.java b/hutool-core/src/main/java/cn/hutool/core/util/StrUtil.java index 2ccd49633..5695c7c23 100755 --- a/hutool-core/src/main/java/cn/hutool/core/util/StrUtil.java +++ b/hutool-core/src/main/java/cn/hutool/core/util/StrUtil.java @@ -488,7 +488,9 @@ public class StrUtil extends CharSequenceUtil implements StrPool { } /** - * 截断字符串,使用其按照指定编码为字节后不超过maxBytes长度 + * 截断字符串,使用其按照指定编码为字节后不超过maxBytes长度
+ * 此方法用于截取总bytes数不超过指定长度,如果字符出没有超出原样输出,如果超出了,则截取掉超出部分,并可选添加..., + * 但是添加“...”后总长度也不超过限制长度。 * * @param str 原始字符串 * @param charset 指定编码 diff --git a/hutool-core/src/main/java/cn/hutool/core/util/URLUtil.java b/hutool-core/src/main/java/cn/hutool/core/util/URLUtil.java index a66463e67..f4d384a61 100644 --- a/hutool-core/src/main/java/cn/hutool/core/util/URLUtil.java +++ b/hutool-core/src/main/java/cn/hutool/core/util/URLUtil.java @@ -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(); diff --git a/hutool-core/src/test/java/cn/hutool/core/annotation/TestIssueI8CLBJ.java b/hutool-core/src/test/java/cn/hutool/core/annotation/TestIssueI8CLBJ.java index 04c2a9267..fd80af15d 100644 --- a/hutool-core/src/test/java/cn/hutool/core/annotation/TestIssueI8CLBJ.java +++ b/hutool-core/src/test/java/cn/hutool/core/annotation/TestIssueI8CLBJ.java @@ -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(); } diff --git a/hutool-core/src/test/java/cn/hutool/core/bean/IssueI8JASOTest.java b/hutool-core/src/test/java/cn/hutool/core/bean/IssueI8JASOTest.java new file mode 100644 index 000000000..93bc9753d --- /dev/null +++ b/hutool-core/src/test/java/cn/hutool/core/bean/IssueI8JASOTest.java @@ -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; + } +} diff --git a/hutool-core/src/test/java/cn/hutool/core/date/DateUtilTest.java b/hutool-core/src/test/java/cn/hutool/core/date/DateUtilTest.java index 776615120..02c72ae41 100755 --- a/hutool-core/src/test/java/cn/hutool/core/date/DateUtilTest.java +++ b/hutool-core/src/test/java/cn/hutool/core/date/DateUtilTest.java @@ -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()); + } } diff --git a/hutool-core/src/test/java/cn/hutool/core/img/IssueI8L8UATest.java b/hutool-core/src/test/java/cn/hutool/core/img/IssueI8L8UATest.java new file mode 100644 index 000000000..70fcc059c --- /dev/null +++ b/hutool-core/src/test/java/cn/hutool/core/img/IssueI8L8UATest.java @@ -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")); + } +} diff --git a/hutool-core/src/test/java/cn/hutool/core/text/split/StrSplitterTest.java b/hutool-core/src/test/java/cn/hutool/core/text/split/StrSplitterTest.java index 16f812f69..8498bf479 100644 --- a/hutool-core/src/test/java/cn/hutool/core/text/split/StrSplitterTest.java +++ b/hutool-core/src/test/java/cn/hutool/core/text/split/StrSplitterTest.java @@ -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 split = StrSplitter.split(str1, ',', 0, true, true); + final String str1 = "a, ,efedsfs, ddf"; + final List 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 split = StrSplitter.split(str1, "aa", 0, true, true); + final String str1 = "aabbccaaddaaee"; + final List 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 split = StrSplitter.split(str1, 0); + final String str1 = "aa bbccaa ddaaee"; + final List 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 split = StrSplitter.splitPath(str1, 0); + final String str1 = "/use/local/bin"; + final List 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 split = StrSplitter.split(str, '.', 0, true, true, Long::parseLong); + final String str = "1.2."; + final List 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 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 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); + } } diff --git a/hutool-core/src/test/java/cn/hutool/core/util/StrUtilTest.java b/hutool-core/src/test/java/cn/hutool/core/util/StrUtilTest.java index 558ae6b24..d1e1ca03b 100755 --- a/hutool-core/src/test/java/cn/hutool/core/util/StrUtilTest.java +++ b/hutool-core/src/test/java/cn/hutool/core/util/StrUtilTest.java @@ -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"; diff --git a/hutool-cron/pom.xml b/hutool-cron/pom.xml index c632c4b69..36ed1823c 100755 --- a/hutool-cron/pom.xml +++ b/hutool-cron/pom.xml @@ -9,7 +9,7 @@ cn.hutool hutool-parent - 5.8.23 + 5.8.24 hutool-cron diff --git a/hutool-crypto/pom.xml b/hutool-crypto/pom.xml index d48d44ec2..4bb3ca56f 100755 --- a/hutool-crypto/pom.xml +++ b/hutool-crypto/pom.xml @@ -9,7 +9,7 @@ cn.hutool hutool-parent - 5.8.23 + 5.8.24 hutool-crypto diff --git a/hutool-db/pom.xml b/hutool-db/pom.xml index f4d7b10b8..b6e87daa5 100755 --- a/hutool-db/pom.xml +++ b/hutool-db/pom.xml @@ -9,7 +9,7 @@ cn.hutool hutool-parent - 5.8.23 + 5.8.24 hutool-db diff --git a/hutool-db/src/main/java/cn/hutool/db/DialectRunner.java b/hutool-db/src/main/java/cn/hutool/db/DialectRunner.java index a891f3ba8..c0c7906e6 100644 --- a/hutool-db/src/main/java/cn/hutool/db/DialectRunner.java +++ b/hutool-db/src/main/java/cn/hutool/db/DialectRunner.java @@ -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(); diff --git a/hutool-db/src/main/java/cn/hutool/db/dialect/DialectFactory.java b/hutool-db/src/main/java/cn/hutool/db/dialect/DialectFactory.java index 0e439019a..6dfea6ae5 100755 --- a/hutool-db/src/main/java/cn/hutool/db/dialect/DialectFactory.java +++ b/hutool-db/src/main/java/cn/hutool/db/dialect/DialectFactory.java @@ -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; diff --git a/hutool-db/src/main/java/cn/hutool/db/dialect/DriverNamePool.java b/hutool-db/src/main/java/cn/hutool/db/dialect/DriverNamePool.java index 51cdec818..aab7a8aca 100644 --- a/hutool-db/src/main/java/cn/hutool/db/dialect/DriverNamePool.java +++ b/hutool-db/src/main/java/cn/hutool/db/dialect/DriverNamePool.java @@ -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"; } diff --git a/hutool-db/src/main/java/cn/hutool/db/dialect/impl/PostgresqlDialect.java b/hutool-db/src/main/java/cn/hutool/db/dialect/impl/PostgresqlDialect.java index 0a5e0fc7d..a7bd189e5 100644 --- a/hutool-db/src/main/java/cn/hutool/db/dialect/impl/PostgresqlDialect.java +++ b/hutool-db/src/main/java/cn/hutool/db/dialect/impl/PostgresqlDialect.java @@ -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); } diff --git a/hutool-dfa/pom.xml b/hutool-dfa/pom.xml index a467e88c0..7a49cd226 100755 --- a/hutool-dfa/pom.xml +++ b/hutool-dfa/pom.xml @@ -9,7 +9,7 @@ cn.hutool hutool-parent - 5.8.23 + 5.8.24 hutool-dfa diff --git a/hutool-extra/pom.xml b/hutool-extra/pom.xml index 086d12468..c817a1d25 100755 --- a/hutool-extra/pom.xml +++ b/hutool-extra/pom.xml @@ -9,7 +9,7 @@ cn.hutool hutool-parent - 5.8.23 + 5.8.24 hutool-extra @@ -394,7 +394,7 @@ ch.qos.logback logback-classic - 1.2.11 + 1.4.13 test diff --git a/hutool-extra/src/main/java/cn/hutool/extra/compress/archiver/StreamArchiver.java b/hutool-extra/src/main/java/cn/hutool/extra/compress/archiver/StreamArchiver.java index 5372b7381..99d5e38b1 100755 --- a/hutool-extra/src/main/java/cn/hutool/extra/compress/archiver/StreamArchiver.java +++ b/hutool-extra/src/main/java/cn/hutool/extra/compress/archiver/StreamArchiver.java @@ -177,6 +177,8 @@ public class StreamArchiver implements Archiver { for (File childFile : files) { addInternal(childFile, entryName, filter); } + } else { + out.closeArchiveEntry(); } } else { if (file.isFile()) { diff --git a/hutool-extra/src/main/java/cn/hutool/extra/pinyin/engine/tinypinyin/TinyPinyinEngine.java b/hutool-extra/src/main/java/cn/hutool/extra/pinyin/engine/tinypinyin/TinyPinyinEngine.java index edc678cae..2ecf0dd1c 100644 --- a/hutool-extra/src/main/java/cn/hutool/extra/pinyin/engine/tinypinyin/TinyPinyinEngine.java +++ b/hutool-extra/src/main/java/cn/hutool/extra/pinyin/engine/tinypinyin/TinyPinyinEngine.java @@ -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(); } } diff --git a/hutool-extra/src/main/java/cn/hutool/extra/spring/SpringUtil.java b/hutool-extra/src/main/java/cn/hutool/extra/spring/SpringUtil.java index 203888014..abbdbda40 100755 --- a/hutool-extra/src/main/java/cn/hutool/extra/spring/SpringUtil.java +++ b/hutool-extra/src/main/java/cn/hutool/extra/spring/SpringUtil.java @@ -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 属性值类型 + * @param key 配置项key + * @param targetType 配置项类型 + * @param defaultValue 默认值 + * @return 属性值 + * @since 5.8.24 + */ + public static T getProperty(String key, Class targetType, T defaultValue) { + if (null == applicationContext) { + return null; + } + return applicationContext.getEnvironment().getProperty(key, targetType, defaultValue); + } + /** * 获取应用程序名称 * diff --git a/hutool-extra/src/main/java/cn/hutool/extra/tokenizer/engine/ikanalyzer/IKAnalyzerEngine.java b/hutool-extra/src/main/java/cn/hutool/extra/tokenizer/engine/ikanalyzer/IKAnalyzerEngine.java index 6cb8c2e7a..a030268fe 100644 --- a/hutool-extra/src/main/java/cn/hutool/extra/tokenizer/engine/ikanalyzer/IKAnalyzerEngine.java +++ b/hutool-extra/src/main/java/cn/hutool/extra/tokenizer/engine/ikanalyzer/IKAnalyzerEngine.java @@ -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分词引擎实现
* 项目地址: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); } } diff --git a/hutool-extra/src/test/java/cn/hutool/extra/compress/ArchiverTest.java b/hutool-extra/src/test/java/cn/hutool/extra/compress/ArchiverTest.java index 8ba8d499a..3175efc3e 100644 --- a/hutool-extra/src/test/java/cn/hutool/extra/compress/ArchiverTest.java +++ b/hutool-extra/src/test/java/cn/hutool/extra/compress/ArchiverTest.java @@ -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(); + } } diff --git a/hutool-http/pom.xml b/hutool-http/pom.xml index 94271dac0..b55ae1617 100755 --- a/hutool-http/pom.xml +++ b/hutool-http/pom.xml @@ -9,7 +9,7 @@ cn.hutool hutool-parent - 5.8.23 + 5.8.24 hutool-http diff --git a/hutool-http/src/main/java/cn/hutool/http/HTMLFilter.java b/hutool-http/src/main/java/cn/hutool/http/HTMLFilter.java index ffed78541..3bfaee3a5 100644 --- a/hutool-http/src/main/java/cn/hutool/http/HTMLFilter.java +++ b/hutool-http/src/main/java/cn/hutool/http/HTMLFilter.java @@ -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"}; diff --git a/hutool-http/src/main/java/cn/hutool/http/server/HttpServerRequest.java b/hutool-http/src/main/java/cn/hutool/http/server/HttpServerRequest.java index 05555f58a..0872dadc6 100644 --- a/hutool-http/src/main/java/cn/hutool/http/server/HttpServerRequest.java +++ b/hutool-http/src/main/java/cn/hutool/http/server/HttpServerRequest.java @@ -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; } /** diff --git a/hutool-http/src/main/java/cn/hutool/http/server/action/RootAction.java b/hutool-http/src/main/java/cn/hutool/http/server/action/RootAction.java index 8c10bd998..15ea39630 100644 --- a/hutool-http/src/main/java/cn/hutool/http/server/action/RootAction.java +++ b/hutool-http/src/main/java/cn/hutool/http/server/action/RootAction.java @@ -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; } } diff --git a/hutool-http/src/test/java/cn/hutool/http/HTMLFilterTest.java b/hutool-http/src/test/java/cn/hutool/http/HTMLFilterTest.java new file mode 100644 index 000000000..03045a9a7 --- /dev/null +++ b/hutool-http/src/test/java/cn/hutool/http/HTMLFilterTest.java @@ -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 = "

a

"; + String p2 = "

a

"; + + final HTMLFilter htmlFilter = new HTMLFilter(); + String filter = htmlFilter.filter(p1); + Assert.assertEquals("

a

", filter); + + filter = htmlFilter.filter(p2); + Assert.assertEquals("

a

", filter); + } +} diff --git a/hutool-json/pom.xml b/hutool-json/pom.xml index 9135f1845..894451b6b 100755 --- a/hutool-json/pom.xml +++ b/hutool-json/pom.xml @@ -9,7 +9,7 @@ cn.hutool hutool-parent - 5.8.23 + 5.8.24 hutool-json diff --git a/hutool-json/src/test/java/cn/hutool/json/IssueI8NMP7Test.java b/hutool-json/src/test/java/cn/hutool/json/IssueI8NMP7Test.java new file mode 100644 index 000000000..6bd9db836 --- /dev/null +++ b/hutool-json/src/test/java/cn/hutool/json/IssueI8NMP7Test.java @@ -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; + } +} diff --git a/hutool-json/src/test/java/cn/hutool/json/IssueI8PC9FTest.java b/hutool-json/src/test/java/cn/hutool/json/IssueI8PC9FTest.java new file mode 100644 index 000000000..393fe3371 --- /dev/null +++ b/hutool-json/src/test/java/cn/hutool/json/IssueI8PC9FTest.java @@ -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; + } +} diff --git a/hutool-jwt/pom.xml b/hutool-jwt/pom.xml index 8a86ab83e..49224ccdf 100755 --- a/hutool-jwt/pom.xml +++ b/hutool-jwt/pom.xml @@ -9,7 +9,7 @@ cn.hutool hutool-parent - 5.8.23 + 5.8.24 hutool-jwt diff --git a/hutool-jwt/src/main/java/cn/hutool/jwt/JWT.java b/hutool-jwt/src/main/java/cn/hutool/jwt/JWT.java index e7214aa48..1d511d74c 100755 --- a/hutool-jwt/src/main/java/cn/hutool/jwt/JWT.java +++ b/hutool-jwt/src/main/java/cn/hutool/jwt/JWT.java @@ -302,7 +302,18 @@ public class JWT implements RegisteredPayload { * @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 { * @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); diff --git a/hutool-log/pom.xml b/hutool-log/pom.xml index 62a5d636e..63b568a64 100755 --- a/hutool-log/pom.xml +++ b/hutool-log/pom.xml @@ -9,7 +9,7 @@ cn.hutool hutool-parent - 5.8.23 + 5.8.24 hutool-log @@ -21,7 +21,7 @@ 1.7.36 - 1.4.6 + 1.4.13 1.2.17 2.20.0 1.2 diff --git a/hutool-poi/pom.xml b/hutool-poi/pom.xml index c439d0e54..808fc8f62 100755 --- a/hutool-poi/pom.xml +++ b/hutool-poi/pom.xml @@ -9,7 +9,7 @@ cn.hutool hutool-parent - 5.8.23 + 5.8.24 hutool-poi diff --git a/hutool-poi/src/main/java/cn/hutool/poi/excel/ExcelBase.java b/hutool-poi/src/main/java/cn/hutool/poi/excel/ExcelBase.java index 0e0481b7e..c86c134d6 100644 --- a/hutool-poi/src/main/java/cn/hutool/poi/excel/ExcelBase.java +++ b/hutool-poi/src/main/java/cn/hutool/poi/excel/ExcelBase.java @@ -189,7 +189,8 @@ public class ExcelBase> implements Closeable { sheet = workbook.cloneSheet(sheetIndex, newSheetName); } else { sheet = this.workbook.cloneSheet(sheetIndex); - this.workbook.setSheetName(sheetIndex, newSheetName); + // issue#I8QIBB,clone后的sheet的index应该重新获取 + this.workbook.setSheetName(workbook.getSheetIndex(sheet), newSheetName); } if (setAsCurrentSheet) { this.sheet = sheet; diff --git a/hutool-script/pom.xml b/hutool-script/pom.xml index a19f01d66..7959a5a86 100755 --- a/hutool-script/pom.xml +++ b/hutool-script/pom.xml @@ -9,7 +9,7 @@ cn.hutool hutool-parent - 5.8.23 + 5.8.24 hutool-script diff --git a/hutool-setting/pom.xml b/hutool-setting/pom.xml index 0d97a98a6..eb196f1cc 100755 --- a/hutool-setting/pom.xml +++ b/hutool-setting/pom.xml @@ -9,7 +9,7 @@ cn.hutool hutool-parent - 5.8.23 + 5.8.24 hutool-setting diff --git a/hutool-socket/pom.xml b/hutool-socket/pom.xml index ea8e7c2bc..2c7651f8e 100755 --- a/hutool-socket/pom.xml +++ b/hutool-socket/pom.xml @@ -9,7 +9,7 @@ cn.hutool hutool-parent - 5.8.23 + 5.8.24 hutool-socket diff --git a/hutool-system/pom.xml b/hutool-system/pom.xml index f17be3d03..7133dcc2d 100755 --- a/hutool-system/pom.xml +++ b/hutool-system/pom.xml @@ -9,7 +9,7 @@ cn.hutool hutool-parent - 5.8.23 + 5.8.24 hutool-system diff --git a/pom.xml b/pom.xml index b2dc0732a..d1ab97eb3 100755 --- a/pom.xml +++ b/pom.xml @@ -8,7 +8,7 @@ cn.hutool hutool-parent - 5.8.23 + 5.8.24 hutool Hutool是一个小而全的Java工具类库,通过静态方法封装,降低相关API的学习成本,提高工作效率,使Java拥有函数式语言般的优雅,让Java语言也可以“甜甜的”。 https://github.com/dromara/hutool