diff --git a/CHANGELOG.md b/CHANGELOG.md index a5a4d3014..fd6379237 100755 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,7 +2,7 @@ # 🚀Changelog ------------------------------------------------------------------------------------------------------------- -# 5.8.41(2025-09-29) +# 5.8.41(2025-10-10) ### 🐣新特性 * 【core 】 增加`WeakKeyValueConcurrentMap`及其关联类,同时废弃`WeakConcurrentMap`并替换(issue#4039@Github) @@ -33,6 +33,9 @@ * 【extra 】 修复`JschSessionPool`并发问题(pr#4079@Github) * 【extra 】 修复`Sftp`递归删除目录时使用相对路径可能导致死循环的问题(pr#1380@Gitee) * 【db 】 修复`SqlUtil.removeOuterOrderBy`处理没有order by的语句导致异常问题(pr#4089@Github) +* 【extra 】 修复`Sftp.upload`目标路径为null时空指针问题(issue#ID14WX@Gitee) +* 【ai 】 修复`AIConfigBuilder`中方法名拼写错误(pr#1382@Gitee) +* 【core 】 修复`StrBuilder`charAt越界判断错误(pr#4094@Github) ------------------------------------------------------------------------------------------------------------- # 5.8.40(2025-08-26) diff --git a/hutool-ai/src/main/java/cn/hutool/ai/core/AIConfigBuilder.java b/hutool-ai/src/main/java/cn/hutool/ai/core/AIConfigBuilder.java index 4065056ac..bedd79071 100644 --- a/hutool-ai/src/main/java/cn/hutool/ai/core/AIConfigBuilder.java +++ b/hutool-ai/src/main/java/cn/hutool/ai/core/AIConfigBuilder.java @@ -112,8 +112,21 @@ public class AIConfigBuilder { * @param timeout 超时时间 * @return config * @since 5.8.39 + * @deprecated 请使用 {@link #setTimeout(int)} */ - public synchronized AIConfigBuilder setTimout(final int timeout) { + @Deprecated + public AIConfigBuilder setTimout(final int timeout) { + return setTimeout(timeout); + } + + /** + * 设置连接超时时间,不设置为默认值 + * + * @param timeout 超时时间 + * @return config + * @since 5.8.41 + */ + public synchronized AIConfigBuilder setTimeout(final int timeout) { if (timeout > 0) { config.setTimeout(timeout); } @@ -126,10 +139,23 @@ public class AIConfigBuilder { * @param readTimout 取超时时间 * @return config * @since 5.8.39 + * @deprecated 请使用 {@link #setReadTimeout(int)} */ - public synchronized AIConfigBuilder setReadTimout(final int readTimout) { - if (readTimout > 0) { - config.setReadTimeout(readTimout); + @Deprecated + public AIConfigBuilder setReadTimout(final int readTimout) { + return setReadTimeout(readTimout); + } + + /** + * 设置读取超时时间,不设置为默认值 + * + * @param readTimeout 取超时时间 + * @return config + * @since 5.8.41 + */ + public synchronized AIConfigBuilder setReadTimeout(final int readTimeout) { + if (readTimeout > 0) { + config.setReadTimeout(readTimeout); } return this; } diff --git a/hutool-core/src/main/java/cn/hutool/core/text/StrBuilder.java b/hutool-core/src/main/java/cn/hutool/core/text/StrBuilder.java index 305da2923..87e299f90 100644 --- a/hutool-core/src/main/java/cn/hutool/core/text/StrBuilder.java +++ b/hutool-core/src/main/java/cn/hutool/core/text/StrBuilder.java @@ -486,7 +486,7 @@ public class StrBuilder implements CharSequence, Appendable, Serializable { if(index < 0){ index = this.position + index; } - if ((index < 0) || (index > this.position)) { + if ((index < 0) || (index >= this.position)) { throw new StringIndexOutOfBoundsException(index); } return this.value[index]; diff --git a/hutool-core/src/test/java/cn/hutool/core/text/StrBuilderTest.java b/hutool-core/src/test/java/cn/hutool/core/text/StrBuilderTest.java index 474a71143..04a277af4 100644 --- a/hutool-core/src/test/java/cn/hutool/core/text/StrBuilderTest.java +++ b/hutool-core/src/test/java/cn/hutool/core/text/StrBuilderTest.java @@ -131,4 +131,13 @@ public class StrBuilderTest { helloWorld.insert(6, "Beautiful "); Assertions.assertEquals("Hello Beautiful World", helloWorld.toString()); } + + @Test + void charAtTest() { + final StrBuilder helloWorld = StrBuilder.create("Hello World"); + Assertions.assertEquals('d', helloWorld.charAt(-1)); + Assertions.assertEquals('H', helloWorld.charAt(0)); + Assertions.assertEquals('d', helloWorld.charAt(10)); + Assertions.assertThrows(StringIndexOutOfBoundsException.class, () -> helloWorld.charAt(11));; + } } 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 81435d3e7..e3228d931 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 @@ -1,5 +1,6 @@ package cn.hutool.core.util; +import cn.hutool.core.lang.Console; import cn.hutool.core.lang.Dict; import org.junit.jupiter.api.Test; @@ -633,14 +634,14 @@ public class StrUtilTest { public void replaceLastTest() { final String str = "i am jackjack"; final String result = StrUtil.replaceLast(str, "JACK", null, true); - assertEquals(result, "i am jack"); + assertEquals("i am jack", result); } @Test public void replaceFirstTest() { final String str = "yesyes i do"; final String result = StrUtil.replaceFirst(str, "YES", "", true); - assertEquals(result, "yes i do"); + assertEquals("yes i do", result); } @Test diff --git a/hutool-extra/src/main/java/cn/hutool/extra/ssh/Sftp.java b/hutool-extra/src/main/java/cn/hutool/extra/ssh/Sftp.java index 0fa65b9e5..133e294c7 100755 --- a/hutool-extra/src/main/java/cn/hutool/extra/ssh/Sftp.java +++ b/hutool-extra/src/main/java/cn/hutool/extra/ssh/Sftp.java @@ -3,6 +3,7 @@ package cn.hutool.extra.ssh; import cn.hutool.core.collection.CollUtil; import cn.hutool.core.collection.ListUtil; import cn.hutool.core.io.FileUtil; +import cn.hutool.core.lang.Assert; import cn.hutool.core.lang.Filter; import cn.hutool.core.util.StrUtil; import cn.hutool.extra.ftp.AbstractFtp; @@ -538,7 +539,8 @@ public class Sftp extends AbstractFtp { * @since 5.7.16 */ public boolean upload(String destPath, String fileName, InputStream fileStream) { - destPath = StrUtil.addSuffixIfNot(destPath, StrUtil.SLASH) + StrUtil.removePrefix(fileName, StrUtil.SLASH); + Assert.notEmpty(fileName); + destPath = StrUtil.addSuffixIfNot(StrUtil.nullToEmpty(destPath), StrUtil.SLASH) + StrUtil.removePrefix(fileName, StrUtil.SLASH); put(fileStream, destPath, null, Mode.OVERWRITE); return true; } @@ -570,13 +572,16 @@ public class Sftp extends AbstractFtp { * 将本地文件上传到目标服务器,目标文件名为destPath,若destPath为目录,则目标文件名将与srcFilePath文件名相同。 * * @param srcFilePath 本地文件路径 - * @param destPath 目标路径, + * @param destPath 目标路径,{@code null}表示当前路径 * @param monitor 上传进度监控,通过实现此接口完成进度显示 * @param mode {@link Mode} 模式 * @return this * @since 4.6.5 */ - public Sftp put(String srcFilePath, String destPath, SftpProgressMonitor monitor, Mode mode) { + public Sftp put(String srcFilePath, String destPath, SftpProgressMonitor monitor, Mode mode) { + if(null == destPath){ + destPath = pwd(); + } try { getClient().put(srcFilePath, destPath, monitor, mode.ordinal()); } catch (SftpException e) { @@ -589,13 +594,16 @@ public class Sftp extends AbstractFtp { * 将本地数据流上传到目标服务器,目标文件名为destPath,目标必须为文件 * * @param srcStream 本地的数据流 - * @param destPath 目标路径, + * @param destPath 目标路径,{@code null}表示当前路径 * @param monitor 上传进度监控,通过实现此接口完成进度显示 * @param mode {@link Mode} 模式 * @return this * @since 5.7.16 */ public Sftp put(InputStream srcStream, String destPath, SftpProgressMonitor monitor, Mode mode) { + if(null == destPath){ + destPath = pwd(); + } try { getClient().put(srcStream, destPath, monitor, mode.ordinal()); } catch (SftpException e) { diff --git a/hutool-json/src/test/java/cn/hutool/json/xml/IssueID0HP2Test.java b/hutool-json/src/test/java/cn/hutool/json/xml/IssueID0HP2Test.java new file mode 100644 index 000000000..7b635601b --- /dev/null +++ b/hutool-json/src/test/java/cn/hutool/json/xml/IssueID0HP2Test.java @@ -0,0 +1,22 @@ +package cn.hutool.json.xml; + +import cn.hutool.core.date.DateUtil; +import cn.hutool.json.JSONConfig; +import cn.hutool.json.JSONObject; +import cn.hutool.json.JSONUtil; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +public class IssueID0HP2Test { + + /** + * JSON转换为XML时使用默认的日期格式,并不能自定义格式,日期格式只用于生成JSON字符串 + */ + @Test + void jsonWithDateToXmlTest() { + final JSONObject json = JSONUtil.createObj(JSONConfig.create().setDateFormat("yyyy/MM/dd")) + .set("date", DateUtil.parse("2025-10-03")); + String xml = JSONUtil.toXmlStr(json); + Assertions.assertEquals("2025-10-03 00:00:00", xml); + } +}