mirror of
https://gitee.com/dromara/hutool.git
synced 2025-05-06 13:48:00 +08:00
Merge remote-tracking branch 'origin/v5-dev' into v5-dev
This commit is contained in:
commit
bd746c68eb
11
CHANGELOG.md
11
CHANGELOG.md
@ -2,7 +2,7 @@
|
|||||||
# 🚀Changelog
|
# 🚀Changelog
|
||||||
|
|
||||||
-------------------------------------------------------------------------------------------------------------
|
-------------------------------------------------------------------------------------------------------------
|
||||||
# 5.8.22(2023-08-16)
|
# 5.8.22(2023-09-05)
|
||||||
|
|
||||||
### 🐣新特性
|
### 🐣新特性
|
||||||
* 【core 】 NumberUtil.nullToZero增加重载(issue#I7PPD2@Gitee)
|
* 【core 】 NumberUtil.nullToZero增加重载(issue#I7PPD2@Gitee)
|
||||||
@ -22,6 +22,15 @@
|
|||||||
* 【core 】 去除默认的ACCEPT_LANGUAGE(issue#3258@Github)
|
* 【core 】 去除默认的ACCEPT_LANGUAGE(issue#3258@Github)
|
||||||
* 【core 】 修复FieldsComparator比较结果不正确问题(issue#3259@Github)
|
* 【core 】 修复FieldsComparator比较结果不正确问题(issue#3259@Github)
|
||||||
* 【core 】 修复Db.findAll全局忽略大小写无效问题(issue#I7T30Y@Gitee)
|
* 【core 】 修复Db.findAll全局忽略大小写无效问题(issue#I7T30Y@Gitee)
|
||||||
|
* 【core 】 修复Ipv4Util.getEndIpLong 取反符号导致数据越界(issue#I7U1OQ@Gitee)
|
||||||
|
* 【http 】 修复302重定向时,Location中的问号被转义问题(issue#3265@Github)
|
||||||
|
* 【core 】 修复CombinationAnnotationElement判断循环问题(pr#3267@Github)
|
||||||
|
* 【core 】 修复StrUtil#containsAny NPE问题(pr#1063@Gitee)
|
||||||
|
* 【all 】 修复SONArray的add()方法抛出OutOfMemory异常问题(issue#3286@Github)
|
||||||
|
* 【core 】 修复fillColumns空指针问题(issue#3284@Github)
|
||||||
|
* 【core 】 修复Convert不能转换Optional和Opt问题(issue#I7WJHH@Gitee)
|
||||||
|
* 【core 】 修复DateUtil.age年龄计算问题(issue#I7XMYW@Gitee)
|
||||||
|
* 【core 】 修复JSONUtil.parse()溢出问题(issue#3289@Github)
|
||||||
|
|
||||||
-------------------------------------------------------------------------------------------------------------
|
-------------------------------------------------------------------------------------------------------------
|
||||||
# 5.8.21(2023-07-29)
|
# 5.8.21(2023-07-29)
|
||||||
|
@ -143,7 +143,7 @@ public class CombinationAnnotationElement implements AnnotatedElement, Serializa
|
|||||||
annotationType = annotation.annotationType();
|
annotationType = annotation.annotationType();
|
||||||
// issue#I5FQGW@Gitee:跳过元注解和已经处理过的注解,防止递归调用
|
// issue#I5FQGW@Gitee:跳过元注解和已经处理过的注解,防止递归调用
|
||||||
if (AnnotationUtil.isNotJdkMateAnnotation(annotationType)
|
if (AnnotationUtil.isNotJdkMateAnnotation(annotationType)
|
||||||
&& false == declaredAnnotationMap.containsKey(annotationType)) {
|
&& false == annotationMap.containsKey(annotationType)) {
|
||||||
if(test(annotation)){
|
if(test(annotation)){
|
||||||
annotationMap.put(annotationType, annotation);
|
annotationMap.put(annotationType, annotation);
|
||||||
}
|
}
|
||||||
|
@ -2,8 +2,10 @@ package cn.hutool.core.collection;
|
|||||||
|
|
||||||
import cn.hutool.core.comparator.PinyinComparator;
|
import cn.hutool.core.comparator.PinyinComparator;
|
||||||
import cn.hutool.core.comparator.PropertyComparator;
|
import cn.hutool.core.comparator.PropertyComparator;
|
||||||
|
import cn.hutool.core.exceptions.UtilException;
|
||||||
import cn.hutool.core.lang.Assert;
|
import cn.hutool.core.lang.Assert;
|
||||||
import cn.hutool.core.lang.Matcher;
|
import cn.hutool.core.lang.Matcher;
|
||||||
|
import cn.hutool.core.lang.Validator;
|
||||||
import cn.hutool.core.util.ArrayUtil;
|
import cn.hutool.core.util.ArrayUtil;
|
||||||
import cn.hutool.core.util.ObjectUtil;
|
import cn.hutool.core.util.ObjectUtil;
|
||||||
import cn.hutool.core.util.PageUtil;
|
import cn.hutool.core.util.PageUtil;
|
||||||
@ -431,6 +433,8 @@ public class ListUtil {
|
|||||||
if (index < size) {
|
if (index < size) {
|
||||||
list.set(index, element);
|
list.set(index, element);
|
||||||
} else {
|
} else {
|
||||||
|
// issue#3286, 增加安全检查,最多增加10倍
|
||||||
|
Validator.checkIndexLimit(index, list.size());
|
||||||
for (int i = size; i < index; i++) {
|
for (int i = size; i < index; i++) {
|
||||||
list.add(paddingElement);
|
list.add(paddingElement);
|
||||||
}
|
}
|
||||||
|
@ -193,6 +193,20 @@ public class ConverterRegistry implements Serializable {
|
|||||||
type = defaultValue.getClass();
|
type = defaultValue.getClass();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// issue#I7WJHH,Opt和Optional处理
|
||||||
|
if (value instanceof Opt) {
|
||||||
|
value = ((Opt<T>) value).get();
|
||||||
|
if (ObjUtil.isNull(value)) {
|
||||||
|
return defaultValue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (value instanceof Optional) {
|
||||||
|
value = ((Optional<T>) value).orElse(null);
|
||||||
|
if (ObjUtil.isNull(value)) {
|
||||||
|
return defaultValue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (type instanceof TypeReference) {
|
if (type instanceof TypeReference) {
|
||||||
type = ((TypeReference<?>) type).getType();
|
type = ((TypeReference<?>) type).getType();
|
||||||
}
|
}
|
||||||
|
@ -629,7 +629,12 @@ public class CalendarUtil {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 计算相对于dateToCompare的年龄,常用于计算指定生日在某年的年龄
|
* 计算相对于dateToCompare的年龄,常用于计算指定生日在某年的年龄<br>
|
||||||
|
* 按照《最高人民法院关于审理未成年人刑事案件具体应用法律若干问题的解释》第二条规定刑法第十七条规定的“周岁”,按照公历的年、月、日计算,从周岁生日的第二天起算。
|
||||||
|
* <ul>
|
||||||
|
* <li>2022-03-01出生,则相对2023-03-01,周岁为0,相对于2023-03-02才是1岁。</li>
|
||||||
|
* <li>1999-02-28出生,则相对2000-02-29,周岁为1</li>
|
||||||
|
* </ul>
|
||||||
*
|
*
|
||||||
* @param birthday 生日
|
* @param birthday 生日
|
||||||
* @param dateToCompare 需要对比的日期
|
* @param dateToCompare 需要对比的日期
|
||||||
@ -646,22 +651,22 @@ public class CalendarUtil {
|
|||||||
final int year = cal.get(Calendar.YEAR);
|
final int year = cal.get(Calendar.YEAR);
|
||||||
final int month = cal.get(Calendar.MONTH);
|
final int month = cal.get(Calendar.MONTH);
|
||||||
final int dayOfMonth = cal.get(Calendar.DAY_OF_MONTH);
|
final int dayOfMonth = cal.get(Calendar.DAY_OF_MONTH);
|
||||||
final boolean isLastDayOfMonth = dayOfMonth == cal.getActualMaximum(Calendar.DAY_OF_MONTH);
|
|
||||||
|
|
||||||
|
// 复用cal
|
||||||
cal.setTimeInMillis(birthday);
|
cal.setTimeInMillis(birthday);
|
||||||
int age = year - cal.get(Calendar.YEAR);
|
int age = year - cal.get(Calendar.YEAR);
|
||||||
|
|
||||||
final int monthBirth = cal.get(Calendar.MONTH);
|
|
||||||
|
|
||||||
//当前日期,则为0岁
|
//当前日期,则为0岁
|
||||||
if (age == 0){
|
if (age == 0){
|
||||||
return 0;
|
return 0;
|
||||||
} else if (month == monthBirth) {
|
}
|
||||||
|
|
||||||
|
final int monthBirth = cal.get(Calendar.MONTH);
|
||||||
|
if (month == monthBirth) {
|
||||||
final int dayOfMonthBirth = cal.get(Calendar.DAY_OF_MONTH);
|
final int dayOfMonthBirth = cal.get(Calendar.DAY_OF_MONTH);
|
||||||
final boolean isLastDayOfMonthBirth = dayOfMonthBirth == cal.getActualMaximum(Calendar.DAY_OF_MONTH);
|
// issue#I6E6ZG,法定生日当天不算年龄,从第二天开始计算
|
||||||
if ((false == isLastDayOfMonth || false == isLastDayOfMonthBirth) && dayOfMonth <= dayOfMonthBirth) {
|
if (dayOfMonth <= dayOfMonthBirth) {
|
||||||
// 如果生日在当月,但是未超过生日当天的日期,年龄减一
|
// 如果生日在当月,但是未达到生日当天的日期,年龄减一
|
||||||
age--;
|
age--;
|
||||||
}
|
}
|
||||||
} else if (month < monthBirth) {
|
} else if (month < monthBirth) {
|
||||||
|
@ -97,7 +97,7 @@ public class ConsoleTable {
|
|||||||
*/
|
*/
|
||||||
private void fillColumns(List<String> l, String[] columns) {
|
private void fillColumns(List<String> l, String[] columns) {
|
||||||
for (int i = 0; i < columns.length; i++) {
|
for (int i = 0; i < columns.length; i++) {
|
||||||
String column = columns[i];
|
String column = StrUtil.toString(columns[i]);
|
||||||
if (isSBCMode) {
|
if (isSBCMode) {
|
||||||
column = Convert.toSBC(column);
|
column = Convert.toSBC(column);
|
||||||
}
|
}
|
||||||
|
@ -1257,4 +1257,23 @@ public class Validator {
|
|||||||
}
|
}
|
||||||
return value;
|
return value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 检查给定的index是否超出长度限制,默认检查超出倍数(10倍),此方法主要用于内部,检查包括:
|
||||||
|
* <ul>
|
||||||
|
* <li>数组调用setOrPadding时,最多允许padding的长度</li>
|
||||||
|
* <li>List调用setOrPadding时,最多允许padding的长度</li>
|
||||||
|
* <li>JSONArray调用setOrPadding时,最多允许padding的长度</li>
|
||||||
|
* </ul>
|
||||||
|
*
|
||||||
|
* @param index 索引
|
||||||
|
* @param size 数组、列表长度
|
||||||
|
* @since 5.8.22
|
||||||
|
*/
|
||||||
|
public static void checkIndexLimit(final int index, final int size) {
|
||||||
|
// issue#3286, 增加安全检查,最多增加10倍
|
||||||
|
if (index > (size + 1) * 10) {
|
||||||
|
throw new ValidateException("Index [{}] is too large for size: [{}]", index, size);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -407,7 +407,7 @@ public class Ipv4Util {
|
|||||||
*/
|
*/
|
||||||
public static Long getEndIpLong(String ip, int maskBit) {
|
public static Long getEndIpLong(String ip, int maskBit) {
|
||||||
return getBeginIpLong(ip, maskBit)
|
return getBeginIpLong(ip, maskBit)
|
||||||
+ ~ipv4ToLong(getMaskByMaskBit(maskBit));
|
+ (0xffffffffL & ~ipv4ToLong(getMaskByMaskBit(maskBit)));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -1064,7 +1064,7 @@ public class CharSequenceUtil {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
for (CharSequence checkStr : testStrs) {
|
for (CharSequence checkStr : testStrs) {
|
||||||
if (str.toString().contains(checkStr)) {
|
if (null != checkStr && str.toString().contains(checkStr)) {
|
||||||
return checkStr.toString();
|
return checkStr.toString();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2948,10 +2948,10 @@ public class NumberUtil {
|
|||||||
/**
|
/**
|
||||||
* 检查是否为有效的数字<br>
|
* 检查是否为有效的数字<br>
|
||||||
* 检查Double和Float是否为无限大,或者Not a Number<br>
|
* 检查Double和Float是否为无限大,或者Not a Number<br>
|
||||||
* 非数字类型和Null将返回true
|
* 非数字类型和Null将返回false
|
||||||
*
|
*
|
||||||
* @param number 被检查类型
|
* @param number 被检查类型
|
||||||
* @return 检查结果,非数字类型和Null将返回true
|
* @return 检查结果,非数字类型和Null将返回false
|
||||||
* @since 4.6.7
|
* @since 4.6.7
|
||||||
*/
|
*/
|
||||||
public static boolean isValidNumber(Number number) {
|
public static boolean isValidNumber(Number number) {
|
||||||
|
38
hutool-core/src/test/java/cn/hutool/core/convert/IssueI7WJHHTest.java
Executable file
38
hutool-core/src/test/java/cn/hutool/core/convert/IssueI7WJHHTest.java
Executable file
@ -0,0 +1,38 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2023 looly(loolly@aliyun.com)
|
||||||
|
* Hutool is licensed under Mulan PSL v2.
|
||||||
|
* You can use this software according to the terms and conditions of the Mulan PSL v2.
|
||||||
|
* You may obtain a copy of Mulan PSL v2 at:
|
||||||
|
* http://license.coscl.org.cn/MulanPSL2
|
||||||
|
* THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
|
||||||
|
* EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
|
||||||
|
* MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
|
||||||
|
* See the Mulan PSL v2 for more details.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package cn.hutool.core.convert;
|
||||||
|
|
||||||
|
import cn.hutool.core.lang.Opt;
|
||||||
|
import org.junit.Assert;
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
import java.util.Optional;
|
||||||
|
|
||||||
|
public class IssueI7WJHHTest {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void toIntTest() {
|
||||||
|
final Optional<Integer> optional = Optional.of(1);
|
||||||
|
final Integer integer = Convert.toInt(optional);
|
||||||
|
|
||||||
|
Assert.assertEquals(Integer.valueOf(1), integer);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void toIntTest2() {
|
||||||
|
final Opt<Integer> optional = Opt.of(1);
|
||||||
|
final Integer integer = Convert.toInt(optional);
|
||||||
|
|
||||||
|
Assert.assertEquals(Integer.valueOf(1), integer);
|
||||||
|
}
|
||||||
|
}
|
@ -868,7 +868,9 @@ public class DateUtilTest {
|
|||||||
final String d1 = "2000-02-29";
|
final String d1 = "2000-02-29";
|
||||||
final String d2 = "2018-02-28";
|
final String d2 = "2018-02-28";
|
||||||
final int age = DateUtil.age(DateUtil.parseDate(d1), DateUtil.parseDate(d2));
|
final int age = DateUtil.age(DateUtil.parseDate(d1), DateUtil.parseDate(d2));
|
||||||
Assert.assertEquals(18, age);
|
|
||||||
|
// issue#I6E6ZG,法定生日当天不算年龄,从第二天开始计算
|
||||||
|
Assert.assertEquals(17, age);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test(expected = IllegalArgumentException.class)
|
@Test(expected = IllegalArgumentException.class)
|
||||||
@ -878,6 +880,28 @@ public class DateUtilTest {
|
|||||||
DateUtil.age(DateUtil.parseDate(d1), DateUtil.parseDate(d2));
|
DateUtil.age(DateUtil.parseDate(d1), DateUtil.parseDate(d2));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void ageTest3() {
|
||||||
|
// 按照《最高人民法院关于审理未成年人刑事案件具体应用法律若干问题的解释》第二条规定刑法第十七条规定的“周岁”,按照公历的年、月、日计算,从周岁生日的第二天起算。
|
||||||
|
// 那我们认为就算当年是闰年,29日也算周岁生日的第二天,可以算作一岁
|
||||||
|
final String d1 = "1998-02-28";
|
||||||
|
final String d2 = "2000-02-29";
|
||||||
|
final int age = DateUtil.age(DateUtil.parse(d1), DateUtil.parse(d2));
|
||||||
|
// issue#I6E6ZG,法定生日当天不算年龄,从第二天开始计算
|
||||||
|
Assert.assertEquals(2, age);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void ageTest4() {
|
||||||
|
// 按照《最高人民法院关于审理未成年人刑事案件具体应用法律若干问题的解释》第二条规定刑法第十七条规定的“周岁”,按照公历的年、月、日计算,从周岁生日的第二天起算。
|
||||||
|
// 那我们认为就算当年是闰年,29日也算周岁生日的第二天,可以算作一岁
|
||||||
|
final String d1 = "1999-02-28";
|
||||||
|
final String d2 = "2000-02-29";
|
||||||
|
final int age = DateUtil.age(DateUtil.parse(d1), DateUtil.parse(d2));
|
||||||
|
// issue#I6E6ZG,法定生日当天不算年龄,从第二天开始计算
|
||||||
|
Assert.assertEquals(1, age);
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void isExpiredTest() {
|
public void isExpiredTest() {
|
||||||
final DateTime startDate = DateUtil.parse("2019-12-01 17:02:30");
|
final DateTime startDate = DateUtil.parse("2019-12-01 17:02:30");
|
||||||
|
27
hutool-core/src/test/java/cn/hutool/core/date/IssueI7XMYWTest.java
Executable file
27
hutool-core/src/test/java/cn/hutool/core/date/IssueI7XMYWTest.java
Executable file
@ -0,0 +1,27 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2023 looly(loolly@aliyun.com)
|
||||||
|
* Hutool is licensed under Mulan PSL v2.
|
||||||
|
* You can use this software according to the terms and conditions of the Mulan PSL v2.
|
||||||
|
* You may obtain a copy of Mulan PSL v2 at:
|
||||||
|
* http://license.coscl.org.cn/MulanPSL2
|
||||||
|
* THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
|
||||||
|
* EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
|
||||||
|
* MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
|
||||||
|
* See the Mulan PSL v2 for more details.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package cn.hutool.core.date;
|
||||||
|
|
||||||
|
import org.junit.Assert;
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
public class IssueI7XMYWTest {
|
||||||
|
@Test
|
||||||
|
public void ageTest() {
|
||||||
|
DateTime date1 = DateUtil.parse("2023-08-31");
|
||||||
|
Assert.assertEquals(49, DateUtil.age(DateUtil.parse("1973-08-31"), date1));
|
||||||
|
|
||||||
|
date1 = DateUtil.parse("2023-08-30");
|
||||||
|
Assert.assertEquals(49, DateUtil.age(DateUtil.parse("1973-08-30"), date1));
|
||||||
|
}
|
||||||
|
}
|
@ -220,6 +220,12 @@ public class NumberUtilTest {
|
|||||||
Assert.assertEquals("467.81", format);
|
Assert.assertEquals("467.81", format);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void isValidNumberTest() {
|
||||||
|
boolean validNumber = NumberUtil.isValidNumber(1);
|
||||||
|
Assert.assertTrue(validNumber);
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void decimalFormatMoneyTest() {
|
public void decimalFormatMoneyTest() {
|
||||||
final double c = 299792400.543534534;
|
final double c = 299792400.543534534;
|
||||||
|
@ -412,6 +412,10 @@ public class StrUtilTest {
|
|||||||
Assert.assertFalse(containsAny);
|
Assert.assertFalse(containsAny);
|
||||||
containsAny = StrUtil.containsAny("aaabbbccc", "d", "c");
|
containsAny = StrUtil.containsAny("aaabbbccc", "d", "c");
|
||||||
Assert.assertTrue(containsAny);
|
Assert.assertTrue(containsAny);
|
||||||
|
|
||||||
|
// https://gitee.com/dromara/hutool/issues/I7WSYD
|
||||||
|
containsAny = StrUtil.containsAny("你好啊", "嗯", null);
|
||||||
|
Assert.assertFalse(containsAny);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@ -130,7 +130,8 @@ public class PartParser {
|
|||||||
if (size == 1) {// 普通形式
|
if (size == 1) {// 普通形式
|
||||||
results = parseRange(value, -1);
|
results = parseRange(value, -1);
|
||||||
} else if (size == 2) {// 间隔形式
|
} else if (size == 2) {// 间隔形式
|
||||||
final int step = parseNumber(parts.get(1));
|
// issue#I7SMP7,步进不检查范围
|
||||||
|
final int step = parseNumber(parts.get(1), false);
|
||||||
if (step < 1) {
|
if (step < 1) {
|
||||||
throw new CronException("Non positive divisor for field: [{}]", value);
|
throw new CronException("Non positive divisor for field: [{}]", value);
|
||||||
}
|
}
|
||||||
@ -163,7 +164,7 @@ public class PartParser {
|
|||||||
//根据步进的第一个数字确定起始时间,类似于 12/3则从12(秒、分等)开始
|
//根据步进的第一个数字确定起始时间,类似于 12/3则从12(秒、分等)开始
|
||||||
int minValue = part.getMin();
|
int minValue = part.getMin();
|
||||||
if (false == isMatchAllStr(value)) {
|
if (false == isMatchAllStr(value)) {
|
||||||
minValue = Math.max(minValue, parseNumber(value));
|
minValue = Math.max(minValue, parseNumber(value, true));
|
||||||
} else {
|
} else {
|
||||||
//在全匹配模式下,如果步进不存在,表示步进为1
|
//在全匹配模式下,如果步进不存在,表示步进为1
|
||||||
if (step < 1) {
|
if (step < 1) {
|
||||||
@ -190,15 +191,15 @@ public class PartParser {
|
|||||||
List<String> parts = StrUtil.split(value, '-');
|
List<String> parts = StrUtil.split(value, '-');
|
||||||
int size = parts.size();
|
int size = parts.size();
|
||||||
if (size == 1) {// 普通值
|
if (size == 1) {// 普通值
|
||||||
final int v1 = parseNumber(value);
|
final int v1 = parseNumber(value, true);
|
||||||
if (step > 0) {//类似 20/2的形式
|
if (step > 0) {//类似 20/2的形式
|
||||||
NumberUtil.appendRange(v1, part.getMax(), step, results);
|
NumberUtil.appendRange(v1, part.getMax(), step, results);
|
||||||
} else {
|
} else {
|
||||||
results.add(v1);
|
results.add(v1);
|
||||||
}
|
}
|
||||||
} else if (size == 2) {// range值
|
} else if (size == 2) {// range值
|
||||||
final int v1 = parseNumber(parts.get(0));
|
final int v1 = parseNumber(parts.get(0), true);
|
||||||
final int v2 = parseNumber(parts.get(1));
|
final int v2 = parseNumber(parts.get(1), true);
|
||||||
if (step < 1) {
|
if (step < 1) {
|
||||||
//在range模式下,如果步进不存在,表示步进为1
|
//在range模式下,如果步进不存在,表示步进为1
|
||||||
step = 1;
|
step = 1;
|
||||||
@ -233,10 +234,11 @@ public class PartParser {
|
|||||||
* 解析单个int值,支持别名
|
* 解析单个int值,支持别名
|
||||||
*
|
*
|
||||||
* @param value 被解析的值
|
* @param value 被解析的值
|
||||||
|
* @param checkValue 是否检查值在有效范围内
|
||||||
* @return 解析结果
|
* @return 解析结果
|
||||||
* @throws CronException 当无效数字或无效别名时抛出
|
* @throws CronException 当无效数字或无效别名时抛出
|
||||||
*/
|
*/
|
||||||
private int parseNumber(String value) throws CronException {
|
private int parseNumber(String value, boolean checkValue) throws CronException {
|
||||||
int i;
|
int i;
|
||||||
try {
|
try {
|
||||||
i = Integer.parseInt(value);
|
i = Integer.parseInt(value);
|
||||||
@ -254,13 +256,7 @@ public class PartParser {
|
|||||||
i = Week.SUNDAY.ordinal();
|
i = Week.SUNDAY.ordinal();
|
||||||
}
|
}
|
||||||
|
|
||||||
// issue#I7SMP7
|
return checkValue ? part.checkValue(i) : i;
|
||||||
// 年的形式中,如果类似于*/2,不做范围检查
|
|
||||||
if(Part.YEAR.equals(this.part)){
|
|
||||||
return i;
|
|
||||||
}
|
|
||||||
|
|
||||||
return part.checkValue(i);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -1293,13 +1293,23 @@ public class HttpRequest extends HttpBase<HttpRequest> {
|
|||||||
final UrlBuilder redirectUrl;
|
final UrlBuilder redirectUrl;
|
||||||
String location = httpConnection.header(Header.LOCATION);
|
String location = httpConnection.header(Header.LOCATION);
|
||||||
if (false == HttpUtil.isHttp(location) && false == HttpUtil.isHttps(location)) {
|
if (false == HttpUtil.isHttp(location) && false == HttpUtil.isHttps(location)) {
|
||||||
// issue#I5TPSY
|
// issue#I5TPSY, location可能为相对路径
|
||||||
// location可能为相对路径
|
|
||||||
if (false == location.startsWith("/")) {
|
if (false == location.startsWith("/")) {
|
||||||
location = StrUtil.addSuffixIfNot(this.url.getPathStr(), "/") + location;
|
location = StrUtil.addSuffixIfNot(this.url.getPathStr(), "/") + location;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// issue#3265, 相对路径中可能存在参数,单独处理参数
|
||||||
|
final String query;
|
||||||
|
final List<String> split = StrUtil.split(location, '?', 2);
|
||||||
|
if (split.size() == 2) {
|
||||||
|
// 存在参数
|
||||||
|
location = split.get(0);
|
||||||
|
query = split.get(1);
|
||||||
|
} else {
|
||||||
|
query = null;
|
||||||
|
}
|
||||||
redirectUrl = UrlBuilder.of(this.url.getScheme(), this.url.getHost(), this.url.getPort()
|
redirectUrl = UrlBuilder.of(this.url.getScheme(), this.url.getHost(), this.url.getPort()
|
||||||
, location, null, null, this.charset);
|
, location, query, null, this.charset);
|
||||||
} else {
|
} else {
|
||||||
redirectUrl = UrlBuilder.ofHttpWithoutEncode(location);
|
redirectUrl = UrlBuilder.ofHttpWithoutEncode(location);
|
||||||
}
|
}
|
||||||
|
24
hutool-http/src/test/java/cn/hutool/http/IssueI7WZEOTest.java
Executable file
24
hutool-http/src/test/java/cn/hutool/http/IssueI7WZEOTest.java
Executable file
@ -0,0 +1,24 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2023 looly(loolly@aliyun.com)
|
||||||
|
* Hutool is licensed under Mulan PSL v2.
|
||||||
|
* You can use this software according to the terms and conditions of the Mulan PSL v2.
|
||||||
|
* You may obtain a copy of Mulan PSL v2 at:
|
||||||
|
* http://license.coscl.org.cn/MulanPSL2
|
||||||
|
* THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
|
||||||
|
* EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
|
||||||
|
* MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
|
||||||
|
* See the Mulan PSL v2 for more details.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package cn.hutool.http;
|
||||||
|
|
||||||
|
import cn.hutool.core.lang.Console;
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
public class IssueI7WZEOTest {
|
||||||
|
@Test
|
||||||
|
public void postTest() {
|
||||||
|
final String post = HttpUtil.post("https://tenapi.cn/v2/video", "url=https://v.douyin.com/ie1EX3LH/");
|
||||||
|
Console.log(post);
|
||||||
|
}
|
||||||
|
}
|
@ -3,6 +3,7 @@ package cn.hutool.json;
|
|||||||
import cn.hutool.core.bean.BeanPath;
|
import cn.hutool.core.bean.BeanPath;
|
||||||
import cn.hutool.core.collection.CollUtil;
|
import cn.hutool.core.collection.CollUtil;
|
||||||
import cn.hutool.core.lang.Filter;
|
import cn.hutool.core.lang.Filter;
|
||||||
|
import cn.hutool.core.lang.Validator;
|
||||||
import cn.hutool.core.lang.mutable.Mutable;
|
import cn.hutool.core.lang.mutable.Mutable;
|
||||||
import cn.hutool.core.lang.mutable.MutableObj;
|
import cn.hutool.core.lang.mutable.MutableObj;
|
||||||
import cn.hutool.core.lang.mutable.MutablePair;
|
import cn.hutool.core.lang.mutable.MutablePair;
|
||||||
@ -457,6 +458,8 @@ public class JSONArray implements JSON, JSONGetter<Integer>, List<Object>, Rando
|
|||||||
InternalJSONUtil.testValidity(element);
|
InternalJSONUtil.testValidity(element);
|
||||||
this.rawList.add(index, JSONUtil.wrap(element, this.config));
|
this.rawList.add(index, JSONUtil.wrap(element, this.config));
|
||||||
} else {
|
} else {
|
||||||
|
// issue#3286, 增加安全检查,最多增加10倍
|
||||||
|
Validator.checkIndexLimit(index, this.size());
|
||||||
while (index != this.size()) {
|
while (index != this.size()) {
|
||||||
this.add(JSONNull.NULL);
|
this.add(JSONNull.NULL);
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
package cn.hutool.json;
|
package cn.hutool.json;
|
||||||
|
|
||||||
|
import cn.hutool.core.lang.Console;
|
||||||
import cn.hutool.core.lang.Filter;
|
import cn.hutool.core.lang.Filter;
|
||||||
import cn.hutool.core.lang.mutable.Mutable;
|
import cn.hutool.core.lang.mutable.Mutable;
|
||||||
import cn.hutool.core.lang.mutable.MutablePair;
|
import cn.hutool.core.lang.mutable.MutablePair;
|
||||||
@ -66,7 +67,7 @@ public class JSONParser {
|
|||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
tokener.back();
|
tokener.back();
|
||||||
key = tokener.nextValue().toString();
|
key = tokener.nextStringValue();
|
||||||
}
|
}
|
||||||
|
|
||||||
// The key is followed by ':'.
|
// The key is followed by ':'.
|
||||||
|
@ -322,6 +322,43 @@ public class JSONTokener {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取下一个String格式的值,用户获取key
|
||||||
|
* @return String格式的值
|
||||||
|
* @since 5.8.22
|
||||||
|
*/
|
||||||
|
public String nextStringValue(){
|
||||||
|
char c = this.nextClean();
|
||||||
|
|
||||||
|
switch (c) {
|
||||||
|
case '"':
|
||||||
|
case '\'':
|
||||||
|
return this.nextString(c);
|
||||||
|
case '{':
|
||||||
|
case '[':
|
||||||
|
throw this.syntaxError("Sting value must be not begin with a '{' or '['");
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Handle unquoted text. This could be the values true, false, or null, or it can be a number.
|
||||||
|
* An implementation (such as this one) is allowed to also accept non-standard forms. Accumulate
|
||||||
|
* characters until we reach the end of the text or a formatting character.
|
||||||
|
*/
|
||||||
|
|
||||||
|
final StringBuilder sb = new StringBuilder();
|
||||||
|
while (c >= ' ' && ",:]}/\\\"[{;=#".indexOf(c) < 0) {
|
||||||
|
sb.append(c);
|
||||||
|
c = this.next();
|
||||||
|
}
|
||||||
|
this.back();
|
||||||
|
|
||||||
|
final String string = sb.toString().trim();
|
||||||
|
if (string.isEmpty()) {
|
||||||
|
throw this.syntaxError("Missing value");
|
||||||
|
}
|
||||||
|
return string;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 获得下一个值,值类型可以是Boolean, Double, Integer, JSONArray, JSONObject, Long, or String, or the JSONObject.NULL
|
* 获得下一个值,值类型可以是Boolean, Double, Integer, JSONArray, JSONObject, Long, or String, or the JSONObject.NULL
|
||||||
*
|
*
|
||||||
@ -366,7 +403,7 @@ public class JSONTokener {
|
|||||||
this.back();
|
this.back();
|
||||||
|
|
||||||
string = sb.toString().trim();
|
string = sb.toString().trim();
|
||||||
if (0 == string.length()) {
|
if (string.isEmpty()) {
|
||||||
throw this.syntaxError("Missing value");
|
throw this.syntaxError("Missing value");
|
||||||
}
|
}
|
||||||
return InternalJSONUtil.stringToValue(string);
|
return InternalJSONUtil.stringToValue(string);
|
||||||
|
52
hutool-json/src/test/java/cn/hutool/json/Issue3274Test.java
Executable file
52
hutool-json/src/test/java/cn/hutool/json/Issue3274Test.java
Executable file
@ -0,0 +1,52 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2023 looly(loolly@aliyun.com)
|
||||||
|
* Hutool is licensed under Mulan PSL v2.
|
||||||
|
* You can use this software according to the terms and conditions of the Mulan PSL v2.
|
||||||
|
* You may obtain a copy of Mulan PSL v2 at:
|
||||||
|
* http://license.coscl.org.cn/MulanPSL2
|
||||||
|
* THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
|
||||||
|
* EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
|
||||||
|
* MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
|
||||||
|
* See the Mulan PSL v2 for more details.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package cn.hutool.json;
|
||||||
|
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.Getter;
|
||||||
|
import org.junit.Assert;
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
public class Issue3274Test {
|
||||||
|
@Test
|
||||||
|
public void toBeanTest(){
|
||||||
|
final JSONObject entries = new JSONObject("{\n" +
|
||||||
|
" \n" +
|
||||||
|
" \"age\": 36,\n" +
|
||||||
|
" \"gender\": \"\",\n" +
|
||||||
|
" \"id\": \"123123123\"\n" +
|
||||||
|
"}", JSONConfig.create().setIgnoreError(true));
|
||||||
|
final LarkCoreHrPersonal larkCoreHrPersonal = entries.toBean(LarkCoreHrPersonal.class);
|
||||||
|
Assert.assertNotNull(larkCoreHrPersonal);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Data
|
||||||
|
static class LarkCoreHrPersonal {
|
||||||
|
private String id;
|
||||||
|
private String age="";
|
||||||
|
private Gender gender;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Getter
|
||||||
|
enum Gender {
|
||||||
|
male("male","Male","男"),
|
||||||
|
female("female","Female","女"),
|
||||||
|
other("other","Other","其他");
|
||||||
|
private JSONArray display;
|
||||||
|
private String enum_name;
|
||||||
|
Gender(final String enum_name, final String en_Us, final String zh_CN){
|
||||||
|
this.enum_name=enum_name;
|
||||||
|
this.display=new JSONArray("[{\"lang\": \"en-US\",\"value\": \""+en_Us+"\"},{\"lang\": \"zh-CN\",\"value\": \""+zh_CN+"\"}]");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
23
hutool-json/src/test/java/cn/hutool/json/Issue3289Test.java
Executable file
23
hutool-json/src/test/java/cn/hutool/json/Issue3289Test.java
Executable file
@ -0,0 +1,23 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2023 looly(loolly@aliyun.com)
|
||||||
|
* Hutool is licensed under Mulan PSL v2.
|
||||||
|
* You can use this software according to the terms and conditions of the Mulan PSL v2.
|
||||||
|
* You may obtain a copy of Mulan PSL v2 at:
|
||||||
|
* http://license.coscl.org.cn/MulanPSL2
|
||||||
|
* THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
|
||||||
|
* EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
|
||||||
|
* MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
|
||||||
|
* See the Mulan PSL v2 for more details.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package cn.hutool.json;
|
||||||
|
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
public class Issue3289Test {
|
||||||
|
@Test(expected = JSONException.class)
|
||||||
|
public void parseTest() {
|
||||||
|
final String s = "{\"a\":1,[6E962756779]}";
|
||||||
|
JSONUtil.parse(s);
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user