mirror of
https://gitee.com/chinabugotech/hutool.git
synced 2025-12-06 09:09:10 +08:00
Compare commits
12 Commits
472b0d2841
...
5ae493119f
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
5ae493119f | ||
|
|
ea717843f6 | ||
|
|
b8908cf3ef | ||
|
|
ab1774d4e6 | ||
|
|
ccaecf6bc9 | ||
|
|
3d2dd38add | ||
|
|
d64a0d36aa | ||
|
|
ab936327a2 | ||
|
|
6ef5f1c1fd | ||
|
|
6776ddb29d | ||
|
|
449df10509 | ||
|
|
fc5e1ecff9 |
@ -11,6 +11,8 @@
|
||||
* 【core 】 `Combination`和`Arrangement `重构避免数组频繁拷贝,并避免溢出(pr#4144@Github)
|
||||
* 【core 】 优化`EscapeUtil`,兼容不规范的转义(pr#4150@Github)
|
||||
* 【core 】 优化`ObjectUtil.contains`String改为CharSequence(pr#4154@Github)
|
||||
* 【poi 】 `Word07Writer`增加addText重载,支持字体颜色(pr#1388@Gitee)
|
||||
* 【core 】 增强`HexUtil`自动去除`0x`和`#`前缀(pr#4163@Github)
|
||||
|
||||
### 🐞Bug修复
|
||||
* 【jwt 】 修复verify方法在定义alg为`none`时验证失效问题(issue#4105@Github)
|
||||
@ -31,6 +33,8 @@
|
||||
* 【core 】 修复`URLUtil.url`未断开连接问题(pr#4149@Github)
|
||||
* 【core 】 修复`Bimap.put`重复put问题(pr#4150@Github)
|
||||
* 【core 】 修复`StrUtil.str(ByteBuffer, Charset)` 方法修改入参 `ByteBuffer` 的 `position`,导致入参变化 (pr#4153@Github)
|
||||
* 【core 】 修复`ReflectUtil.newInstanceIfPossible`传入Object逻辑错误(pr#4160@Github)
|
||||
* 【core 】 修复`DateModifier`处理AM和PM的ceiling和round问题(pr#4161@Github)
|
||||
|
||||
-------------------------------------------------------------------------------------------------------------
|
||||
# 5.8.41(2025-10-12)
|
||||
|
||||
@ -73,13 +73,13 @@ public class DateModifier {
|
||||
case ROUND:
|
||||
int min = isAM ? 0 : 12;
|
||||
int max = isAM ? 11 : 23;
|
||||
int href = (max - min) / 2 + 1;
|
||||
int href = min + (max - min) / 2 + 1;
|
||||
int value = calendar.get(Calendar.HOUR_OF_DAY);
|
||||
calendar.set(Calendar.HOUR_OF_DAY, (value < href) ? min : max);
|
||||
break;
|
||||
}
|
||||
// 处理下一级别字段
|
||||
return modify(calendar, dateField + 1, modifyType);
|
||||
return modify(calendar, dateField + 1, modifyType, truncateMillisecond);
|
||||
}
|
||||
|
||||
final int endField = truncateMillisecond ? Calendar.SECOND : Calendar.MILLISECOND;
|
||||
|
||||
@ -57,9 +57,9 @@ public class Combination implements Serializable {
|
||||
* 1. 利用对称性 m = min(m, n-m)
|
||||
* 2. 每一步先乘 BigInteger,再除以当前 i,保证数值不暴涨
|
||||
*
|
||||
* @param n 总数 n(必须 >= 0)
|
||||
* @param m 取出 m(必须 >= 0)
|
||||
* @return C(n, m) 的 BigInteger 精确值;当 m > n 时返回 BigInteger.ZERO
|
||||
* @param n 总数 n(必须 大于等于 0)
|
||||
* @param m 取出 m(必须 大于等于 0)
|
||||
* @return C(n, m) 的 BigInteger 精确值;当 m 大于 n 时返回 BigInteger.ZERO
|
||||
*/
|
||||
public static BigInteger countBig(int n, int m) {
|
||||
if (n < 0 || m < 0) {
|
||||
@ -87,9 +87,10 @@ public class Combination implements Serializable {
|
||||
/**
|
||||
* 安全组合数 long 版本。
|
||||
*
|
||||
* @param n 总数 n(必须 >= 0)
|
||||
* @param m 取出 m(必须 >= 0)
|
||||
* @param n 总数 n(必须 大于等于 0)
|
||||
* @param m 取出 m(必须 大于等于 0)
|
||||
* <p>若结果超出 long 范围,会抛 ArithmeticException,而非溢出。</p>
|
||||
* @return C(n, m) 的 long 精确值;当 m 大于 n 时返回 0L
|
||||
*/
|
||||
public static long countSafe(int n, int m) {
|
||||
BigInteger big = countBig(n, m);
|
||||
|
||||
@ -27,7 +27,7 @@ public class HexUtil {
|
||||
* @return 是否为16进制
|
||||
*/
|
||||
public static boolean isHexNumber(String value) {
|
||||
if(StrUtil.startWith(value, '-')){
|
||||
if (StrUtil.startWith(value, '-')) {
|
||||
// issue#2875
|
||||
return false;
|
||||
}
|
||||
@ -35,7 +35,7 @@ public class HexUtil {
|
||||
if (value.startsWith("0x", index) || value.startsWith("0X", index)) {
|
||||
index += 2;
|
||||
} else if (value.startsWith("#", index)) {
|
||||
index ++;
|
||||
index++;
|
||||
}
|
||||
try {
|
||||
new BigInteger(value.substring(index), 16);
|
||||
@ -304,7 +304,7 @@ public class HexUtil {
|
||||
* @since 5.7.4
|
||||
*/
|
||||
public static int hexToInt(String value) {
|
||||
return Integer.parseInt(value, 16);
|
||||
return Integer.parseInt(removeHexPrefix(value), 16);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -326,7 +326,7 @@ public class HexUtil {
|
||||
* @since 5.7.4
|
||||
*/
|
||||
public static long hexToLong(String value) {
|
||||
return Long.parseLong(value, 16);
|
||||
return Long.parseLong(removeHexPrefix(value), 16);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -352,7 +352,7 @@ public class HexUtil {
|
||||
if (null == hexStr) {
|
||||
return null;
|
||||
}
|
||||
return new BigInteger(hexStr, 16);
|
||||
return new BigInteger(removeHexPrefix(hexStr), 16);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -392,4 +392,24 @@ public class HexUtil {
|
||||
return builder.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* 去除十六进制字符串的常见前缀 0x、0X、#
|
||||
*
|
||||
* @param hexStr 十六进制字符串
|
||||
* @return 去除前缀后的字符串
|
||||
*/
|
||||
private static String removeHexPrefix(String hexStr) {
|
||||
if (StrUtil.length(hexStr) > 1) {
|
||||
final char c0 = hexStr.charAt(0);
|
||||
switch (c0) {
|
||||
case '0':
|
||||
if (hexStr.charAt(1) == 'x' || hexStr.charAt(1) == 'X') {
|
||||
return hexStr.substring(2);
|
||||
}
|
||||
case '#':
|
||||
return hexStr.substring(1);
|
||||
}
|
||||
}
|
||||
return hexStr;
|
||||
}
|
||||
}
|
||||
|
||||
@ -888,13 +888,19 @@ public class ReflectUtil {
|
||||
return (T) ClassUtil.getPrimitiveDefaultValue(type);
|
||||
}
|
||||
|
||||
// 某些特殊接口的实例化按照默认实现进行
|
||||
if (type.isAssignableFrom(AbstractMap.class)) {
|
||||
type = (Class<T>) HashMap.class;
|
||||
} else if (type.isAssignableFrom(List.class)) {
|
||||
type = (Class<T>) ArrayList.class;
|
||||
} else if (type.isAssignableFrom(Set.class)) {
|
||||
type = (Class<T>) HashSet.class;
|
||||
if (Object.class != type) {
|
||||
// 某些特殊接口的实例化按照默认实现进行
|
||||
if (type.isAssignableFrom(AbstractMap.class)) {
|
||||
type = (Class<T>) HashMap.class;
|
||||
} else if (type.isAssignableFrom(List.class)) {
|
||||
type = (Class<T>) ArrayList.class;
|
||||
} else if (type.isAssignableFrom(Set.class)) {
|
||||
type = (Class<T>) HashSet.class;
|
||||
} else if (type.isAssignableFrom(Queue.class)) {
|
||||
type = (Class<T>) LinkedList.class;
|
||||
} else if (type.isAssignableFrom(Deque.class)) {
|
||||
type = (Class<T>) LinkedList.class;
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
|
||||
@ -130,6 +130,33 @@ public class DateUtilTest {
|
||||
assertEquals("2020-02-29 12:59:59.000", dateTime.toString(DatePattern.NORM_DATETIME_MS_PATTERN));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void cellingAmPmTest(){
|
||||
final String dateStr2 = "2020-02-29 10:59:34";
|
||||
final Date date2 = DateUtil.parse(dateStr2);
|
||||
|
||||
|
||||
DateTime dateTime = DateUtil.ceiling(date2, DateField.AM_PM);
|
||||
assertEquals("2020-02-29 11:59:59.999", dateTime.toString(DatePattern.NORM_DATETIME_MS_PATTERN));
|
||||
|
||||
dateTime = DateUtil.ceiling(date2, DateField.AM_PM, true);
|
||||
assertEquals("2020-02-29 11:59:59.000", dateTime.toString(DatePattern.NORM_DATETIME_MS_PATTERN));
|
||||
}
|
||||
|
||||
@Test void roundAmPmTest() {
|
||||
final String dateStr = "2020-02-29 13:59:34";
|
||||
final Date date = DateUtil.parse(dateStr);
|
||||
|
||||
DateTime dateTime = DateUtil.round(date, DateField.AM_PM);
|
||||
assertEquals("2020-02-29 12:59:59.000", dateTime.toString(DatePattern.NORM_DATETIME_MS_PATTERN));
|
||||
|
||||
final String dateStr2 = "2020-02-29 18:59:34";
|
||||
final Date date2 = DateUtil.parse(dateStr2);
|
||||
|
||||
DateTime dateTime2 = DateUtil.round(date2, DateField.AM_PM);
|
||||
assertEquals("2020-02-29 23:59:59.000", dateTime2.toString(DatePattern.NORM_DATETIME_MS_PATTERN));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void ceilingDayTest() {
|
||||
final String dateStr2 = "2020-02-29 12:59:34";
|
||||
|
||||
@ -3,6 +3,7 @@ package cn.hutool.core.util;
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import java.math.BigInteger;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
|
||||
/**
|
||||
@ -85,4 +86,34 @@ public class HexUtilTest {
|
||||
final String s1 = HexUtil.decodeHexStr(s);
|
||||
assertEquals("6", s1);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void hexToIntTest() {
|
||||
final String hex1 = "FF";
|
||||
assertEquals(255, HexUtil.hexToInt(hex1));
|
||||
final String hex2 = "0xFF";
|
||||
assertEquals(255, HexUtil.hexToInt(hex2));
|
||||
final String hex3 = "#FF";
|
||||
assertEquals(255, HexUtil.hexToInt(hex3));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void hexToLongTest() {
|
||||
final String hex1 = "FF";
|
||||
assertEquals(255L, HexUtil.hexToLong(hex1));
|
||||
final String hex2 = "0xFF";
|
||||
assertEquals(255L, HexUtil.hexToLong(hex2));
|
||||
final String hex3 = "#FF";
|
||||
assertEquals(255L, HexUtil.hexToLong(hex3));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void toBigIntegerTest() {
|
||||
final String hex1 = "FF";
|
||||
assertEquals(new BigInteger("FF", 16), HexUtil.toBigInteger(hex1));
|
||||
final String hex2 = "0xFF";
|
||||
assertEquals(new BigInteger("FF", 16), HexUtil.toBigInteger(hex2));
|
||||
final String hex3 = "#FF";
|
||||
assertEquals(new BigInteger("FF", 16), HexUtil.toBigInteger(hex3));
|
||||
}
|
||||
}
|
||||
|
||||
@ -151,7 +151,7 @@ public class ReflectUtilTest {
|
||||
final TestClass testClass = new TestClass();
|
||||
final Method method = ReflectUtil.getMethod(TestClass.class, "setA", int.class);
|
||||
assertThrows(IllegalArgumentException.class,
|
||||
() -> ReflectUtil.invoke(testClass, method, "NaN"));
|
||||
() -> ReflectUtil.invoke(testClass, method, "NaN"));
|
||||
}
|
||||
|
||||
@Test
|
||||
@ -210,6 +210,7 @@ public class ReflectUtilTest {
|
||||
private String n;
|
||||
}
|
||||
|
||||
@SuppressWarnings("UnusedReturnValue")
|
||||
public static Method getMethodWithReturnTypeCheck(final Class<?> clazz, final boolean ignoreCase, final String methodName, final Class<?>... paramTypes) throws SecurityException {
|
||||
if (null == clazz || StrUtil.isBlank(methodName)) {
|
||||
return null;
|
||||
@ -220,9 +221,9 @@ public class ReflectUtilTest {
|
||||
if (ArrayUtil.isNotEmpty(methods)) {
|
||||
for (final Method method : methods) {
|
||||
if (StrUtil.equals(methodName, method.getName(), ignoreCase)
|
||||
&& ClassUtil.isAllAssignableFrom(method.getParameterTypes(), paramTypes)
|
||||
&& (res == null
|
||||
|| res.getReturnType().isAssignableFrom(method.getReturnType()))) {
|
||||
&& ClassUtil.isAllAssignableFrom(method.getParameterTypes(), paramTypes)
|
||||
&& (res == null
|
||||
|| res.getReturnType().isAssignableFrom(method.getReturnType()))) {
|
||||
res = method;
|
||||
}
|
||||
}
|
||||
@ -300,6 +301,7 @@ public class ReflectUtilTest {
|
||||
}
|
||||
|
||||
class C2 extends C1 {
|
||||
@SuppressWarnings("RedundantMethodOverride")
|
||||
@Override
|
||||
public void getA() {
|
||||
|
||||
@ -307,7 +309,7 @@ public class ReflectUtilTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void newInstanceIfPossibleTest(){
|
||||
public void newInstanceIfPossibleTest() {
|
||||
//noinspection ConstantConditions
|
||||
final int intValue = ReflectUtil.newInstanceIfPossible(int.class);
|
||||
assertEquals(0, intValue);
|
||||
@ -330,19 +332,19 @@ public class ReflectUtilTest {
|
||||
|
||||
public static class JdbcDialects {
|
||||
private static final List<Number> DIALECTS =
|
||||
Arrays.asList(1L, 2L, 3L);
|
||||
Arrays.asList(1L, 2L, 3L);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void setFieldValueWithFinalTest() {
|
||||
final String fieldName = "DIALECTS";
|
||||
final List<Number> dialects =
|
||||
Arrays.asList(
|
||||
1,
|
||||
2,
|
||||
3,
|
||||
99
|
||||
);
|
||||
Arrays.asList(
|
||||
1,
|
||||
2,
|
||||
3,
|
||||
99
|
||||
);
|
||||
final Field field = ReflectUtil.getField(JdbcDialects.class, fieldName);
|
||||
ReflectUtil.removeFinalModify(field);
|
||||
ReflectUtil.setFieldValue(JdbcDialects.class, fieldName, dialects);
|
||||
@ -351,24 +353,63 @@ public class ReflectUtilTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void issue2625Test(){
|
||||
public void issue2625Test() {
|
||||
// 内部类继承的情况下父类方法会被定义为桥接方法,因此按照pr#1965@Github判断返回值的继承关系来代替判断桥接。
|
||||
final Method getThis = ReflectUtil.getMethod(A.C.class, "getThis");
|
||||
assertTrue(getThis.isBridge());
|
||||
}
|
||||
|
||||
@SuppressWarnings("InnerClassMayBeStatic")
|
||||
public class A{
|
||||
public class A {
|
||||
|
||||
public class C extends B{
|
||||
public class C extends B {
|
||||
|
||||
}
|
||||
|
||||
protected class B{
|
||||
public B getThis(){
|
||||
protected class B {
|
||||
public B getThis() {
|
||||
return this;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void newInstanceIfPossibleTest2() {
|
||||
// 测试Object.class不应该被错误地实例化为HashMap,应该返回Object实例
|
||||
Object objectInstance = ReflectUtil.newInstanceIfPossible(Object.class);
|
||||
assertNotNull(objectInstance);
|
||||
assertEquals(Object.class, objectInstance.getClass());
|
||||
|
||||
// 测试Map.class能够正确实例化为HashMap
|
||||
Map<?, ?> mapInstance = ReflectUtil.newInstanceIfPossible(Map.class);
|
||||
assertNotNull(mapInstance);
|
||||
assertInstanceOf(HashMap.class, mapInstance);
|
||||
|
||||
// 测试Collection.class能够正确实例化为ArrayList
|
||||
Collection<?> collectionInstance = ReflectUtil.newInstanceIfPossible(Collection.class);
|
||||
assertNotNull(collectionInstance);
|
||||
assertInstanceOf(ArrayList.class, collectionInstance);
|
||||
|
||||
|
||||
// 测试List.class能够正确实例化为ArrayList
|
||||
List<?> listInstance = ReflectUtil.newInstanceIfPossible(List.class);
|
||||
assertNotNull(listInstance);
|
||||
assertInstanceOf(ArrayList.class, listInstance);
|
||||
|
||||
// 测试Set.class能够正确实例化为HashSet
|
||||
Set<?> setInstance = ReflectUtil.newInstanceIfPossible(Set.class);
|
||||
assertNotNull(setInstance);
|
||||
assertInstanceOf(HashSet.class, setInstance);
|
||||
|
||||
// 测试Queue接口能够正确实例化为LinkedList
|
||||
Queue<?> queueInstance = ReflectUtil.newInstanceIfPossible(Queue.class);
|
||||
assertNotNull(queueInstance);
|
||||
assertInstanceOf(LinkedList.class, queueInstance);
|
||||
|
||||
// 测试Deque接口能够正确实例化为LinkedList
|
||||
Deque<?> dequeInstance = ReflectUtil.newInstanceIfPossible(Deque.class);
|
||||
assertNotNull(dequeInstance);
|
||||
assertInstanceOf(LinkedList.class, dequeInstance);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -13,7 +13,7 @@ import org.apache.poi.xwpf.usermodel.XWPFDocument;
|
||||
import org.apache.poi.xwpf.usermodel.XWPFParagraph;
|
||||
import org.apache.poi.xwpf.usermodel.XWPFRun;
|
||||
|
||||
import java.awt.Font;
|
||||
import java.awt.*;
|
||||
import java.io.Closeable;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
@ -102,7 +102,19 @@ public class Word07Writer implements Closeable {
|
||||
* @return this
|
||||
*/
|
||||
public Word07Writer addText(Font font, String... texts) {
|
||||
return addText(null, font, texts);
|
||||
return addText(null, font, null, texts);
|
||||
}
|
||||
|
||||
/**
|
||||
* 增加一个段落
|
||||
*
|
||||
* @param font 字体信息{@link Font}
|
||||
* @param color 字体颜色{@link Color}
|
||||
* @param texts 段落中的文本,支持多个文本作为一个段落
|
||||
* @return this
|
||||
*/
|
||||
public Word07Writer addText(Font font, Color color, String... texts) {
|
||||
return addText(null, font, color, texts);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -114,6 +126,20 @@ public class Word07Writer implements Closeable {
|
||||
* @return this
|
||||
*/
|
||||
public Word07Writer addText(ParagraphAlignment align, Font font, String... texts) {
|
||||
return addText(align, font, null, texts);
|
||||
}
|
||||
|
||||
/**
|
||||
* 增加一个段落
|
||||
*
|
||||
* @param align 段落对齐方式{@link ParagraphAlignment}
|
||||
* @param font 字体信息{@link Font}
|
||||
* @param color 字体颜色{@link Color}
|
||||
* @param texts 段落中的文本,支持多个文本作为一个段落
|
||||
* @return this
|
||||
* @since 5.8.42
|
||||
*/
|
||||
public Word07Writer addText(ParagraphAlignment align, Font font, Color color, String... texts) {
|
||||
final XWPFParagraph p = this.doc.createParagraph();
|
||||
if (null != align) {
|
||||
p.setAlignment(align);
|
||||
@ -129,6 +155,10 @@ public class Word07Writer implements Closeable {
|
||||
run.setBold(font.isBold());
|
||||
run.setItalic(font.isItalic());
|
||||
}
|
||||
if (null != color) {
|
||||
String hexColor = String.format("%02X", color.getRGB());
|
||||
run.setColor(hexColor);
|
||||
}
|
||||
}
|
||||
}
|
||||
return this;
|
||||
|
||||
@ -8,7 +8,7 @@ import cn.hutool.core.lang.Console;
|
||||
import org.junit.jupiter.api.Disabled;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import java.awt.Font;
|
||||
import java.awt.*;
|
||||
import java.io.File;
|
||||
import java.util.ArrayList;
|
||||
import java.util.LinkedHashMap;
|
||||
@ -23,6 +23,7 @@ public class WordWriterTest {
|
||||
Word07Writer writer = new Word07Writer();
|
||||
writer.addText(new Font("方正小标宋简体", Font.PLAIN, 22), "我是第一部分", "我是第二部分");
|
||||
writer.addText(new Font("宋体", Font.PLAIN, 22), "我是正文第一部分", "我是正文第二部分");
|
||||
writer.addText(new Font("宋体", Font.PLAIN, 22), Color.RED, "我是正文第三部分", "我是正文第四部分");
|
||||
writer.flush(FileUtil.file("e:/wordWrite.docx"));
|
||||
writer.close();
|
||||
Console.log("OK");
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user