Compare commits

...

32 Commits

Author SHA1 Message Date
Golden Looly
fdf12f38b3 Merge pull request #4017 from TouyamaRie/v5-dev
针对log模块,新增单元测试:复杂对象参数测试
2025-08-18 18:57:41 +08:00
TouyamaRie
2197da291c Update LogTest.java, add complexObjectTest 2025-08-18 16:30:42 +08:00
Looly
4289a2658a 增加Gbase8s驱动支持 2025-08-18 10:23:43 +08:00
Golden Looly
010393653d Merge pull request #4012 from TouyamaRie/v5-dev-0814
针对log模块LogTest.java,新增单元测试:参数化日志消息边界情况测试、国际化日志消息测试
2025-08-14 16:01:33 +08:00
TouyamaRie
74ea349957 Update LogTest.java, add parameterizedMessageEdgeCasesTest and i18nMessageTest 2025-08-14 15:54:12 +08:00
Looly
66e2829415 getClientIP优先获取传入的请求头信息(pr#1373@Gitee) 2025-08-11 11:19:29 +08:00
Looly
8bc7c997d1 !1373 getClientIP 优先获取传入的请求头信息
Merge pull request !1373 from handy/handy
2025-08-11 03:15:49 +00:00
Looly
6e382b869e add test 2025-08-11 11:14:53 +08:00
Looly
3d49f9bf39 !1374 修复错别字
Merge pull request !1374 from handy/handy1
2025-08-11 03:14:45 +00:00
Looly
68ef91b6fb add test 2025-08-11 11:00:55 +08:00
Golden Looly
2b8284c7fb Merge pull request #4007 from qibinhang/v5-dev
新增测试用例以测试更多重要场景
2025-08-11 10:59:23 +08:00
Looly
445703380b fix comment 2025-08-11 10:48:06 +08:00
Golden Looly
e92ab1308c Merge pull request #4004 from fanchenggang/v5-dev
fix 汉子 to 汉字 Update Pinyin4jEngine.java
2025-08-11 10:47:44 +08:00
handy
c8f11cd9f9 修复错别字 2025-08-08 20:00:08 +08:00
qibinhang
f3d95e908b Add tests for covering additional, important scenarios of FileUtil. 2025-08-08 13:51:47 +08:00
qibinhang
8bfefbb9ba Add tests for covering additional, important scenarios of CollUtil. 2025-08-08 13:51:40 +08:00
qibinhang
3e330f1c64 Add tests for covering additional, important scenarios of CollStreamUtil. 2025-08-08 13:51:04 +08:00
handy
a27ebb4403 getClientIP 优先获取传入的请求头信息 2025-08-07 13:35:00 +08:00
Farahani
d5bc6216a4 Update Pinyin4jEngine.java
fix(方法注释):注释错误修改
2025-08-06 00:21:10 +08:00
Looly
b580030b5b !1370 fix(方法注释):方法注释错误修改
Merge pull request !1370 from xy/v5-dev
2025-07-31 10:36:05 +00:00
xuyang
7070c73dbf fix(方法注释):方法注释错误修改 2025-07-31 14:53:44 +08:00
Looly
59f33fd12d MapUtil增加flatten方法(pr#1368@Gitee) 2025-07-30 19:14:23 +08:00
Looly
4dcbc74cd1 MapUtil增加flatten方法(pr#1368@Gitee) 2025-07-30 19:10:12 +08:00
Looly
6760658cba !1368 将多层级Map处理为一个层级Map类型
Merge pull request !1368 from ckpaly/v5-dev
2025-07-30 11:05:25 +00:00
Looly
0b71a19ba8 DateUtil新增方法yyyy-MM-ddTHH:mmXXX格式支持(pr#1367@Gitee) 2025-07-30 19:03:37 +08:00
Looly
cee0efdee1 !1367 新增 yyyy-MM-dd'T'HH:mmXXX 格式支持
Merge pull request !1367 from 盘古给你一斧/v5-dev
2025-07-30 11:00:21 +00:00
chenkai
0661bf27df 将多层级Map处理为一个层级Map类型 2025-07-29 14:40:48 +08:00
chenkai
cae1203591 将多层级Map处理为一个层级Map类型 2025-07-29 14:31:07 +08:00
盘古给你一斧
5244b7510e 新增 yyyy-MM-dd'T'HH:mmXXX 格式支持 2025-07-28 23:14:51 +08:00
Looly
b5ccc1a7c9 add test 2025-07-24 16:26:35 +08:00
Looly
a09da9fd3d CalendarUtil增加isSameYearcalendar方法(issue#3995@Github) 2025-07-24 16:16:32 +08:00
Looly
3d0d8dea4b 修复QLExpressEngineallowClassSet无效问题(issue#3994@Github) 2025-07-22 15:26:19 +08:00
24 changed files with 413 additions and 45 deletions

View File

@@ -2,16 +2,22 @@
# 🚀Changelog
-------------------------------------------------------------------------------------------------------------
# 5.8.40(2025-07-21)
# 5.8.40(2025-08-18)
### 🐣新特性
* 【captcha】 `MathGenerator`四则运算方式支持不生成负数结果pr#1363@Gitee
* 【core 】 增加`MapValueProvider``RecordConverter`并支持Record转换issue#3985@Github
* 【core 】 `CalendarUtil`增加`isSameYear``calendar`方法issue#3995@Github
* 【core 】 `DateUtil`增加`yyyy-MM-dd'T'HH:mmXXX`格式支持pr#1367@Gitee
* 【core 】 `MapUtil`增加flatten方法pr#1368@Gitee
* 【extra 】 `getClientIP`优先获取传入的请求头信息pr#1373@Gitee
* 【db 】 增加`Gbase8s`驱动支持issue#ICSFAM@Gitee
### 🐞Bug修复
* 【extra 】 `Sftp``reconnectIfTimeout`方法改为捕获所有异常issue#3989@Github
* 【core 】 修复`ChineseDate `闰年闰月节日获取问题issue#ICL1BT@Gitee
* 【core 】 修复`TreeBuilder`append重复向idTreeMap中put问题pr#3992@Github
* 【extra 】 修复`QLExpressEngine`allowClassSet无效问题issue#3994@Github
-------------------------------------------------------------------------------------------------------------
# 5.8.39(2025-06-20)

View File

@@ -469,7 +469,7 @@ public class CollUtil {
*
* @param collection 集合
* @param value 需要查找的值
* @return 果集合为空null或者空返回{@code false},否则找到元素返回{@code true}
* @return 果集合为空null或者空返回{@code false},否则找到元素返回{@code true}
* @since 5.7.16
*/
public static boolean safeContains(Collection<?> collection, Object value) {

View File

@@ -74,6 +74,21 @@ public class CalendarUtil {
return cal;
}
/**
* 转换为指定时区的Calendar返回新的Calendar
*
* @param calendar 时间
* @param timeZone 新时区
* @return 指定时区的新的calendar对象
* @since 5.8.30
*/
public static Calendar calendar(Calendar calendar, final TimeZone timeZone) {
// 转换到统一时区例如UTC
calendar = (Calendar) calendar.clone();
calendar.setTimeZone(timeZone);
return calendar;
}
/**
* 是否为上午
*
@@ -437,6 +452,30 @@ public class CalendarUtil {
cal1.get(Calendar.ERA) == cal2.get(Calendar.ERA);
}
/**
* 比较两个日期是否为同一年<br>
* 同一个年的意思是ERA公元、year都一致。
*
* @param cal1 日期1
* @param cal2 日期2
* @return 是否为同一年
* @since 5.8.30
*/
public static boolean isSameYear(final Calendar cal1, Calendar cal2) {
if (cal1 == null || cal2 == null) {
throw new IllegalArgumentException("The date must not be null");
}
if (!ObjUtil.equals(cal1.getTimeZone(), cal2.getTimeZone())) {
// 统一时区
cal2 = calendar(cal2, cal1.getTimeZone());
}
return cal1.get(Calendar.YEAR) == cal2.get(Calendar.YEAR) && //
// issue#3011@Github
cal1.get(Calendar.ERA) == cal2.get(Calendar.ERA);
}
/**
* <p>检查两个Calendar时间戳是否相同。</p>
*

View File

@@ -285,6 +285,15 @@ public class DatePattern {
*/
public static final FastDateFormat UTC_WITH_XXX_OFFSET_FORMAT = FastDateFormat.getInstance(UTC_WITH_XXX_OFFSET_PATTERN);
/**
* UTC时间yyyy-MM-dd'T'HH:mmXXX
*/
public static final String UTC_SIMPLE_MINUTE_WITH_XXX_OFFSET_PATTERN = "yyyy-MM-dd'T'HH:mmXXX";
/**
* UTC时间{@link FastDateFormat}yyyy-MM-dd'T'HH:mmXXX
*/
public static final FastDateFormat UTC_SIMPLE_MINUTE_WITH_XXX_OFFSET_FORMAT = FastDateFormat.getInstance(UTC_SIMPLE_MINUTE_WITH_XXX_OFFSET_PATTERN);
/**
* UTC时间yyyy-MM-dd'T'HH:mm:ss.SSS'Z'
*/

View File

@@ -901,6 +901,9 @@ public class DateUtil extends CalendarUtil {
// 带毫秒格式类似2018-09-13T05:34:31.999+08:00
iso8601String = normalizeMillSeconds(iso8601String, ".", "+");
return parse(iso8601String, DatePattern.UTC_MS_WITH_XXX_OFFSET_FORMAT);
} else if (iso8601String.length() == 22 && StrUtil.count(iso8601String, ':') == 2) {
// 精确到分钟格式类似2025-07-28T20:00+08:00
return parse(iso8601String, DatePattern.UTC_SIMPLE_MINUTE_WITH_XXX_OFFSET_FORMAT);
} else {
// 格式类似2018-09-13T05:34:31+08:00
return parse(iso8601String, DatePattern.UTC_WITH_XXX_OFFSET_FORMAT);

View File

@@ -1544,4 +1544,47 @@ public class MapUtil {
}
return list;
}
/**
* 将多层级Map处理为一个层级Map类型
*
* @param map 入参Map
* @param <K> 键类型
* @param <V> 值类型
* @return 单层级Map返回值
* @since 5.8.40
*/
public static <K, V> Map<K, V> flatten(final Map<K, V> map) {
return flatten(map, new HashMap<>());
}
/**
* 递归调用将多层级Map处理为一个层级Map类型
*
* @param map 入参Map
* @param flatMap 单层级Map返回值
* @param <K> 键类型
* @param <V> 值类型
* @return 单层级Map返回值
* @since 5.8.40
*/
@SuppressWarnings("unchecked")
public static <K, V> Map<K, V> flatten(final Map<K, V> map, Map<K, V> flatMap) {
Assert.notNull(map);
if (null == flatMap) {
flatMap = new HashMap<>();
}
Map<K, V> finalFlatMap = flatMap;
map.forEach((k, v) -> {
// 避免嵌套循环
if (v instanceof Map && v != map) {
flatten((Map<K, V>) v, finalFlatMap);
} else {
finalFlatMap.put(k, v);
}
});
return flatMap;
}
}

View File

@@ -1823,7 +1823,7 @@ public class NumberUtil {
* @param bigNum1 数字1
* @param bigNum2 数字2
* @return 是否大于等于
* @since 3, 0.9
* @since 3.0.9
*/
public static boolean isGreaterOrEqual(BigDecimal bigNum1, BigDecimal bigNum2) {
Assert.notNull(bigNum1);
@@ -1837,7 +1837,7 @@ public class NumberUtil {
* @param bigNum1 数字1
* @param bigNum2 数字2
* @return 是否小于
* @since 3, 0.9
* @since 3.0.9
*/
public static boolean isLess(BigDecimal bigNum1, BigDecimal bigNum2) {
Assert.notNull(bigNum1);
@@ -1851,7 +1851,7 @@ public class NumberUtil {
* @param bigNum1 数字1
* @param bigNum2 数字2
* @return 是否小于等于
* @since 3, 0.9
* @since 3.0.9
*/
public static boolean isLessOrEqual(BigDecimal bigNum1, BigDecimal bigNum2) {
Assert.notNull(bigNum1);

View File

@@ -4,13 +4,14 @@ import cn.hutool.core.map.MapUtil;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.ToString;
import static org.junit.jupiter.api.Assertions.*;
import org.junit.jupiter.api.Test;
import java.util.*;
import java.util.stream.Collector;
import java.util.stream.Collectors;
import static org.junit.jupiter.api.Assertions.*;
/**
* CollectionStream测试方法
*/
@@ -19,17 +20,17 @@ public class CollStreamUtilTest {
@Test
public void testToIdentityMap() {
Map<Long, Student> map = CollStreamUtil.toIdentityMap(null, Student::getStudentId);
assertEquals(map, Collections.EMPTY_MAP);
assertEquals(Collections.EMPTY_MAP, map);
List<Student> list = new ArrayList<>();
map = CollStreamUtil.toIdentityMap(list, Student::getStudentId);
assertEquals(map, Collections.EMPTY_MAP);
assertEquals(Collections.EMPTY_MAP, map);
list.add(new Student(1, 1, 1, "张三"));
list.add(new Student(1, 1, 2, "李四"));
list.add(new Student(1, 1, 3, "王五"));
map = CollStreamUtil.toIdentityMap(list, Student::getStudentId);
assertEquals(map.get(1L).getName(), "张三");
assertEquals(map.get(2L).getName(), "李四");
assertEquals(map.get(3L).getName(), "王五");
assertEquals("张三", map.get(1L).getName());
assertEquals("李四", map.get(2L).getName());
assertEquals("王五", map.get(3L).getName());
assertNull(map.get(4L));
// 测试value为空时
@@ -41,17 +42,17 @@ public class CollStreamUtilTest {
@Test
public void testToMap() {
Map<Long, String> map = CollStreamUtil.toMap(null, Student::getStudentId, Student::getName);
assertEquals(map, Collections.EMPTY_MAP);
assertEquals(Collections.EMPTY_MAP, map);
List<Student> list = new ArrayList<>();
map = CollStreamUtil.toMap(list, Student::getStudentId, Student::getName);
assertEquals(map, Collections.EMPTY_MAP);
assertEquals(Collections.EMPTY_MAP, map);
list.add(new Student(1, 1, 1, "张三"));
list.add(new Student(1, 1, 2, "李四"));
list.add(new Student(1, 1, 3, "王五"));
map = CollStreamUtil.toMap(list, Student::getStudentId, Student::getName);
assertEquals(map.get(1L), "张三");
assertEquals(map.get(2L), "李四");
assertEquals(map.get(3L), "王五");
assertEquals("张三", map.get(1L));
assertEquals("李四", map.get(2L));
assertEquals("王五", map.get(3L));
assertNull(map.get(4L));
// 测试value为空时
@@ -60,13 +61,47 @@ public class CollStreamUtilTest {
assertNull(map.get(4L));
}
@Test
public void testToMap_KeyCollision_SilentlyOverwrite() {
List<Student> list = new ArrayList<>();
list.add(new Student(1, 101, 1, "张三"));
list.add(new Student(1, 102, 1, "李四"));
Map<Long, String> map = CollStreamUtil.toMap(list, Student::getStudentId, Student::getName, false);
assertEquals(1, map.size());
assertEquals("李四", map.get(1L)); // 确保后面的值覆盖前面的
}
@Test
public void testToMap_NullKeyOrValue() {
List<Student> list = new ArrayList<>();
list.add(new Student(1, 1, 1L, "张三"));
list.add(null);
list.add(new Student(1, 2, 2L, null));
assertThrows(NullPointerException.class, () -> {
CollStreamUtil.toMap(list, Student::getStudentId, Student::getName);
});
}
@Test
public void testToMap_LargeInputPerformance() {
List<Student> list = new ArrayList<>();
for (long i = 0; i < 10000; i++) {
list.add(new Student(1, 1, i, "学生" + i));
}
Map<Long, String> map = CollStreamUtil.toMap(list, Student::getStudentId, Student::getName);
assertEquals(10000, map.size());
}
@Test
public void testGroupByKey() {
Map<Long, List<Student>> map = CollStreamUtil.groupByKey(null, Student::getClassId);
assertEquals(map, Collections.EMPTY_MAP);
assertEquals(Collections.EMPTY_MAP, map);
List<Student> list = new ArrayList<>();
map = CollStreamUtil.groupByKey(list, Student::getClassId);
assertEquals(map, Collections.EMPTY_MAP);
assertEquals(Collections.EMPTY_MAP, map);
list.add(new Student(1, 1, 1, "张三"));
list.add(new Student(1, 2, 2, "李四"));
list.add(new Student(2, 1, 1, "擎天柱"));
@@ -92,10 +127,10 @@ public class CollStreamUtilTest {
@Test
public void testGroupBy2Key() {
Map<Long, Map<Long, List<Student>>> map = CollStreamUtil.groupBy2Key(null, Student::getTermId, Student::getClassId);
assertEquals(map, Collections.EMPTY_MAP);
assertEquals(Collections.EMPTY_MAP, map);
List<Student> list = new ArrayList<>();
map = CollStreamUtil.groupBy2Key(list, Student::getTermId, Student::getClassId);
assertEquals(map, Collections.EMPTY_MAP);
assertEquals(Collections.EMPTY_MAP, map);
list.add(new Student(1, 1, 1, "张三"));
list.add(new Student(1, 2, 2, "李四"));
list.add(new Student(1, 2, 3, "王五"));
@@ -131,11 +166,11 @@ public class CollStreamUtilTest {
@Test
public void testGroup2Map() {
Map<Long, Map<Long, Student>> map = CollStreamUtil.group2Map(null, Student::getTermId, Student::getClassId);
assertEquals(map, Collections.EMPTY_MAP);
assertEquals(Collections.EMPTY_MAP, map);
List<Student> list = new ArrayList<>();
map = CollStreamUtil.group2Map(list, Student::getTermId, Student::getClassId);
assertEquals(map, Collections.EMPTY_MAP);
assertEquals(Collections.EMPTY_MAP, map);
list.add(new Student(1, 1, 1, "张三"));
list.add(new Student(1, 2, 1, "李四"));
list.add(new Student(2, 2, 1, "王五"));
@@ -152,7 +187,10 @@ public class CollStreamUtilTest {
// 对null友好
Map<Long, Map<Long, Student>> termIdClassIdStudentMap = CollStreamUtil.group2Map(Arrays.asList(null, new Student(2, 2, 1, "王五")), Student::getTermId, Student::getClassId);
Map<Long, Map<Long, Student>> termIdClassIdStudentCompareMap = new HashMap<Long, Map<Long, Student>>() {{
Map<Long, Map<Long, Student>> termIdClassIdStudentCompareMap = new HashMap<Long, Map<Long, Student>>() {
private static final long serialVersionUID = -26683057599474572L;
{
put(null, MapUtil.of(null, null));
put(2L, MapUtil.of(2L, new Student(2, 2, 1, "王五")));
}};
@@ -162,11 +200,11 @@ public class CollStreamUtilTest {
@Test
public void testGroupKeyValue() {
Map<Long, List<Long>> map = CollStreamUtil.groupKeyValue(null, Student::getTermId, Student::getClassId);
assertEquals(map, Collections.EMPTY_MAP);
assertEquals(Collections.EMPTY_MAP, map);
List<Student> list = new ArrayList<>();
map = CollStreamUtil.groupKeyValue(list, Student::getTermId, Student::getClassId);
assertEquals(map, Collections.EMPTY_MAP);
assertEquals(Collections.EMPTY_MAP, map);
list.add(new Student(1, 1, 1, "张三"));
list.add(new Student(1, 2, 1, "李四"));
list.add(new Student(2, 2, 1, "王五"));
@@ -184,12 +222,12 @@ public class CollStreamUtilTest {
// 参数null测试
Map<Long, List<Student>> map = CollStreamUtil.groupBy(null, Student::getTermId, Collectors.toList());
assertEquals(map, Collections.EMPTY_MAP);
assertEquals(Collections.EMPTY_MAP, map);
// 参数空数组测试
List<Student> list = new ArrayList<>();
map = CollStreamUtil.groupBy(list, Student::getTermId, Collectors.toList());
assertEquals(map, Collections.EMPTY_MAP);
assertEquals(Collections.EMPTY_MAP, map);
// 放入元素
list.add(new Student(1, 1, 1, "张三"));
@@ -230,10 +268,10 @@ public class CollStreamUtilTest {
@Test
public void testTranslate2List() {
List<String> list = CollStreamUtil.toList(null, Student::getName);
assertEquals(list, Collections.EMPTY_LIST);
assertEquals(Collections.EMPTY_LIST, list);
List<Student> students = new ArrayList<>();
list = CollStreamUtil.toList(students, Student::getName);
assertEquals(list, Collections.EMPTY_LIST);
assertEquals(Collections.EMPTY_LIST, list);
students.add(new Student(1, 1, 1, "张三"));
students.add(new Student(1, 2, 2, "李四"));
students.add(new Student(2, 1, 1, "李四"));
@@ -252,10 +290,10 @@ public class CollStreamUtilTest {
@Test
public void testTranslate2Set() {
Set<String> set = CollStreamUtil.toSet(null, Student::getName);
assertEquals(set, Collections.EMPTY_SET);
assertEquals(Collections.EMPTY_SET, set);
List<Student> students = new ArrayList<>();
set = CollStreamUtil.toSet(students, Student::getName);
assertEquals(set, Collections.EMPTY_SET);
assertEquals(Collections.EMPTY_SET, set);
students.add(new Student(1, 1, 1, "张三"));
students.add(new Student(1, 2, 2, "李四"));
students.add(new Student(2, 1, 1, "李四"));
@@ -274,7 +312,7 @@ public class CollStreamUtilTest {
Map<Long, Student> map1 = null;
Map<Long, Student> map2 = Collections.emptyMap();
Map<Long, String> map = CollStreamUtil.merge(map1, map2, (s1, s2) -> s1.getName() + s2.getName());
assertEquals(map, Collections.EMPTY_MAP);
assertEquals(Collections.EMPTY_MAP, map);
map1 = new HashMap<>();
map1.put(1L, new Student(1, 1, 1, "张三"));
map = CollStreamUtil.merge(map1, map2, this::merge);

View File

@@ -62,6 +62,25 @@ public class CollUtilTest {
assertEquals(srcList, answerList);
}
@Test
public void testPadLeft_NegativeMinLen_ShouldNotModifyList() {
List<String> list = CollUtil.newArrayList("a", "b", "c");
List<String> original = CollUtil.newArrayList("a", "b", "c");
CollUtil.padLeft(list, -5, "x");
assertEquals(original, list, "List should remain unchanged when minLen is negative");
}
@Test
public void testPadLeft_EmptyList_MinLenZero() {
List<String> list = CollUtil.newArrayList();
CollUtil.padLeft(list, 0, "x");
assertTrue(list.isEmpty(), "List should remain empty when minLen is 0");
}
@Test
public void testPadRight() {
final List<String> srcList = CollUtil.newArrayList("a");
@@ -212,6 +231,19 @@ public class CollUtilTest {
assertEquals("[1]", r2.toString());
}
@Test
public void testSubtractWithDuplicates() {
Collection<String> coll1 = new ArrayList<>(Arrays.asList("a", "b", "b", "c"));
Collection<String> coll2 = Collections.singletonList("b");
Collection<String> result = CollUtil.subtract(coll1, coll2);
List<String> expected = Arrays.asList("a", "c");
List<String> resultList = new ArrayList<>(result);
Collections.sort(resultList);
Collections.sort(expected);
assertEquals(expected, resultList);
}
@Test
public void toMapListAndToListMapTest() {
final HashMap<String, String> map1 = new HashMap<>();
@@ -825,6 +857,34 @@ public class CollUtilTest {
assertEquals(2, i);
}
@Test
public void lastIndexOf_NoMatchExists() {
List<String> list = CollUtil.newArrayList("a", "b", "c");
int idx = CollUtil.lastIndexOf(list, item -> item.equals("z"));
assertEquals(-1, idx);
}
@Test
public void lastIndexOf_MatcherIsNull_MatchAll() {
List<String> list = CollUtil.newArrayList("x", "y", "z");
int idx = CollUtil.lastIndexOf(list, null);
assertEquals(2, idx);
}
@Test
public void lastIndexOf_EmptyCollection() {
List<String> list = CollUtil.newArrayList();
int idx = CollUtil.lastIndexOf(list, Objects::nonNull);
assertEquals(-1, idx);
}
@Test
public void lastIndexOf_SingletonCollection_Match() {
List<String> list = CollUtil.newArrayList("foo");
int idx = CollUtil.lastIndexOf(list, item -> item.equals("foo"));
assertEquals(0, idx);
}
@Test
public void pageTest() {
final List<Dict> objects = CollUtil.newArrayList();
@@ -907,7 +967,7 @@ public class CollUtilTest {
// 测试集合1和集合2不包含相同元素的情况
final List<String> list1 = Arrays.asList("a", "b", "c");
final List<String> list2 = Arrays.asList("d", "e", "f");
// 期望结果返回集合1的完整拷贝
final List<String> result = CollUtil.subtractToList(list1, list2);
assertEquals(3, result.size());
@@ -916,11 +976,11 @@ public class CollUtilTest {
assertEquals("c", result.get(2));
assertEquals(list1, result);
assertNotSame(list1, result); // 确保返回的是拷贝而不是原始引用
// 测试集合1中有重复元素的情况
final List<String> list3 = Arrays.asList("a", "a", "b", "b", "c");
final List<String> list4 = Arrays.asList("d", "e", "f");
// 期望结果返回集合1的完整拷贝包括重复元素
final List<String> result2 = CollUtil.subtractToList(list3, list4);
assertEquals(5, result2.size());
@@ -931,11 +991,11 @@ public class CollUtilTest {
assertEquals("c", result2.get(4));
assertEquals(list3, result2);
assertNotSame(list3, result2);
// 测试不同类型的元素但确保两个集合的泛型类型一致
final List<Integer> list5 = Arrays.asList(1, 2, 3);
final List<Integer> list6 = Arrays.asList(4, 5, 6);
// 期望结果返回集合1的完整拷贝
final List<Integer> result3 = CollUtil.subtractToList(list5, list6);
assertEquals(3, result3.size());
@@ -1227,9 +1287,9 @@ public class CollUtilTest {
genderMap.put(5, "小孩");
genderMap.put(6, "");
assertEquals(people.get(1).getGender(), "woman");
assertEquals("woman", people.get(1).getGender());
CollUtil.setValueByMap(people, genderMap, Person::getId, Person::setGender);
assertEquals(people.get(1).getGender(), "妇女");
assertEquals("妇女", people.get(1).getGender());
final Map<Integer, Person> personMap = new HashMap<>();
personMap.put(1, new Person("AA", 21, "", 1));
@@ -1245,7 +1305,7 @@ public class CollUtilTest {
x.setAge(y.getAge());
});
assertEquals(people.get(1).getGender(), "小孩");
assertEquals("小孩", people.get(1).getGender());
}
@Test

View File

@@ -667,6 +667,12 @@ public class DateUtilTest {
dateStr = dt.toString(simpleDateFormat);
assertEquals("2018-09-13 13:34:39.999", dateStr);
dateStr1 = "2025-07-28T20:00+08:00";
dt = DateUtil.parse(dateStr1);
assert dt != null;
dateStr = dt.toString();
assertEquals("2025-07-28 20:00:00", dateStr);
// 使用UTC时区
dateStr1 = "2018-09-13T13:34:39.99";
dt = DateUtil.parse(dateStr1);

View File

@@ -528,6 +528,44 @@ public class FileUtilTest {
assertTrue(FileUtil.isSub(file, file2));
}
@Test
public void isSub_SubIsAncestorOfParentTest() {
File parent = new File("d:/home/user/docs/notes");
File sub = new File("d:/home/user/docs");
assertFalse(FileUtil.isSub(parent, sub));
}
@Test
public void isSub_SamePathTest() {
File parent = new File("d:/home/user/docs");
File sub = new File("d:/home/user/docs");
assertTrue(FileUtil.isSub(parent, sub));
}
@Test
public void isSub_NonexistentPathsTest() {
File parent = new File("d:/unlikely/to/exist/parent");
File sub = new File("d:/unlikely/to/exist/parent/child/file.txt");
assertTrue(FileUtil.isSub(parent, sub));
File nonchild = new File("d:/also/unlikely/path.txt");
assertFalse(FileUtil.isSub(parent, nonchild));
}
@Test
public void isSub_NullParentTest() {
assertThrows(IllegalArgumentException.class, () -> {
FileUtil.isSub(null, new java.io.File("d:/any/path"));
});
}
@Test
public void isSub_NullSubTest() {
assertThrows(IllegalArgumentException.class, () -> {
FileUtil.isSub(new java.io.File("d:/any/path"), null);
});
}
@Test
@Disabled
public void appendLinesTest(){

View File

@@ -862,4 +862,29 @@ public class MapUtilTest {
assertEquals(0, MapUtil.get(map, "age", new TypeReference<Integer>() {
}, 0));
}
@Test
public void flattenMapReturnsTest() {
Map<String, String> clothes = new HashMap<>();
clothes.put("clothesName", "ANTA");
clothes.put("clothesPrice", "200");
Map<String, Object> person = new HashMap<>();
person.put("personName", "XXXX");
person.put("clothes", clothes);
Map<String, Object> map = new HashMap<>();
map.put("home", "AAA");
map.put("person", person);
Map<String, Object> flattenMap = MapUtil.flatten(map);
assertEquals("ANTA", MapUtil.get(flattenMap, "clothesName", new TypeReference<String>() {
}));
assertEquals("200", MapUtil.get(flattenMap, "clothesPrice", new TypeReference<String>() {
}));
assertEquals("XXXX", MapUtil.get(flattenMap, "personName", new TypeReference<String>() {
}));
assertEquals("AAA", MapUtil.get(flattenMap, "home", new TypeReference<String>() {
}));
}
}

View File

@@ -0,0 +1,15 @@
package cn.hutool.core.text.csv;
import cn.hutool.core.io.resource.ResourceUtil;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
public class IssueICRMKATest {
@Test
public void issueICRMAKTest() {
CsvReader reader = CsvUtil.getReader();
CsvData data = reader.read(ResourceUtil.getUtf8Reader("issueICRMKA.csv"));
final CsvRow row = data.getRow(1);
Assertions.assertEquals("6.3\" Google Pixel 9 Pro 128 GB Beige", row.get(0));
}
}

View File

@@ -0,0 +1,13 @@
package cn.hutool.core.util;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
public class IssueICOJVZTest {
@Test
void toUnderlineTest(){
String field = "PAGE_NAME";
field = StrUtil.toUnderlineCase(field).toUpperCase();
Assertions.assertEquals("PAGE_NAME", field);
}
}

View File

@@ -0,0 +1,2 @@
config,price,unit,package_name,package,equipment,pic_path,crawler_date,set_Discount,installment_details,installment_price,installment_time
6.3" Google Pixel 9 Pro 128 GB Beige,94999,,fwefwe,fewfew,"Cores - 8x(3.1 GHz), 16 GB, 1 SIM, OLED, 2856x1280, 50+48+48 MP camera, NFC, 5G, GPS, 4700 mAh more details",["https://xx.png"],2025/8/1 4:05,,gerwgrweg,fwefw,fwefwe
Can't render this file because it contains an unexpected character in line 2 and column 4.

View File

@@ -173,6 +173,9 @@ public class DialectFactory implements DriverNamePool {
} else if (nameContainsProductInfo.contains("sap")) {
// sap hana
driver = DRIVER_HANA;
} else if (nameContainsProductInfo.contains("gbasedbt-sqli")) {
// Gbase8shttps://www.gbase.cn/community/post/4029
driver = DRIVER_GBASE8S;
}
return driver;

View File

@@ -104,6 +104,11 @@ public interface DriverNamePool {
* JDBC 驱动 南大通用
*/
String DRIVER_GBASE = "com.gbase.jdbc.Driver";
/**
* JDBC 驱动 南大通用8S<br>
* 见https://www.gbase.cn/community/post/4029
*/
String DRIVER_GBASE8S = "com.gbasedbt.jdbc.Driver";
/**
* JDBC 驱动 神州数据库
*/

View File

@@ -4,7 +4,10 @@ import cn.hutool.extra.expression.ExpressionEngine;
import cn.hutool.extra.expression.ExpressionException;
import com.ql.util.express.DefaultContext;
import com.ql.util.express.ExpressRunner;
import com.ql.util.express.config.QLExpressRunStrategy;
import javax.naming.InitialContext;
import java.lang.reflect.Method;
import java.util.Collection;
import java.util.Map;
@@ -24,10 +27,24 @@ public class QLExpressEngine implements ExpressionEngine {
*/
public QLExpressEngine() {
engine = new ExpressRunner();
// issue#3994@Github
// Enforce blacklisting of high-risk method invocations
QLExpressRunStrategy.setForbidInvokeSecurityRiskMethods(true);
// Explicitly forbid JNDI lookup calls through InitialContext
QLExpressRunStrategy.addSecurityRiskMethod(InitialContext.class, "doLookup");
}
@Override
public Object eval(final String expression, final Map<String, Object> context, Collection<Class<?>> allowClassSet) {
// issue#3994@Github
if (null != allowClassSet) {
for (Class<?> clazz : allowClassSet) {
for (Method method : clazz.getDeclaredMethods()) {
QLExpressRunStrategy.addSecureMethod(clazz, method.getName());
}
}
}
final DefaultContext<String, Object> defaultContext = new DefaultContext<>();
defaultContext.putAll(context);
try {

View File

@@ -27,7 +27,7 @@ import com.github.stuxuhai.jpinyin.PinyinHelper;
*/
public class JPinyinEngine implements PinyinEngine {
//设置汉拼音输出的格式
//设置汉拼音输出的格式
PinyinFormat format;
public JPinyinEngine(){

View File

@@ -33,7 +33,7 @@ import net.sourceforge.pinyin4j.format.exception.BadHanyuPinyinOutputFormatCombi
*/
public class Pinyin4jEngine implements PinyinEngine {
//设置汉拼音输出的格式
//设置汉拼音输出的格式
HanyuPinyinOutputFormat format;
/**

View File

@@ -215,7 +215,7 @@ public class JakartaServletUtil {
public static String getClientIP(HttpServletRequest request, String... otherHeaderNames) {
String[] headers = {"X-Forwarded-For", "X-Real-IP", "Proxy-Client-IP", "WL-Proxy-Client-IP", "HTTP_CLIENT_IP", "HTTP_X_FORWARDED_FOR"};
if (ArrayUtil.isNotEmpty(otherHeaderNames)) {
headers = ArrayUtil.addAll(headers, otherHeaderNames);
headers = ArrayUtil.addAll(otherHeaderNames, headers);
}
return getClientIPByHeader(request, headers);

View File

@@ -215,7 +215,7 @@ public class ServletUtil {
public static String getClientIP(HttpServletRequest request, String... otherHeaderNames) {
String[] headers = {"X-Forwarded-For", "X-Real-IP", "Proxy-Client-IP", "WL-Proxy-Client-IP", "HTTP_CLIENT_IP", "HTTP_X_FORWARDED_FOR"};
if (ArrayUtil.isNotEmpty(otherHeaderNames)) {
headers = ArrayUtil.addAll(headers, otherHeaderNames);
headers = ArrayUtil.addAll(otherHeaderNames, headers);
}
return getClientIPByHeader(request, headers);

View File

@@ -391,7 +391,7 @@ public class HttpServerRequest extends HttpServerBase {
public String getClientIP(String... otherHeaderNames) {
String[] headers = {"X-Forwarded-For", "X-Real-IP", "Proxy-Client-IP", "WL-Proxy-Client-IP", "HTTP_CLIENT_IP", "HTTP_X_FORWARDED_FOR"};
if (ArrayUtil.isNotEmpty(otherHeaderNames)) {
headers = ArrayUtil.addAll(headers, otherHeaderNames);
headers = ArrayUtil.addAll(otherHeaderNames, headers);
}
return getClientIPByHeader(headers);

View File

@@ -7,6 +7,11 @@ import cn.hutool.log.Log;
import cn.hutool.log.LogFactory;
import cn.hutool.log.level.Level;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* 日志门面单元测试
* @author Looly
@@ -45,4 +50,45 @@ public class LogTest {
log.info(null);
log.warn(null);
}
@Test
public void parameterizedMessageEdgeCasesTest() {
Log log = LogFactory.get();
// 测试不同数量的参数
log.info("No parameters");
log.info("One: {}", "param1");
log.info("Two: {} and {}", "param1", "param2");
log.info("Three: {}, {}, {}", "param1", "param2", "param3");
log.info("Four: {}, {}, {}, {}", "param1", "param2", "param3", "param4");
// 测试参数不足的情况
log.info("Missing param: {} and {}", "only_one");
// 测试参数过多的情况
log.info("Extra param: {}", "param1", "extra_param");
}
@Test
public void i18nMessageTest() {
Log log = LogFactory.get();
// 国际化消息测试
log.info("中文消息测试");
log.info("Message with unicode: {}", "特殊字符©®™✓✗★☆");
log.info("多语言混排: 中文, English, 日本語, 한글");
log.info("Emoji测试: 😀🚀🌏");
}
@Test
public void complexObjectTest() {
Log log = LogFactory.get();
// 复杂对象参数测试
List<String> list = Arrays.asList("item1", "item2");
Map<String, Object> map = new HashMap<>();
map.put("key", "value");
log.info("List: {}", list);
log.info("Map: {}", map);
log.info("Null object: {}", (Object)null);
}
}