mirror of
https://gitee.com/chinabugotech/hutool.git
synced 2025-05-09 23:51:34 +08:00
Merge remote-tracking branch 'origin/v5-dev' into v5-dev
This commit is contained in:
commit
7242d6992f
18
CHANGELOG.md
18
CHANGELOG.md
@ -2,12 +2,28 @@
|
||||
# 🚀Changelog
|
||||
|
||||
-------------------------------------------------------------------------------------------------------------
|
||||
# 5.7.21 (2022-01-21)
|
||||
# 5.7.21 (2022-02-07)
|
||||
|
||||
### 🐣新特性
|
||||
* 【extra 】 增加jetbrick模板支持
|
||||
* 【extra 】 EmojiUtil增加方法(pr#519@Gitee)
|
||||
* 【core 】 DateUtil 添加两个日期是否同一周方法(pr#516@Gitee)
|
||||
* 【db 】 新增条件组,用于处理复杂的where条件(pr#514@Gitee)
|
||||
* 【core 】 新增LocalDateTimeUtil.weekOfYear(issue#I4RWXC@Gitee)
|
||||
* 【core 】 Month增加toJdkMonth、getValueBaseOne
|
||||
* 【core 】 CsvWriter修改规则,去除末尾多余换行符(issue#I4RSQY@Gitee)
|
||||
* 【core 】 DateUtil增加rangeFunc和rangeConsume(issue#I4RSQY@Gitee)
|
||||
* 【core 】 DateTime增加setUseJdkToStringStyle方法
|
||||
* 【core 】 CharSequenceUtil增加replace重载(issue#2122@Github)
|
||||
* 【core 】 IntMap和LongMap使用位运算快速求解取余运算(pr#2123@Github)
|
||||
* 【core 】 新增通用builder类:GenericBuilder(pr#526@Gitee)
|
||||
* 【core 】 新增copySafely方法与mkdirsSafely方法(pr#527@Gitee)
|
||||
|
||||
### 🐞Bug修复
|
||||
* 【core 】 修复ChineseDate农历获取正月出现数组越界BUG(issue#2112@Github)
|
||||
* 【extra 】 修复EmojiUtil.toHtmlHex()方法(pr#519@Gitee)
|
||||
* 【system 】 修复CpuInfo.getUsed()方法(issue#2116@Github)
|
||||
* 【dfa 】 修复密集匹配和贪婪匹配冲突问题(issue#2126@Github)
|
||||
|
||||
-------------------------------------------------------------------------------------------------------------
|
||||
# 5.7.20 (2022-01-20)
|
||||
|
@ -7,5 +7,5 @@ echo ' / /_/ // / / // __// __ \ / __ \ / / '
|
||||
echo ' / __ // /_/ // /_ / /_/ // /_/ // / '
|
||||
echo '/_/ /_/ \____/ \__/ \____/ \____//_/ '
|
||||
echo ''
|
||||
echo '-----------http://hutool.cn/------------'
|
||||
echo '-----------https://hutool.cn/-----------'
|
||||
echo '========================================'
|
||||
|
@ -32,22 +32,22 @@ public class IntMap implements BitMap, Serializable {
|
||||
@Override
|
||||
public void add(long i) {
|
||||
int r = (int) (i / BitMap.MACHINE32);
|
||||
int c = (int) (i % BitMap.MACHINE32);
|
||||
int c = (int) (i & (BitMap.MACHINE32 - 1));
|
||||
ints[r] = ints[r] | (1 << c);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean contains(long i) {
|
||||
int r = (int) (i / BitMap.MACHINE32);
|
||||
int c = (int) (i % BitMap.MACHINE32);
|
||||
int c = (int) (i & (BitMap.MACHINE32 - 1));
|
||||
return ((ints[r] >>> c) & 1) == 1;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void remove(long i) {
|
||||
int r = (int) (i / BitMap.MACHINE32);
|
||||
int c = (int) (i % BitMap.MACHINE32);
|
||||
int c = (int) (i & (BitMap.MACHINE32 - 1));
|
||||
ints[r] &= ~(1 << c);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
@ -32,22 +32,22 @@ public class LongMap implements BitMap, Serializable {
|
||||
@Override
|
||||
public void add(long i) {
|
||||
int r = (int) (i / BitMap.MACHINE64);
|
||||
long c = i % BitMap.MACHINE64;
|
||||
long c = i & (BitMap.MACHINE64 - 1);
|
||||
longs[r] = longs[r] | (1L << c);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean contains(long i) {
|
||||
int r = (int) (i / BitMap.MACHINE64);
|
||||
long c = i % BitMap.MACHINE64;
|
||||
long c = i & (BitMap.MACHINE64 - 1);
|
||||
return ((longs[r] >>> c) & 1) == 1;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void remove(long i) {
|
||||
int r = (int) (i / BitMap.MACHINE64);
|
||||
long c = i % BitMap.MACHINE64;
|
||||
long c = i & (BitMap.MACHINE64 - 1);
|
||||
longs[r] &= ~(1L << c);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
@ -6,8 +6,8 @@ import cn.hutool.core.lang.func.Func0;
|
||||
/**
|
||||
* Bean属性缓存<br>
|
||||
* 缓存用于防止多次反射造成的性能问题
|
||||
* @author Looly
|
||||
*
|
||||
* @author Looly
|
||||
*/
|
||||
public enum BeanDescCache {
|
||||
INSTANCE;
|
||||
@ -16,12 +16,22 @@ public enum BeanDescCache {
|
||||
|
||||
/**
|
||||
* 获得属性名和{@link BeanDesc}Map映射
|
||||
*
|
||||
* @param beanClass Bean的类
|
||||
* @param supplier 对象不存在时创建对象的函数
|
||||
* @param supplier 对象不存在时创建对象的函数
|
||||
* @return 属性名和{@link BeanDesc}映射
|
||||
* @since 5.4.2
|
||||
*/
|
||||
public BeanDesc getBeanDesc(Class<?> beanClass, Func0<BeanDesc> supplier){
|
||||
public BeanDesc getBeanDesc(Class<?> beanClass, Func0<BeanDesc> supplier) {
|
||||
return bdCache.get(beanClass, supplier);
|
||||
}
|
||||
|
||||
/**
|
||||
* 清空全局的Bean属性缓存
|
||||
*
|
||||
* @since 5.7.21
|
||||
*/
|
||||
public void clear() {
|
||||
this.bdCache.clear();
|
||||
}
|
||||
}
|
||||
|
@ -56,6 +56,16 @@ public enum BeanInfoCache {
|
||||
getCache(ignoreCase).put(beanClass, fieldNamePropertyDescriptorMap);
|
||||
}
|
||||
|
||||
/**
|
||||
* 清空缓存
|
||||
*
|
||||
* @since 5.7.21
|
||||
*/
|
||||
public void clear() {
|
||||
this.pdCache.clear();
|
||||
this.ignoreCasePdCache.clear();
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据是否忽略字段名的大小写,返回不用Cache对象
|
||||
*
|
||||
|
@ -0,0 +1,208 @@
|
||||
package cn.hutool.core.builder;
|
||||
|
||||
import cn.hutool.core.lang.func.Supplier1;
|
||||
import cn.hutool.core.lang.func.Supplier2;
|
||||
import cn.hutool.core.lang.func.Supplier3;
|
||||
import cn.hutool.core.lang.func.Supplier4;
|
||||
import cn.hutool.core.lang.func.Supplier5;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.function.BiConsumer;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
/**
|
||||
* <p>通用Builder</p>
|
||||
* 参考: <a href="https://blog.csdn.net/weixin_43935907/article/details/105003719">一看就会的java8通用Builder</a>
|
||||
* <p>使用方法如下:</p>
|
||||
* <pre>
|
||||
* Box box = GenericBuilder
|
||||
* .of(Box::new)
|
||||
* .with(Box::setId, 1024L)
|
||||
* .with(Box::setTitle, "Hello World!")
|
||||
* .with(Box::setLength, 9)
|
||||
* .with(Box::setWidth, 8)
|
||||
* .with(Box::setHeight, 7)
|
||||
* .build();
|
||||
*
|
||||
* </pre>
|
||||
*
|
||||
* <p> 我们也可以对已创建的对象进行修改:</p>
|
||||
* <pre>
|
||||
* Box boxModified = GenericBuilder
|
||||
* .of(() -> box)
|
||||
* .with(Box::setTitle, "Hello Friend!")
|
||||
* .with(Box::setLength, 3)
|
||||
* .with(Box::setWidth, 4)
|
||||
* .with(Box::setHeight, 5)
|
||||
* .build();
|
||||
* </pre>
|
||||
* <p> 我们还可以对这样调用有参构造,这对于创建一些在有参构造中包含初始化函数的对象是有意义的:</p>
|
||||
* <pre>
|
||||
* Box box1 = GenericBuilder
|
||||
* .of(Box::new, 2048L, "Hello Partner!", 222, 333, 444)
|
||||
* .with(Box::alis)
|
||||
* .build();
|
||||
* </pre>
|
||||
* <p>注意:本工具类支持调用的方法的参数数量不超过1个,更多的参数不利于阅读和维护。</p>
|
||||
*
|
||||
* @author TomXin
|
||||
* @since 5.7.21
|
||||
*/
|
||||
public class GenericBuilder<T> implements Builder<T> {
|
||||
|
||||
/**
|
||||
* 实例化器
|
||||
*/
|
||||
private final Supplier<T> instant;
|
||||
|
||||
/**
|
||||
* 修改器列表
|
||||
*/
|
||||
private final List<Consumer<T>> modifiers = new ArrayList<>();
|
||||
|
||||
/**
|
||||
* 构造
|
||||
*
|
||||
* @param instant 实例化器
|
||||
*/
|
||||
public GenericBuilder(Supplier<T> instant) {
|
||||
this.instant = instant;
|
||||
}
|
||||
|
||||
/**
|
||||
* 通过无参数实例化器创建GenericBuilder
|
||||
*
|
||||
* @param instant 实例化器
|
||||
* @param <T> 目标类型
|
||||
* @return GenericBuilder对象
|
||||
*/
|
||||
public static <T> GenericBuilder<T> of(Supplier<T> instant) {
|
||||
return new GenericBuilder<>(instant);
|
||||
}
|
||||
|
||||
/**
|
||||
* 通过1参数实例化器创建GenericBuilder
|
||||
*
|
||||
* @param instant 实例化器
|
||||
* @param p1 参数一
|
||||
* @param <T> 目标类型
|
||||
* @param <P1> 参数一类型
|
||||
* @return GenericBuilder对象
|
||||
*/
|
||||
public static <T, P1> GenericBuilder<T> of(Supplier1<T, P1> instant, P1 p1) {
|
||||
return of(instant.toSupplier(p1));
|
||||
}
|
||||
|
||||
/**
|
||||
* 通过2参数实例化器创建GenericBuilder
|
||||
*
|
||||
* @param instant 实例化器
|
||||
* @param p1 参数一
|
||||
* @param p2 参数二
|
||||
* @param <T> 目标类型
|
||||
* @param <P1> 参数一类型
|
||||
* @param <P2> 参数二类型
|
||||
* @return GenericBuilder对象
|
||||
*/
|
||||
public static <T, P1, P2> GenericBuilder<T> of(Supplier2<T, P1, P2> instant, P1 p1, P2 p2) {
|
||||
return of(instant.toSupplier(p1, p2));
|
||||
}
|
||||
|
||||
/**
|
||||
* 通过3参数实例化器创建GenericBuilder
|
||||
*
|
||||
* @param instant 实例化器
|
||||
* @param p1 参数一
|
||||
* @param p2 参数二
|
||||
* @param p3 参数三
|
||||
* @param <T> 目标类型
|
||||
* @param <P1> 参数一类型
|
||||
* @param <P2> 参数二类型
|
||||
* @param <P3> 参数三类型
|
||||
* @return GenericBuilder对象
|
||||
*/
|
||||
public static <T, P1, P2, P3> GenericBuilder<T> of(Supplier3<T, P1, P2, P3> instant, P1 p1, P2 p2, P3 p3) {
|
||||
return of(instant.toSupplier(p1, p2, p3));
|
||||
}
|
||||
|
||||
/**
|
||||
* 通过4参数实例化器创建GenericBuilder
|
||||
*
|
||||
* @param instant 实例化器
|
||||
* @param p1 参数一
|
||||
* @param p2 参数二
|
||||
* @param p3 参数三
|
||||
* @param p4 参数四
|
||||
* @param <T> 目标类型
|
||||
* @param <P1> 参数一类型
|
||||
* @param <P2> 参数二类型
|
||||
* @param <P3> 参数三类型
|
||||
* @param <P4> 参数四类型
|
||||
* @return GenericBuilder对象
|
||||
*/
|
||||
public static <T, P1, P2, P3, P4> GenericBuilder<T> of(Supplier4<T, P1, P2, P3, P4> instant, P1 p1, P2 p2, P3 p3, P4 p4) {
|
||||
return of(instant.toSupplier(p1, p2, p3, p4));
|
||||
}
|
||||
|
||||
/**
|
||||
* 通过5参数实例化器创建GenericBuilder
|
||||
*
|
||||
* @param instant 实例化器
|
||||
* @param p1 参数一
|
||||
* @param p2 参数二
|
||||
* @param p3 参数三
|
||||
* @param p4 参数四
|
||||
* @param p5 参数五
|
||||
* @param <T> 目标类型
|
||||
* @param <P1> 参数一类型
|
||||
* @param <P2> 参数二类型
|
||||
* @param <P3> 参数三类型
|
||||
* @param <P4> 参数四类型
|
||||
* @param <P5> 参数五类型
|
||||
* @return GenericBuilder对象
|
||||
*/
|
||||
public static <T, P1, P2, P3, P4, P5> GenericBuilder<T> of(Supplier5<T, P1, P2, P3, P4, P5> instant, P1 p1, P2 p2, P3 p3, P4 p4, P5 p5) {
|
||||
return of(instant.toSupplier(p1, p2, p3, p4, p5));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 调用无参数方法
|
||||
*
|
||||
* @param consumer 无参数Consumer
|
||||
* @return GenericBuilder对象
|
||||
*/
|
||||
public GenericBuilder<T> with(Consumer<T> consumer) {
|
||||
modifiers.add(consumer);
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 调用1参数方法
|
||||
*
|
||||
* @param <P1> 参数一类型
|
||||
* @param consumer 1参数Consumer,一般为Setter方法引用
|
||||
* @param p1 参数一
|
||||
* @return GenericBuilder对象
|
||||
*/
|
||||
public <P1> GenericBuilder<T> with(BiConsumer<T, P1> consumer, P1 p1) {
|
||||
modifiers.add(instant -> consumer.accept(instant, p1));
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 构建
|
||||
*
|
||||
* @return 目标对象
|
||||
*/
|
||||
@Override
|
||||
public T build() {
|
||||
T value = instant.get();
|
||||
modifiers.forEach(modifier -> modifier.accept(value));
|
||||
modifiers.clear();
|
||||
return value;
|
||||
}
|
||||
}
|
@ -132,7 +132,7 @@ public class CalendarUtil {
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取秒级别的开始时间,即忽略毫秒部分
|
||||
* 修改秒级别的开始时间,即忽略毫秒部分
|
||||
*
|
||||
* @param calendar 日期 {@link Calendar}
|
||||
* @return {@link Calendar}
|
||||
@ -143,7 +143,7 @@ public class CalendarUtil {
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取秒级别的结束时间,即毫秒设置为999
|
||||
* 修改秒级别的结束时间,即毫秒设置为999
|
||||
*
|
||||
* @param calendar 日期 {@link Calendar}
|
||||
* @return {@link Calendar}
|
||||
@ -154,7 +154,7 @@ public class CalendarUtil {
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取某小时的开始时间
|
||||
* 修改某小时的开始时间
|
||||
*
|
||||
* @param calendar 日期 {@link Calendar}
|
||||
* @return {@link Calendar}
|
||||
@ -164,7 +164,7 @@ public class CalendarUtil {
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取某小时的结束时间
|
||||
* 修改某小时的结束时间
|
||||
*
|
||||
* @param calendar 日期 {@link Calendar}
|
||||
* @return {@link Calendar}
|
||||
@ -174,7 +174,7 @@ public class CalendarUtil {
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取某分钟的开始时间
|
||||
* 修改某分钟的开始时间
|
||||
*
|
||||
* @param calendar 日期 {@link Calendar}
|
||||
* @return {@link Calendar}
|
||||
@ -184,7 +184,7 @@ public class CalendarUtil {
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取某分钟的结束时间
|
||||
* 修改某分钟的结束时间
|
||||
*
|
||||
* @param calendar 日期 {@link Calendar}
|
||||
* @return {@link Calendar}
|
||||
@ -194,7 +194,7 @@ public class CalendarUtil {
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取某天的开始时间
|
||||
* 修改某天的开始时间
|
||||
*
|
||||
* @param calendar 日期 {@link Calendar}
|
||||
* @return {@link Calendar}
|
||||
@ -204,7 +204,7 @@ public class CalendarUtil {
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取某天的结束时间
|
||||
* 修改某天的结束时间
|
||||
*
|
||||
* @param calendar 日期 {@link Calendar}
|
||||
* @return {@link Calendar}
|
||||
@ -214,7 +214,7 @@ public class CalendarUtil {
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取给定日期当前周的开始时间,周一定为一周的开始时间
|
||||
* 修改给定日期当前周的开始时间,周一定为一周的开始时间
|
||||
*
|
||||
* @param calendar 日期 {@link Calendar}
|
||||
* @return {@link Calendar}
|
||||
@ -224,7 +224,7 @@ public class CalendarUtil {
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取给定日期当前周的开始时间
|
||||
* 修改给定日期当前周的开始时间
|
||||
*
|
||||
* @param calendar 日期 {@link Calendar}
|
||||
* @param isMondayAsFirstDay 是否周一做为一周的第一天(false表示周日做为第一天)
|
||||
@ -238,7 +238,7 @@ public class CalendarUtil {
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取某周的结束时间,周日定为一周的结束
|
||||
* 修改某周的结束时间,周日定为一周的结束
|
||||
*
|
||||
* @param calendar 日期 {@link Calendar}
|
||||
* @return {@link Calendar}
|
||||
@ -248,7 +248,7 @@ public class CalendarUtil {
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取某周的结束时间
|
||||
* 修改某周的结束时间
|
||||
*
|
||||
* @param calendar 日期 {@link Calendar}
|
||||
* @param isSundayAsLastDay 是否周日做为一周的最后一天(false表示周六做为最后一天)
|
||||
@ -261,7 +261,7 @@ public class CalendarUtil {
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取某月的开始时间
|
||||
* 修改某月的开始时间
|
||||
*
|
||||
* @param calendar 日期 {@link Calendar}
|
||||
* @return {@link Calendar}
|
||||
@ -271,7 +271,7 @@ public class CalendarUtil {
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取某月的结束时间
|
||||
* 修改某月的结束时间
|
||||
*
|
||||
* @param calendar 日期 {@link Calendar}
|
||||
* @return {@link Calendar}
|
||||
@ -281,7 +281,7 @@ public class CalendarUtil {
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取某季度的开始时间
|
||||
* 修改某季度的开始时间
|
||||
*
|
||||
* @param calendar 日期 {@link Calendar}
|
||||
* @return {@link Calendar}
|
||||
@ -313,7 +313,7 @@ public class CalendarUtil {
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取某年的开始时间
|
||||
* 修改某年的开始时间
|
||||
*
|
||||
* @param calendar 日期 {@link Calendar}
|
||||
* @return {@link Calendar}
|
||||
@ -323,7 +323,7 @@ public class CalendarUtil {
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取某年的结束时间
|
||||
* 修改某年的结束时间
|
||||
*
|
||||
* @param calendar 日期 {@link Calendar}
|
||||
* @return {@link Calendar}
|
||||
@ -348,6 +348,40 @@ public class CalendarUtil {
|
||||
cal1.get(Calendar.ERA) == cal2.get(Calendar.ERA);
|
||||
}
|
||||
|
||||
/**
|
||||
* 比较两个日期是否为同一周
|
||||
*
|
||||
* @param cal1 日期1
|
||||
* @param cal2 日期2
|
||||
* @param isMon 是否为周一。国内第一天为星期一,国外第一天为星期日
|
||||
* @return 是否为同一周
|
||||
* @since 5.7.21
|
||||
*/
|
||||
public static boolean isSameWeek(Calendar cal1, Calendar cal2, boolean isMon) {
|
||||
if (cal1 == null || cal2 == null) {
|
||||
throw new IllegalArgumentException("The date must not be null");
|
||||
}
|
||||
|
||||
// 防止比较前修改原始Calendar对象
|
||||
cal1 = (Calendar) cal1.clone();
|
||||
cal2 = (Calendar) cal2.clone();
|
||||
|
||||
// 把所传日期设置为其当前周的第一天
|
||||
// 比较设置后的两个日期是否是同一天:true 代表同一周
|
||||
if (isMon) {
|
||||
cal1.setFirstDayOfWeek(Calendar.MONDAY);
|
||||
cal1.set(Calendar.DAY_OF_WEEK, Calendar.MONDAY);
|
||||
cal2.setFirstDayOfWeek(Calendar.MONDAY);
|
||||
cal2.set(Calendar.DAY_OF_WEEK, Calendar.MONDAY);
|
||||
} else {
|
||||
cal1.setFirstDayOfWeek(Calendar.SUNDAY);
|
||||
cal1.set(Calendar.DAY_OF_WEEK, Calendar.SUNDAY);
|
||||
cal2.setFirstDayOfWeek(Calendar.SUNDAY);
|
||||
cal2.set(Calendar.DAY_OF_WEEK, Calendar.SUNDAY);
|
||||
}
|
||||
return isSameDay(cal1, cal2);
|
||||
}
|
||||
|
||||
/**
|
||||
* 比较两个日期是否为同一月
|
||||
*
|
||||
|
@ -24,13 +24,30 @@ import java.util.Locale;
|
||||
import java.util.TimeZone;
|
||||
|
||||
/**
|
||||
* 包装java.util.Date
|
||||
* 包装{@link Date}<br>
|
||||
* 此类继承了{@link Date},并提供扩展方法,如时区等。<br>
|
||||
* 此类重写了父类的{@code toString()}方法,返回值为"yyyy-MM-dd HH:mm:ss"格式
|
||||
*
|
||||
* @author xiaoleilu
|
||||
*/
|
||||
public class DateTime extends Date {
|
||||
private static final long serialVersionUID = -5395712593979185936L;
|
||||
|
||||
private static boolean useJdkToStringStyle = false;
|
||||
|
||||
/**
|
||||
* 设置全局的,是否使用{@link Date}默认的toString()格式<br>
|
||||
* 如果为{@code true},则调用toString()时返回"EEE MMM dd HH:mm:ss zzz yyyy"格式,<br>
|
||||
* 如果为{@code false},则返回"yyyy-MM-dd HH:mm:ss",<br>
|
||||
* 默认为{@code false}
|
||||
*
|
||||
* @param customUseJdkToStringStyle 是否使用{@link Date}默认的toString()格式
|
||||
* @since 5.7.21
|
||||
*/
|
||||
public static void setUseJdkToStringStyle(boolean customUseJdkToStringStyle){
|
||||
useJdkToStringStyle = customUseJdkToStringStyle;
|
||||
}
|
||||
|
||||
/**
|
||||
* 是否可变对象
|
||||
*/
|
||||
@ -932,13 +949,18 @@ public class DateTime extends Date {
|
||||
// -------------------------------------------------------------------- toString start
|
||||
|
||||
/**
|
||||
* 转为"yyyy-MM-dd HH:mm:ss" 格式字符串<br>
|
||||
* 如果时区被设置,会转换为其时区对应的时间,否则转换为当前地点对应的时区
|
||||
* 转为字符串,如果时区被设置,会转换为其时区对应的时间,否则转换为当前地点对应的时区<br>
|
||||
* 可以调用{@link DateTime#setUseJdkToStringStyle(boolean)} 方法自定义默认的风格<br>
|
||||
* 如果{@link #useJdkToStringStyle}为{@code true},返回"EEE MMM dd HH:mm:ss zzz yyyy"格式,<br>
|
||||
* 如果为{@code false},则返回"yyyy-MM-dd HH:mm:ss"
|
||||
*
|
||||
* @return "yyyy-MM-dd HH:mm:ss" 格式字符串
|
||||
* @return 格式字符串
|
||||
*/
|
||||
@Override
|
||||
public String toString() {
|
||||
if(useJdkToStringStyle){
|
||||
return super.toString();
|
||||
}
|
||||
return toString(this.timeZone);
|
||||
}
|
||||
|
||||
|
@ -20,7 +20,9 @@ import java.time.LocalDateTime;
|
||||
import java.time.Year;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
import java.time.temporal.TemporalAccessor;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Calendar;
|
||||
import java.util.Collections;
|
||||
import java.util.Date;
|
||||
import java.util.GregorianCalendar;
|
||||
import java.util.LinkedHashSet;
|
||||
@ -28,6 +30,8 @@ import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.TimeZone;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.function.Function;
|
||||
|
||||
/**
|
||||
* 时间工具类
|
||||
@ -1586,6 +1590,21 @@ public class DateUtil extends CalendarUtil {
|
||||
return CalendarUtil.isSameDay(calendar(date1), calendar(date2));
|
||||
}
|
||||
|
||||
/**
|
||||
* 比较两个日期是否为同一周
|
||||
*
|
||||
* @param date1 日期1
|
||||
* @param date2 日期2
|
||||
* @param isMon 是否为周一。国内第一天为星期一,国外第一天为星期日
|
||||
* @return 是否为同一周
|
||||
*/
|
||||
public static boolean isSameWeek(final Date date1, final Date date2, boolean isMon) {
|
||||
if (date1 == null || date2 == null) {
|
||||
throw new IllegalArgumentException("The date must not be null");
|
||||
}
|
||||
return CalendarUtil.isSameWeek(calendar(date1), calendar(date2), isMon);
|
||||
}
|
||||
|
||||
/**
|
||||
* 比较两个日期是否为同一月
|
||||
*
|
||||
@ -1870,6 +1889,44 @@ public class DateUtil extends CalendarUtil {
|
||||
return new DateRange(start, end, unit);
|
||||
}
|
||||
|
||||
/**
|
||||
* 按日期范围遍历,执行 function
|
||||
*
|
||||
* @param start 起始日期时间(包括)
|
||||
* @param end 结束日期时间
|
||||
* @param unit 步进单位
|
||||
* @param func 每次遍历要执行的 function
|
||||
* @param <T> Date经过函数处理结果类型
|
||||
* @return 结果列表
|
||||
* @since 5.7.21
|
||||
*/
|
||||
public static <T> List<T> rangeFunc(Date start, Date end, final DateField unit, Function<Date, T> func) {
|
||||
if (start == null || end == null || start.after(end)) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
ArrayList<T> list = new ArrayList<>();
|
||||
for(DateTime date : range(start, end, unit)){
|
||||
list.add(func.apply(date));
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
||||
/**
|
||||
* 按日期范围遍历,执行 consumer
|
||||
*
|
||||
* @param start 起始日期时间(包括)
|
||||
* @param end 结束日期时间
|
||||
* @param unit 步进单位
|
||||
* @param consumer 每次遍历要执行的 consumer
|
||||
* @since 5.7.21
|
||||
*/
|
||||
public static void rangeConsume(Date start, Date end, final DateField unit, Consumer<Date> consumer) {
|
||||
if (start == null || end == null || start.after(end)) {
|
||||
return;
|
||||
}
|
||||
range(start, end, unit).forEach(consumer);
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建日期范围生成器
|
||||
*
|
||||
@ -1878,7 +1935,7 @@ public class DateUtil extends CalendarUtil {
|
||||
* @param unit 步进单位
|
||||
* @return {@link DateRange}
|
||||
*/
|
||||
public static List<DateTime> rangeToList(Date start, Date end, final DateField unit) {
|
||||
public static List<DateTime> rangeToList(Date start, Date end, DateField unit) {
|
||||
return CollUtil.newArrayList((Iterable<DateTime>) range(start, end, unit));
|
||||
}
|
||||
|
||||
|
@ -22,6 +22,7 @@ import java.time.temporal.ChronoUnit;
|
||||
import java.time.temporal.Temporal;
|
||||
import java.time.temporal.TemporalAccessor;
|
||||
import java.time.temporal.TemporalUnit;
|
||||
import java.time.temporal.WeekFields;
|
||||
import java.util.Date;
|
||||
import java.util.TimeZone;
|
||||
|
||||
@ -562,4 +563,21 @@ public class LocalDateTimeUtil {
|
||||
return startTime.isAfter(realEndTime) || endTime.isBefore(realStartTime);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获得指定日期是所在年份的第几周,如:
|
||||
* <ul>
|
||||
* <li>如果一年的第一天是星期一,则第一周从第一天开始,没有零周</li>
|
||||
* <li>如果一年的第二天是星期一,则第一周从第二天开始,而第一天在零周</li>
|
||||
* <li>如果一年中的第4天是星期一,则第1周从第4周开始,第1至第3周在零周开始</li>
|
||||
* <li>如果一年中的第5天是星期一,则第二周从第5周开始,第1至第4周在第1周</li>
|
||||
* </ul>
|
||||
*
|
||||
*
|
||||
* @param date 日期({@link LocalDate} 或者 {@link LocalDateTime}等)
|
||||
* @return 所在年的第几周
|
||||
* @since 5.7.21
|
||||
*/
|
||||
public static int weekOfYear(TemporalAccessor date){
|
||||
return TemporalAccessorUtil.get(date, WeekFields.ISO.weekOfYear());
|
||||
}
|
||||
}
|
||||
|
@ -1,5 +1,7 @@
|
||||
package cn.hutool.core.date;
|
||||
|
||||
import cn.hutool.core.lang.Assert;
|
||||
|
||||
import java.util.Calendar;
|
||||
|
||||
/**
|
||||
@ -93,14 +95,27 @@ public enum Month {
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取{@link Calendar}中的对应值
|
||||
* 获取{@link Calendar}中的对应值<br>
|
||||
* 此值从0开始,即0表示一月
|
||||
*
|
||||
* @return {@link Calendar}中的对应值
|
||||
* @return {@link Calendar}中的对应月份值,从0开始计数
|
||||
*/
|
||||
public int getValue() {
|
||||
return this.value;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取月份值,此值与{@link java.time.Month}对应<br>
|
||||
* 此值从1开始,即1表示一月
|
||||
*
|
||||
* @return 月份值,对应{@link java.time.Month},从1开始计数
|
||||
* @since 5.7.21
|
||||
*/
|
||||
public int getValueBaseOne() {
|
||||
Assert.isFalse(this == UNDECIMBER, "Unsupported UNDECIMBER Field");
|
||||
return getValue() + 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取此月份最后一天的值,不支持的月份(例如UNDECIMBER)返回-1
|
||||
*
|
||||
@ -165,17 +180,28 @@ public enum Month {
|
||||
|
||||
/**
|
||||
* 获得指定月的最后一天
|
||||
* @param month 月份,从0开始
|
||||
*
|
||||
* @param month 月份,从0开始
|
||||
* @param isLeapYear 是否为闰年,闰年只对二月有影响
|
||||
* @return 最后一天,可能为28,29,30,31
|
||||
* @since 5.4.7
|
||||
*/
|
||||
public static int getLastDay(int month, boolean isLeapYear){
|
||||
public static int getLastDay(int month, boolean isLeapYear) {
|
||||
int lastDay = DAYS_OF_MONTH[month];
|
||||
if (isLeapYear && Calendar.FEBRUARY == month){
|
||||
if (isLeapYear && Calendar.FEBRUARY == month) {
|
||||
// 二月
|
||||
lastDay += 1;
|
||||
}
|
||||
return lastDay;
|
||||
}
|
||||
|
||||
/**
|
||||
* 转换为{@link java.time.Month}
|
||||
*
|
||||
* @return {@link java.time.Month}
|
||||
* @since 5.7.21
|
||||
*/
|
||||
public java.time.Month toJdkMonth() {
|
||||
return java.time.Month.of(getValueBaseOne());
|
||||
}
|
||||
}
|
||||
|
@ -51,7 +51,7 @@ public class CheckedUtil {
|
||||
* @param expression Lambda表达式
|
||||
* @param <P> 运行时传入的参数类型
|
||||
* @param <R> 最终返回的数据类型
|
||||
* @return cn.hutool.core.lang.func.Func
|
||||
* @return {@link FuncRt}
|
||||
*/
|
||||
public static <P, R> FuncRt<P, R> uncheck(Func<P, R> expression) {
|
||||
return uncheck(expression, new RuntimeException());
|
||||
@ -63,7 +63,7 @@ public class CheckedUtil {
|
||||
*
|
||||
* @param expression 运行时传入的参数类型
|
||||
* @param <R> 最终返回的数据类型
|
||||
* @return cn.hutool.core.lang.func.Func0
|
||||
* @return {@link Func0Rt}
|
||||
*/
|
||||
public static <R> Func0Rt<R> uncheck(Func0<R> expression) {
|
||||
return uncheck(expression, new RuntimeException());
|
||||
@ -76,7 +76,7 @@ public class CheckedUtil {
|
||||
* @param expression 运行时传入的参数类型
|
||||
* @param <P> 运行时传入的参数类型
|
||||
* @param <R> 最终返回的数据类型
|
||||
* @return cn.hutool.core.lang.func.Func1
|
||||
* @return {@link Func1Rt}
|
||||
*/
|
||||
public static <P, R> Func1Rt<P, R> uncheck(Func1<P, R> expression) {
|
||||
return uncheck(expression, new RuntimeException());
|
||||
@ -89,7 +89,7 @@ public class CheckedUtil {
|
||||
*
|
||||
* @param expression 运行时传入的参数类型
|
||||
* @param <P> 运行时传入的参数类型
|
||||
* @return cn.hutool.core.lang.func.VoidFunc
|
||||
* @return {@link VoidFuncRt}
|
||||
*/
|
||||
public static <P> VoidFuncRt<P> uncheck(VoidFunc<P> expression) {
|
||||
return uncheck(expression, new RuntimeException());
|
||||
@ -100,7 +100,7 @@ public class CheckedUtil {
|
||||
* 如此一来,代码中就不用显示的try-catch转化成运行时异常
|
||||
*
|
||||
* @param expression 运行时传入的参数类型
|
||||
* @return cn.hutool.core.lang.func.VoidFunc0
|
||||
* @return {@link VoidFunc0Rt}
|
||||
*/
|
||||
public static VoidFunc0Rt uncheck(VoidFunc0 expression) {
|
||||
return uncheck(expression, new RuntimeException());
|
||||
@ -112,7 +112,7 @@ public class CheckedUtil {
|
||||
*
|
||||
* @param expression 运行时传入的参数类型
|
||||
* @param <P> 运行时传入的参数类型
|
||||
* @return cn.hutool.core.lang.func.VoidFunc1
|
||||
* @return {@link VoidFunc1Rt}
|
||||
*/
|
||||
public static <P> VoidFunc1Rt<P> uncheck(VoidFunc1<P> expression) {
|
||||
return uncheck(expression, new RuntimeException());
|
||||
@ -127,7 +127,7 @@ public class CheckedUtil {
|
||||
* @param rte 期望抛出的运行时异常
|
||||
* @param <P> 运行时传入的参数类型
|
||||
* @param <R> 最终返回的数据类型
|
||||
* @return cn.hutool.core.lang.func.Func
|
||||
* @return {@link FuncRt}
|
||||
*/
|
||||
public static <P, R> FuncRt<P, R> uncheck(Func<P, R> expression, RuntimeException rte) {
|
||||
Objects.requireNonNull(expression, "expression can not be null");
|
||||
@ -152,7 +152,7 @@ public class CheckedUtil {
|
||||
* @param expression Lambda表达式
|
||||
* @param rte 期望抛出的运行时异常
|
||||
* @param <R> 最终返回的数据类型
|
||||
* @return cn.hutool.core.lang.func.Func0
|
||||
* @return {@link Func0Rt}
|
||||
*/
|
||||
public static <R> Func0Rt<R> uncheck(Func0<R> expression, RuntimeException rte) {
|
||||
Objects.requireNonNull(expression, "expression can not be null");
|
||||
@ -178,7 +178,7 @@ public class CheckedUtil {
|
||||
* @param rte 期望抛出的运行时异常
|
||||
* @param <P> 运行时传入的参数类型
|
||||
* @param <R> 最终返回的数据类型
|
||||
* @return cn.hutool.core.lang.func.Func1
|
||||
* @return {@link Func1Rt}
|
||||
*/
|
||||
public static <P, R> Func1Rt<P, R> uncheck(Func1<P, R> expression, RuntimeException rte) {
|
||||
Objects.requireNonNull(expression, "expression can not be null");
|
||||
@ -203,7 +203,7 @@ public class CheckedUtil {
|
||||
* @param expression Lambda表达式
|
||||
* @param rte 期望抛出的运行时异常
|
||||
* @param <P> 运行时传入的参数类型
|
||||
* @return cn.hutool.core.lang.func.VoidFunc
|
||||
* @return {@link VoidFuncRt}
|
||||
*/
|
||||
public static <P> VoidFuncRt<P> uncheck(VoidFunc<P> expression, RuntimeException rte) {
|
||||
Objects.requireNonNull(expression, "expression can not be null");
|
||||
@ -228,7 +228,7 @@ public class CheckedUtil {
|
||||
*
|
||||
* @param expression Lambda表达式
|
||||
* @param rte 期望抛出的运行时异常
|
||||
* @return cn.hutool.core.lang.func.VoidFunc0
|
||||
* @return {@link VoidFunc0Rt}
|
||||
*/
|
||||
public static VoidFunc0Rt uncheck(VoidFunc0 expression, RuntimeException rte) {
|
||||
Objects.requireNonNull(expression, "expression can not be null");
|
||||
@ -254,7 +254,7 @@ public class CheckedUtil {
|
||||
* @param expression Lambda表达式
|
||||
* @param rte 期望抛出的运行时异常
|
||||
* @param <P> 运行时传入的参数类型
|
||||
* @return cn.hutool.core.lang.func.VoidFunc1
|
||||
* @return {@link VoidFunc1Rt}
|
||||
*/
|
||||
public static <P> VoidFunc1Rt<P> uncheck(VoidFunc1<P> expression, RuntimeException rte) {
|
||||
Objects.requireNonNull(expression, "expression can not be null");
|
||||
|
@ -13,6 +13,7 @@ import cn.hutool.core.io.file.Tailer;
|
||||
import cn.hutool.core.io.resource.ResourceUtil;
|
||||
import cn.hutool.core.io.unit.DataSizeUtil;
|
||||
import cn.hutool.core.lang.Assert;
|
||||
import cn.hutool.core.thread.ThreadUtil;
|
||||
import cn.hutool.core.util.ArrayUtil;
|
||||
import cn.hutool.core.util.CharUtil;
|
||||
import cn.hutool.core.util.CharsetUtil;
|
||||
@ -526,24 +527,39 @@ public class FileUtil extends PathUtil {
|
||||
/**
|
||||
* 计算目录或文件的总大小<br>
|
||||
* 当给定对象为文件时,直接调用 {@link File#length()}<br>
|
||||
* 当给定对象为目录时,遍历目录下的所有文件和目录,递归计算其大小,求和返回
|
||||
* 当给定对象为目录时,遍历目录下的所有文件和目录,递归计算其大小,求和返回<br>
|
||||
* 此方法不包括目录本身的占用空间大小。
|
||||
*
|
||||
* @param file 目录或文件,null或者文件不存在返回0
|
||||
* @return 总大小,bytes长度
|
||||
*/
|
||||
public static long size(File file) {
|
||||
return size(file, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* 计算目录或文件的总大小<br>
|
||||
* 当给定对象为文件时,直接调用 {@link File#length()}<br>
|
||||
* 当给定对象为目录时,遍历目录下的所有文件和目录,递归计算其大小,求和返回
|
||||
*
|
||||
* @param file 目录或文件,null或者文件不存在返回0
|
||||
* @param includeDirSize 是否包括每层目录本身的大小
|
||||
* @return 总大小,bytes长度
|
||||
* @since 5.7.21
|
||||
*/
|
||||
public static long size(File file, boolean includeDirSize) {
|
||||
if (null == file || false == file.exists() || isSymlink(file)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (file.isDirectory()) {
|
||||
long size = 0L;
|
||||
long size = includeDirSize ? file.length() : 0;
|
||||
File[] subFiles = file.listFiles();
|
||||
if (ArrayUtil.isEmpty(subFiles)) {
|
||||
return 0L;// empty directory
|
||||
}
|
||||
for (File subFile : subFiles) {
|
||||
size += size(subFile);
|
||||
size += size(subFile, includeDirSize);
|
||||
}
|
||||
return size;
|
||||
} else {
|
||||
@ -811,7 +827,7 @@ public class FileUtil extends PathUtil {
|
||||
|
||||
/**
|
||||
* 创建文件夹,会递归自动创建其不存在的父文件夹,如果存在直接返回此文件夹<br>
|
||||
* 此方法不对File对象类型做判断,如果File不存在,无法判断其类型
|
||||
* 此方法不对File对象类型做判断,如果File不存在,无法判断其类型<br>
|
||||
*
|
||||
* @param dir 目录
|
||||
* @return 创建的目录
|
||||
@ -821,12 +837,48 @@ public class FileUtil extends PathUtil {
|
||||
return null;
|
||||
}
|
||||
if (false == dir.exists()) {
|
||||
//noinspection ResultOfMethodCallIgnored
|
||||
dir.mkdirs();
|
||||
mkdirsSafely(dir, 5, 1);
|
||||
}
|
||||
return dir;
|
||||
}
|
||||
|
||||
/**
|
||||
* 安全地级联创建目录 (确保并发环境下能创建成功)
|
||||
*
|
||||
* <pre>
|
||||
* 并发环境下,假设 test 目录不存在,如果线程A mkdirs "test/A" 目录,线程B mkdirs "test/B"目录,
|
||||
* 其中一个线程可能会失败,进而导致以下代码抛出 FileNotFoundException 异常
|
||||
*
|
||||
* file.getParentFile().mkdirs(); // 父目录正在被另一个线程创建中,返回 false
|
||||
* file.createNewFile(); // 抛出 IO 异常,因为该线程无法感知到父目录已被创建
|
||||
* </pre>
|
||||
*
|
||||
* @param dir 待创建的目录
|
||||
* @param tryCount 最大尝试次数
|
||||
* @param sleepMillis 线程等待的毫秒数
|
||||
* @return true表示创建成功,false表示创建失败
|
||||
* @since 5.7.21
|
||||
* @author z8g
|
||||
*/
|
||||
public static boolean mkdirsSafely(File dir, int tryCount, long sleepMillis) {
|
||||
if (dir == null) {
|
||||
return false;
|
||||
}
|
||||
if (dir.isDirectory()) {
|
||||
return true;
|
||||
}
|
||||
for (int i = 1; i <= tryCount; i++) { // 高并发场景下,可以看到 i 处于 1 ~ 3 之间
|
||||
// 如果文件已存在,也会返回 false,所以该值不能作为是否能创建的依据,因此不对其进行处理
|
||||
//noinspection ResultOfMethodCallIgnored
|
||||
dir.mkdirs();
|
||||
if (dir.exists()) {
|
||||
return true;
|
||||
}
|
||||
ThreadUtil.sleep(sleepMillis);
|
||||
}
|
||||
return dir.exists();
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建临时文件<br>
|
||||
* 创建后的文件名为 prefix[Randon].tmp
|
||||
|
@ -87,12 +87,49 @@ public class NioUtil {
|
||||
Assert.notNull(outChannel, "Out channel is null!");
|
||||
|
||||
try {
|
||||
return inChannel.transferTo(0, inChannel.size(), outChannel);
|
||||
return copySafely(inChannel, outChannel);
|
||||
} catch (IOException e) {
|
||||
throw new IORuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 文件拷贝实现
|
||||
*
|
||||
* <pre>
|
||||
* FileChannel#transferTo 或 FileChannel#transferFrom 的实现是平台相关的,需要确保低版本平台的兼容性
|
||||
* 例如 android 7以下平台在使用 ZipInputStream 解压文件的过程中,
|
||||
* 通过 FileChannel#transferFrom 传输到文件时,其返回值可能小于 totalBytes,不处理将导致文件内容缺失
|
||||
*
|
||||
* // 错误写法,dstChannel.transferFrom 返回值小于 zipEntry.getSize(),导致解压后文件内容缺失
|
||||
* try (InputStream srcStream = zipFile.getInputStream(zipEntry);
|
||||
* ReadableByteChannel srcChannel = Channels.newChannel(srcStream);
|
||||
* FileOutputStream fos = new FileOutputStream(saveFile);
|
||||
* FileChannel dstChannel = fos.getChannel()) {
|
||||
* dstChannel.transferFrom(srcChannel, 0, zipEntry.getSize());
|
||||
* }
|
||||
* </pre>
|
||||
*
|
||||
* @param inChannel 输入通道
|
||||
* @param outChannel 输出通道
|
||||
* @return 输入通道的字节数
|
||||
* @throws IOException 发生IO错误
|
||||
* @link http://androidxref.com/6.0.1_r10/xref/libcore/luni/src/main/java/java/nio/FileChannelImpl.java
|
||||
* @link http://androidxref.com/7.0.0_r1/xref/libcore/ojluni/src/main/java/sun/nio/ch/FileChannelImpl.java
|
||||
* @link http://androidxref.com/7.0.0_r1/xref/libcore/ojluni/src/main/native/FileChannelImpl.c
|
||||
* @author z8g
|
||||
* @since 5.7.21
|
||||
*/
|
||||
private static long copySafely(FileChannel inChannel, FileChannel outChannel) throws IOException {
|
||||
final long totalBytes = inChannel.size();
|
||||
for (long pos = 0, remaining = totalBytes; remaining > 0; ) { // 确保文件内容不会缺失
|
||||
final long writeBytes = inChannel.transferTo(pos, remaining, outChannel); // 实际传输的字节数
|
||||
pos += writeBytes;
|
||||
remaining -= writeBytes;
|
||||
}
|
||||
return totalBytes;
|
||||
}
|
||||
|
||||
/**
|
||||
* 拷贝流,使用NIO,不会关闭channel
|
||||
*
|
||||
|
32
hutool-core/src/main/java/cn/hutool/core/lang/func/Supplier1.java
Executable file
32
hutool-core/src/main/java/cn/hutool/core/lang/func/Supplier1.java
Executable file
@ -0,0 +1,32 @@
|
||||
package cn.hutool.core.lang.func;
|
||||
|
||||
import java.util.function.Supplier;
|
||||
|
||||
/**
|
||||
* 1参数Supplier
|
||||
*
|
||||
* @param <T> 目标 类型
|
||||
* @param <P1> 参数一 类型
|
||||
* @author TomXin
|
||||
* @since 5.7.21
|
||||
*/
|
||||
@FunctionalInterface
|
||||
public interface Supplier1<T, P1> {
|
||||
/**
|
||||
* 生成实例的方法
|
||||
*
|
||||
* @param p1 参数一
|
||||
* @return 目标对象
|
||||
*/
|
||||
T get(P1 p1);
|
||||
|
||||
/**
|
||||
* 将带有参数的Supplier转换为无参{@link Supplier}
|
||||
*
|
||||
* @param p1 参数1
|
||||
* @return {@link Supplier}
|
||||
*/
|
||||
default Supplier<T> toSupplier(P1 p1) {
|
||||
return () -> get(p1);
|
||||
}
|
||||
}
|
36
hutool-core/src/main/java/cn/hutool/core/lang/func/Supplier2.java
Executable file
36
hutool-core/src/main/java/cn/hutool/core/lang/func/Supplier2.java
Executable file
@ -0,0 +1,36 @@
|
||||
package cn.hutool.core.lang.func;
|
||||
|
||||
import java.util.function.Supplier;
|
||||
|
||||
/**
|
||||
* 两个参数的Supplier
|
||||
*
|
||||
* @param <T> 目标 类型
|
||||
* @param <P1> 参数一 类型
|
||||
* @param <P2> 参数二 类型
|
||||
* @author TomXin
|
||||
* @since 5.7.21
|
||||
*/
|
||||
@FunctionalInterface
|
||||
public interface Supplier2<T, P1, P2> {
|
||||
|
||||
/**
|
||||
* 生成实例的方法
|
||||
*
|
||||
* @param p1 参数一
|
||||
* @param p2 参数二
|
||||
* @return 目标对象
|
||||
*/
|
||||
T get(P1 p1, P2 p2);
|
||||
|
||||
/**
|
||||
* 将带有参数的Supplier转换为无参{@link Supplier}
|
||||
*
|
||||
* @param p1 参数1
|
||||
* @param p2 参数2
|
||||
* @return {@link Supplier}
|
||||
*/
|
||||
default Supplier<T> toSupplier(P1 p1, P2 p2) {
|
||||
return () -> get(p1, p2);
|
||||
}
|
||||
}
|
39
hutool-core/src/main/java/cn/hutool/core/lang/func/Supplier3.java
Executable file
39
hutool-core/src/main/java/cn/hutool/core/lang/func/Supplier3.java
Executable file
@ -0,0 +1,39 @@
|
||||
package cn.hutool.core.lang.func;
|
||||
|
||||
import java.util.function.Supplier;
|
||||
|
||||
/**
|
||||
* 3参数Supplier
|
||||
*
|
||||
* @param <T> 目标类型
|
||||
* @param <P1> 参数一类型
|
||||
* @param <P2> 参数二类型
|
||||
* @param <P3> 参数三类型
|
||||
* @author TomXin
|
||||
* @since 5.7.21
|
||||
*/
|
||||
@FunctionalInterface
|
||||
public interface Supplier3<T, P1, P2, P3> {
|
||||
|
||||
/**
|
||||
* 生成实例的方法
|
||||
*
|
||||
* @param p1 参数一
|
||||
* @param p2 参数二
|
||||
* @param p3 参数三
|
||||
* @return 目标对象
|
||||
*/
|
||||
T get(P1 p1, P2 p2, P3 p3);
|
||||
|
||||
/**
|
||||
* 将带有参数的Supplier转换为无参{@link Supplier}
|
||||
*
|
||||
* @param p1 参数1
|
||||
* @param p2 参数2
|
||||
* @param p3 参数3
|
||||
* @return {@link Supplier}
|
||||
*/
|
||||
default Supplier<T> toSupplier(P1 p1, P2 p2, P3 p3) {
|
||||
return () -> get(p1, p2, p3);
|
||||
}
|
||||
}
|
42
hutool-core/src/main/java/cn/hutool/core/lang/func/Supplier4.java
Executable file
42
hutool-core/src/main/java/cn/hutool/core/lang/func/Supplier4.java
Executable file
@ -0,0 +1,42 @@
|
||||
package cn.hutool.core.lang.func;
|
||||
|
||||
import java.util.function.Supplier;
|
||||
|
||||
/**
|
||||
* 4参数Supplier
|
||||
*
|
||||
* @param <T> 目标 类型
|
||||
* @param <P1> 参数一 类型
|
||||
* @param <P2> 参数二 类型
|
||||
* @param <P3> 参数三 类型
|
||||
* @param <P4> 参数四 类型
|
||||
* @author TomXin
|
||||
* @since 5.7.21
|
||||
*/
|
||||
@FunctionalInterface
|
||||
public interface Supplier4<T, P1, P2, P3, P4> {
|
||||
|
||||
/**
|
||||
* 生成实例的方法
|
||||
*
|
||||
* @param p1 参数一
|
||||
* @param p2 参数二
|
||||
* @param p3 参数三
|
||||
* @param p4 参数四
|
||||
* @return 目标对象
|
||||
*/
|
||||
T get(P1 p1, P2 p2, P3 p3, P4 p4);
|
||||
|
||||
/**
|
||||
* 将带有参数的Supplier转换为无参{@link Supplier}
|
||||
*
|
||||
* @param p1 参数1
|
||||
* @param p2 参数2
|
||||
* @param p3 参数3
|
||||
* @param p4 参数4
|
||||
* @return {@link Supplier}
|
||||
*/
|
||||
default Supplier<T> toSupplier(P1 p1, P2 p2, P3 p3, P4 p4) {
|
||||
return () -> get(p1, p2, p3, p4);
|
||||
}
|
||||
}
|
45
hutool-core/src/main/java/cn/hutool/core/lang/func/Supplier5.java
Executable file
45
hutool-core/src/main/java/cn/hutool/core/lang/func/Supplier5.java
Executable file
@ -0,0 +1,45 @@
|
||||
package cn.hutool.core.lang.func;
|
||||
|
||||
import java.util.function.Supplier;
|
||||
|
||||
/**
|
||||
* 5参数Supplier
|
||||
*
|
||||
* @param <T> 目标 类型
|
||||
* @param <P1> 参数一 类型
|
||||
* @param <P2> 参数二 类型
|
||||
* @param <P3> 参数三 类型
|
||||
* @param <P4> 参数四 类型
|
||||
* @param <P5> 参数五 类型
|
||||
* @author TomXin
|
||||
* @since 5.7.21
|
||||
*/
|
||||
@FunctionalInterface
|
||||
public interface Supplier5<T, P1, P2, P3, P4, P5> {
|
||||
|
||||
/**
|
||||
* 生成实例的方法
|
||||
*
|
||||
* @param p1 参数一
|
||||
* @param p2 参数二
|
||||
* @param p3 参数三
|
||||
* @param p4 参数四
|
||||
* @param p5 参数五
|
||||
* @return 目标对象
|
||||
*/
|
||||
T get(P1 p1, P2 p2, P3 p3, P4 p4, P5 p5);
|
||||
|
||||
/**
|
||||
* 将带有参数的Supplier转换为无参{@link Supplier}
|
||||
*
|
||||
* @param p1 参数1
|
||||
* @param p2 参数2
|
||||
* @param p3 参数3
|
||||
* @param p4 参数4
|
||||
* @param p5 参数5
|
||||
* @return {@link Supplier}
|
||||
*/
|
||||
default Supplier<T> toSupplier(P1 p1, P2 p2, P3 p3, P4 p4, P5 p5) {
|
||||
return () -> get(p1, p2, p3, p4, p5);
|
||||
}
|
||||
}
|
@ -3601,6 +3601,46 @@ public class CharSequenceUtil {
|
||||
return stringBuilder.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* 替换指定字符串的指定区间内字符为指定字符串,字符串只重复一次<br>
|
||||
* 此方法使用{@link String#codePoints()}完成拆分替换
|
||||
*
|
||||
* @param str 字符串
|
||||
* @param startInclude 开始位置(包含)
|
||||
* @param endExclude 结束位置(不包含)
|
||||
* @param replacedStr 被替换的字符串
|
||||
* @return 替换后的字符串
|
||||
* @since 3.2.1
|
||||
*/
|
||||
public static String replace(CharSequence str, int startInclude, int endExclude, CharSequence replacedStr) {
|
||||
if (isEmpty(str)) {
|
||||
return str(str);
|
||||
}
|
||||
final String originalStr = str(str);
|
||||
int[] strCodePoints = originalStr.codePoints().toArray();
|
||||
final int strLength = strCodePoints.length;
|
||||
if (startInclude > strLength) {
|
||||
return originalStr;
|
||||
}
|
||||
if (endExclude > strLength) {
|
||||
endExclude = strLength;
|
||||
}
|
||||
if (startInclude > endExclude) {
|
||||
// 如果起始位置大于结束位置,不替换
|
||||
return originalStr;
|
||||
}
|
||||
|
||||
final StringBuilder stringBuilder = new StringBuilder();
|
||||
for (int i = 0; i < startInclude; i++) {
|
||||
stringBuilder.append(new String(strCodePoints, i, 1));
|
||||
}
|
||||
stringBuilder.append(replacedStr);
|
||||
for (int i = endExclude; i < strLength; i++) {
|
||||
stringBuilder.append(new String(strCodePoints, i, 1));
|
||||
}
|
||||
return stringBuilder.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* 替换所有正则匹配的文本,并使用自定义函数决定如何替换<br>
|
||||
* replaceFun可以通过{@link Matcher}提取出匹配到的内容的不同部分,然后经过重新处理、组装变成新的内容放回原位。
|
||||
|
@ -1,6 +1,7 @@
|
||||
package cn.hutool.core.text.csv;
|
||||
|
||||
import cn.hutool.core.bean.BeanUtil;
|
||||
import cn.hutool.core.collection.ArrayIter;
|
||||
import cn.hutool.core.collection.CollUtil;
|
||||
import cn.hutool.core.convert.Convert;
|
||||
import cn.hutool.core.io.FileUtil;
|
||||
@ -45,6 +46,10 @@ public final class CsvWriter implements Closeable, Flushable, Serializable {
|
||||
* 是否处于新行开始
|
||||
*/
|
||||
private boolean newline = true;
|
||||
/**
|
||||
* 是否首行,即CSV开始的位置,当初始化时默认为true,一旦写入内容,为false
|
||||
*/
|
||||
private boolean isFirstLine = true;
|
||||
|
||||
// --------------------------------------------------------------------------------------------------- Constructor start
|
||||
|
||||
@ -183,13 +188,7 @@ public final class CsvWriter implements Closeable, Flushable, Serializable {
|
||||
* @throws IORuntimeException IO异常
|
||||
*/
|
||||
public CsvWriter write(String[]... lines) throws IORuntimeException {
|
||||
if (ArrayUtil.isNotEmpty(lines)) {
|
||||
for (final String[] values : lines) {
|
||||
appendLine(values);
|
||||
}
|
||||
flush();
|
||||
}
|
||||
return this;
|
||||
return write(new ArrayIter<>(lines));
|
||||
}
|
||||
|
||||
/**
|
||||
@ -320,9 +319,14 @@ public final class CsvWriter implements Closeable, Flushable, Serializable {
|
||||
public CsvWriter writeComment(String comment) {
|
||||
Assert.notNull(this.config.commentCharacter, "Comment is disable!");
|
||||
try {
|
||||
if(isFirstLine){
|
||||
// 首行不补换行符
|
||||
isFirstLine = false;
|
||||
}else {
|
||||
writer.write(config.lineDelimiter);
|
||||
}
|
||||
writer.write(this.config.commentCharacter);
|
||||
writer.write(comment);
|
||||
writer.write(config.lineDelimiter);
|
||||
newline = true;
|
||||
} catch (IOException e) {
|
||||
throw new IORuntimeException(e);
|
||||
@ -366,12 +370,17 @@ public final class CsvWriter implements Closeable, Flushable, Serializable {
|
||||
* @param fields 字段列表 ({@code null} 值会被做为空值追加)
|
||||
* @throws IOException IO异常
|
||||
*/
|
||||
private void doAppendLine(final String... fields) throws IOException {
|
||||
private void doAppendLine(String... fields) throws IOException {
|
||||
if (null != fields) {
|
||||
if(isFirstLine){
|
||||
// 首行不补换行符
|
||||
isFirstLine = false;
|
||||
}else {
|
||||
writer.write(config.lineDelimiter);
|
||||
}
|
||||
for (String field : fields) {
|
||||
appendField(field);
|
||||
}
|
||||
writer.write(config.lineDelimiter);
|
||||
newline = true;
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,93 @@
|
||||
package cn.hutool.core.builder;
|
||||
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
import lombok.ToString;
|
||||
import lombok.experimental.Accessors;
|
||||
import org.junit.Assert;
|
||||
import org.junit.Test;
|
||||
|
||||
/**
|
||||
* {@link GenericBuilder} 单元测试类
|
||||
*
|
||||
* @author TomXin
|
||||
*/
|
||||
public class GenericBuilderTest {
|
||||
|
||||
@Test
|
||||
public void test() {
|
||||
Box box = GenericBuilder
|
||||
.of(Box::new)
|
||||
.with(Box::setId, 1024L)
|
||||
.with(Box::setTitle, "Hello World!")
|
||||
.with(Box::setLength, 9)
|
||||
.with(Box::setWidth, 8)
|
||||
.with(Box::setHeight, 7)
|
||||
.build();
|
||||
|
||||
Assert.assertEquals(1024L, box.getId().longValue());
|
||||
Assert.assertEquals("Hello World!", box.getTitle());
|
||||
Assert.assertEquals(9, box.getLength().intValue());
|
||||
Assert.assertEquals(8, box.getWidth().intValue());
|
||||
Assert.assertEquals(7, box.getHeight().intValue());
|
||||
|
||||
// 对象修改
|
||||
Box boxModified = GenericBuilder
|
||||
.of(() -> box)
|
||||
.with(Box::setTitle, "Hello Friend!")
|
||||
.with(Box::setLength, 3)
|
||||
.with(Box::setWidth, 4)
|
||||
.with(Box::setHeight, 5)
|
||||
.build();
|
||||
|
||||
Assert.assertEquals(1024L, boxModified.getId().longValue());
|
||||
Assert.assertEquals("Hello Friend!", box.getTitle());
|
||||
Assert.assertEquals(3, boxModified.getLength().intValue());
|
||||
Assert.assertEquals(4, boxModified.getWidth().intValue());
|
||||
Assert.assertEquals(5, boxModified.getHeight().intValue());
|
||||
|
||||
// 多参数构造
|
||||
Box box1 = GenericBuilder
|
||||
.of(Box::new, 2048L, "Hello Partner!", 222, 333, 444)
|
||||
.with(Box::alis)
|
||||
.build();
|
||||
|
||||
Assert.assertEquals(2048L, box1.getId().longValue());
|
||||
Assert.assertEquals("Hello Partner!", box1.getTitle());
|
||||
Assert.assertEquals(222, box1.getLength().intValue());
|
||||
Assert.assertEquals(333, box1.getWidth().intValue());
|
||||
Assert.assertEquals(444, box1.getHeight().intValue());
|
||||
}
|
||||
|
||||
@Getter
|
||||
@Setter
|
||||
@ToString
|
||||
@Accessors(chain = true)
|
||||
public static class Box {
|
||||
private Long id;
|
||||
private String title;
|
||||
private Integer length;
|
||||
private Integer width;
|
||||
private Integer height;
|
||||
private String titleAlias;
|
||||
|
||||
public Box() {
|
||||
}
|
||||
|
||||
public Box(Long id, String title, Integer length, Integer width, Integer height) {
|
||||
this.id = id;
|
||||
this.title = title;
|
||||
this.length = length;
|
||||
this.width = width;
|
||||
this.height = height;
|
||||
}
|
||||
|
||||
public void alis() {
|
||||
if (StrUtil.isNotBlank(this.title)) {
|
||||
this.titleAlias = "TomXin:\"" + title + "\"";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -19,7 +19,7 @@ import java.util.Map;
|
||||
public class ListUtilTest {
|
||||
|
||||
@Test
|
||||
public void splitTest(){
|
||||
public void splitTest() {
|
||||
List<List<Object>> lists = ListUtil.split(null, 3);
|
||||
Assert.assertEquals(ListUtil.empty(), lists);
|
||||
|
||||
@ -60,7 +60,7 @@ public class ListUtilTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void splitAvgTest(){
|
||||
public void splitAvgTest() {
|
||||
List<List<Object>> lists = ListUtil.splitAvg(null, 3);
|
||||
Assert.assertEquals(ListUtil.empty(), lists);
|
||||
|
||||
@ -80,13 +80,13 @@ public class ListUtilTest {
|
||||
}
|
||||
|
||||
@Test(expected = IllegalArgumentException.class)
|
||||
public void splitAvgNotZero(){
|
||||
public void splitAvgNotZero() {
|
||||
// limit不能小于等于0
|
||||
ListUtil.splitAvg(Arrays.asList(1, 2, 3, 4), 0);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void editTest(){
|
||||
public void editTest() {
|
||||
List<String> a = ListUtil.toLinkedList("1", "2", "3");
|
||||
final List<String> filter = (List<String>) CollUtil.edit(a, str -> "edit" + str);
|
||||
Assert.assertEquals("edit1", filter.get(0));
|
||||
@ -104,7 +104,7 @@ public class ListUtilTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void pageTest(){
|
||||
public void pageTest() {
|
||||
List<Integer> a = ListUtil.toLinkedList(1, 2, 3,4,5);
|
||||
|
||||
PageUtil.setFirstPageNo(1);
|
||||
@ -167,10 +167,13 @@ public class ListUtilTest {
|
||||
Assert.assertArrayEquals(new int[]{}, pageListData.get(0).stream().mapToInt(Integer::valueOf).toArray());
|
||||
Assert.assertArrayEquals(new int[]{3, 4}, pageListData.get(1).stream().mapToInt(Integer::valueOf).toArray());
|
||||
Assert.assertArrayEquals(new int[]{5}, pageListData.get(2).stream().mapToInt(Integer::valueOf).toArray());
|
||||
|
||||
// 恢复默认值,避免影响其他测试用例
|
||||
PageUtil.setFirstPageNo(0);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void subTest(){
|
||||
public void subTest() {
|
||||
final List<Integer> of = ListUtil.of(1, 2, 3, 4);
|
||||
final List<Integer> sub = ListUtil.sub(of, 2, 4);
|
||||
sub.remove(0);
|
||||
@ -181,10 +184,10 @@ public class ListUtilTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void sortByPropertyTest(){
|
||||
public void sortByPropertyTest() {
|
||||
@Data
|
||||
@AllArgsConstructor
|
||||
class TestBean{
|
||||
class TestBean {
|
||||
private int order;
|
||||
private String name;
|
||||
}
|
||||
|
@ -1,15 +1,14 @@
|
||||
package cn.hutool.core.convert;
|
||||
|
||||
import cn.hutool.core.date.DateUtil;
|
||||
import org.junit.Assert;
|
||||
import org.junit.Test;
|
||||
|
||||
import java.sql.Timestamp;
|
||||
import java.time.LocalDateTime;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
import java.util.Date;
|
||||
|
||||
import org.junit.Assert;
|
||||
import org.junit.Test;
|
||||
|
||||
import cn.hutool.core.date.DateUtil;
|
||||
|
||||
public class DateConvertTest {
|
||||
|
||||
@Test
|
||||
@ -28,7 +27,7 @@ public class DateConvertTest {
|
||||
int dateLong = -1497600000;
|
||||
Date value = Convert.toDate(dateLong);
|
||||
Assert.assertNotNull(value);
|
||||
Assert.assertEquals("Mon Dec 15 00:00:00 CST 1969", value.toString());
|
||||
Assert.assertEquals("Mon Dec 15 00:00:00 CST 1969", value.toString().replace("GMT+08:00", "CST"));
|
||||
|
||||
final java.sql.Date sqlDate = Convert.convert(java.sql.Date.class, dateLong);
|
||||
Assert.assertNotNull(sqlDate);
|
||||
@ -53,18 +52,18 @@ public class DateConvertTest {
|
||||
java.sql.Date value2 = Convert.convert(java.sql.Date.class, timeLong);
|
||||
Assert.assertEquals(timeLong, value2.getTime());
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void toLocalDateTimeTest() {
|
||||
Date src = new Date();
|
||||
|
||||
|
||||
LocalDateTime ldt = Convert.toLocalDateTime(src);
|
||||
Assert.assertEquals(ldt, DateUtil.toLocalDateTime(src));
|
||||
|
||||
|
||||
Timestamp ts = Timestamp.from(src.toInstant());
|
||||
ldt = Convert.toLocalDateTime(ts);
|
||||
Assert.assertEquals(ldt, DateUtil.toLocalDateTime(src));
|
||||
|
||||
|
||||
String str = "2020-12-12 12:12:12.0";
|
||||
ldt = Convert.toLocalDateTime(str);
|
||||
Assert.assertEquals(ldt.format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss.S")), str);
|
||||
|
@ -692,8 +692,11 @@ public class DateUtilTest {
|
||||
@Test
|
||||
public void parseCSTTest() {
|
||||
String dateStr = "Wed Sep 16 11:26:23 CST 2009";
|
||||
Console.log(TimeZone.getDefault().getDisplayName());
|
||||
|
||||
SimpleDateFormat sdf = new SimpleDateFormat(DatePattern.JDK_DATETIME_PATTERN, Locale.US);
|
||||
// Asia/Shanghai是以地区命名的地区标准时,在中国叫CST,因此如果解析CST时不使用"Asia/Shanghai"而使用"GMT+08:00",会导致相差一个小时
|
||||
sdf.setTimeZone(TimeZone.getTimeZone("Asia/Shanghai"));
|
||||
final DateTime parse = DateUtil.parse(dateStr, sdf);
|
||||
|
||||
DateTime dateTime = DateUtil.parseCST(dateStr);
|
||||
@ -1004,4 +1007,17 @@ public class DateUtilTest {
|
||||
final DateTime parse = DateUtil.parse("2021-12-01", DatePattern.NORM_DATE_FORMATTER);
|
||||
Assert.assertEquals("2021-12-01 00:00:00", parse.toString());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void isSameWeekTest() {
|
||||
// 周六与周日比较
|
||||
final boolean isSameWeek = DateUtil.isSameWeek(DateTime.of("2022-01-01", "yyyy-MM-dd"), DateTime.of("2022-01-02", "yyyy-MM-dd"), true);
|
||||
Assert.assertTrue(isSameWeek);
|
||||
// 周日与周一比较
|
||||
final boolean isSameWeek1 = DateUtil.isSameWeek(DateTime.of("2022-01-02", "yyyy-MM-dd"), DateTime.of("2022-01-03", "yyyy-MM-dd"), false);
|
||||
Assert.assertTrue(isSameWeek1);
|
||||
// 跨月比较
|
||||
final boolean isSameWeek2 = DateUtil.isSameWeek(DateTime.of("2021-12-29", "yyyy-MM-dd"), DateTime.of("2022-01-01", "yyyy-MM-dd"), true);
|
||||
Assert.assertTrue(isSameWeek2);
|
||||
}
|
||||
}
|
||||
|
@ -197,4 +197,24 @@ public class LocalDateTimeUtilTest {
|
||||
Assert.assertTrue(LocalDateTimeUtil.isOverlap(oneStartTime2,oneEndTime2,realStartTime,realEndTime));
|
||||
Assert.assertFalse(LocalDateTimeUtil.isOverlap(oneStartTime3,oneEndTime3,realStartTime,realEndTime));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void weekOfYearTest(){
|
||||
LocalDate date1 = LocalDate.of(2021, 12, 31);
|
||||
final int weekOfYear1 = LocalDateTimeUtil.weekOfYear(date1);
|
||||
Assert.assertEquals(52, weekOfYear1);
|
||||
|
||||
final int weekOfYear2 = LocalDateTimeUtil.weekOfYear(date1.atStartOfDay());
|
||||
Assert.assertEquals(52, weekOfYear2);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void weekOfYearTest2(){
|
||||
LocalDate date1 = LocalDate.of(2022, 1, 31);
|
||||
final int weekOfYear1 = LocalDateTimeUtil.weekOfYear(date1);
|
||||
Assert.assertEquals(5, weekOfYear1);
|
||||
|
||||
final int weekOfYear2 = LocalDateTimeUtil.weekOfYear(date1.atStartOfDay());
|
||||
Assert.assertEquals(5, weekOfYear2);
|
||||
}
|
||||
}
|
||||
|
@ -37,4 +37,15 @@ public class MonthTest {
|
||||
lastDay = Month.of(Calendar.DECEMBER).getLastDay(true);
|
||||
Assert.assertEquals(31, lastDay);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void toJdkMonthTest(){
|
||||
final java.time.Month month = Month.AUGUST.toJdkMonth();
|
||||
Assert.assertEquals(java.time.Month.AUGUST, month);
|
||||
}
|
||||
|
||||
@Test(expected = IllegalArgumentException.class)
|
||||
public void toJdkMonthTest2(){
|
||||
Month.UNDECIMBER.toJdkMonth();
|
||||
}
|
||||
}
|
||||
|
@ -21,11 +21,8 @@ public class CheckedUtilTest {
|
||||
|
||||
@Test
|
||||
public void sleepTest() {
|
||||
|
||||
VoidFunc0 func = () -> Thread.sleep(1000L);
|
||||
func.callWithRuntimeException();
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
@ -39,7 +36,6 @@ public class CheckedUtilTest {
|
||||
} catch (Exception re) {
|
||||
Assert.assertTrue(re instanceof RuntimeException);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@SuppressWarnings("ConstantConditions")
|
||||
|
@ -36,6 +36,32 @@ public class RangeTest {
|
||||
Assert.assertFalse(range.hasNext());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void dateRangeFuncTest() {
|
||||
DateTime start = DateUtil.parse("2021-01-01");
|
||||
DateTime end = DateUtil.parse("2021-01-03");
|
||||
|
||||
List<Integer> dayOfMonthList = DateUtil.rangeFunc(start, end, DateField.DAY_OF_YEAR, a -> DateTime.of(a).dayOfMonth());
|
||||
Assert.assertArrayEquals(dayOfMonthList.toArray(new Integer[]{}), new Integer[]{1, 2, 3});
|
||||
|
||||
List<Integer> dayOfMonthList2 = DateUtil.rangeFunc(null, null, DateField.DAY_OF_YEAR, a -> DateTime.of(a).dayOfMonth());
|
||||
Assert.assertArrayEquals(dayOfMonthList2.toArray(new Integer[]{}), new Integer[]{});
|
||||
}
|
||||
|
||||
@Test
|
||||
public void dateRangeConsumeTest() {
|
||||
DateTime start = DateUtil.parse("2021-01-01");
|
||||
DateTime end = DateUtil.parse("2021-01-03");
|
||||
|
||||
StringBuilder sb = new StringBuilder();
|
||||
DateUtil.rangeConsume(start, end, DateField.DAY_OF_YEAR, a -> sb.append(DateTime.of(a).dayOfMonth()).append("#"));
|
||||
Assert.assertEquals(sb.toString(), "1#2#3#");
|
||||
|
||||
StringBuilder sb2 = new StringBuilder();
|
||||
DateUtil.rangeConsume(null, null, DateField.DAY_OF_YEAR, a -> sb.append(DateTime.of(a).dayOfMonth()).append("#"));
|
||||
Assert.assertEquals(sb2.toString(), "");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void dateRangeTest2() {
|
||||
DateTime start = DateUtil.parse("2021-01-31");
|
||||
|
@ -22,6 +22,13 @@ public class CharSequenceUtilTest {
|
||||
Assert.assertEquals(replace, result);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void replaceByStrTest(){
|
||||
String replace = "SSM15930297701BeryAllen";
|
||||
String result = CharSequenceUtil.replace(replace, 5, 12, "***");
|
||||
Assert.assertEquals("SSM15***01BeryAllen", result);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void addPrefixIfNotTest(){
|
||||
String str = "hutool";
|
||||
|
@ -12,4 +12,10 @@ public class NamingCaseTest {
|
||||
.set("customerNickV2", "customer_nick_v2")
|
||||
.forEach((key, value) -> Assert.assertEquals(value, NamingCase.toUnderlineCase(key)));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void toUnderLineCaseTest2(){
|
||||
final String wPRunOZTime = NamingCase.toUnderlineCase("wPRunOZTime");
|
||||
Assert.assertEquals("w_P_run_OZ_time", wPRunOZTime);
|
||||
}
|
||||
}
|
||||
|
@ -19,6 +19,8 @@ public class CsvWriterTest {
|
||||
CharsetUtil.CHARSET_GBK, false, csvWriteConfig);
|
||||
|
||||
writer.writeHeaderLine("name", "gender", "address");
|
||||
writer.writeLine("张三", "男", "XX市XX区");
|
||||
writer.writeLine("李四", "男", "XX市XX区,01号");
|
||||
writer.close();
|
||||
}
|
||||
}
|
||||
|
@ -5,32 +5,31 @@ import org.junit.Test;
|
||||
|
||||
/**
|
||||
* 分页单元测试
|
||||
* @author Looly
|
||||
*
|
||||
* @author Looly
|
||||
*/
|
||||
public class PageUtilTest {
|
||||
|
||||
|
||||
@Test
|
||||
public void transToStartEndTest(){
|
||||
PageUtil.setFirstPageNo(0);
|
||||
public void transToStartEndTest() {
|
||||
int[] startEnd1 = PageUtil.transToStartEnd(0, 10);
|
||||
Assert.assertEquals(0, startEnd1[0]);
|
||||
Assert.assertEquals(10, startEnd1[1]);
|
||||
|
||||
|
||||
int[] startEnd2 = PageUtil.transToStartEnd(1, 10);
|
||||
Assert.assertEquals(10, startEnd2[0]);
|
||||
Assert.assertEquals(20, startEnd2[1]);
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void totalPage(){
|
||||
public void totalPage() {
|
||||
int totalPage = PageUtil.totalPage(20, 3);
|
||||
Assert.assertEquals(7, totalPage);
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void rainbowTest() {
|
||||
int[] rainbow = PageUtil.rainbow(5, 20, 6);
|
||||
Assert.assertArrayEquals(new int[] {3, 4, 5, 6, 7, 8}, rainbow);
|
||||
Assert.assertArrayEquals(new int[]{3, 4, 5, 6, 7, 8}, rainbow);
|
||||
}
|
||||
}
|
||||
|
@ -27,7 +27,7 @@
|
||||
<sqlite.version>3.36.0.3</sqlite.version>
|
||||
<!-- 此处固定2.5.x,支持到JDK8 -->
|
||||
<hsqldb.version>2.5.2</hsqldb.version>
|
||||
<jedis.version>4.0.0</jedis.version>
|
||||
<jedis.version>4.1.1</jedis.version>
|
||||
</properties>
|
||||
|
||||
<dependencies>
|
||||
@ -81,7 +81,7 @@
|
||||
<dependency>
|
||||
<groupId>com.github.chris2018998</groupId>
|
||||
<artifactId>beecp</artifactId>
|
||||
<version>3.3.0</version>
|
||||
<version>3.3.1</version>
|
||||
<exclusions>
|
||||
<exclusion>
|
||||
<artifactId>slf4j-api</artifactId>
|
||||
@ -143,13 +143,13 @@
|
||||
<dependency>
|
||||
<groupId>mysql</groupId>
|
||||
<artifactId>mysql-connector-java</artifactId>
|
||||
<version>8.0.27</version>
|
||||
<version>8.0.28</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.postgresql</groupId>
|
||||
<artifactId>postgresql</artifactId>
|
||||
<version>42.3.1</version>
|
||||
<version>42.3.2</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
@ -167,13 +167,13 @@
|
||||
<dependency>
|
||||
<groupId>com.h2database</groupId>
|
||||
<artifactId>h2</artifactId>
|
||||
<version>2.0.206</version>
|
||||
<version>2.1.210</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>ru.yandex.clickhouse</groupId>
|
||||
<artifactId>clickhouse-jdbc</artifactId>
|
||||
<version>0.3.1</version>
|
||||
<version>0.3.2</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
54
hutool-db/src/main/java/cn/hutool/db/sql/ConditionGroup.java
Normal file
54
hutool-db/src/main/java/cn/hutool/db/sql/ConditionGroup.java
Normal file
@ -0,0 +1,54 @@
|
||||
package cn.hutool.db.sql;
|
||||
|
||||
import cn.hutool.core.util.ArrayUtil;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 条件组<br>
|
||||
* 用于构建复杂where条件
|
||||
*
|
||||
* @author tjh
|
||||
* @since 5.7.21
|
||||
*/
|
||||
public class ConditionGroup extends Condition {
|
||||
/**
|
||||
* 条件列表
|
||||
*/
|
||||
private Condition[] conditions;
|
||||
|
||||
/**
|
||||
* 追加条件
|
||||
*
|
||||
* @param conditions 条件列表
|
||||
*/
|
||||
public void addConditions(Condition... conditions) {
|
||||
if (null == this.conditions) {
|
||||
this.conditions = conditions;
|
||||
} else {
|
||||
this.conditions = ArrayUtil.addAll(this.conditions, conditions);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 将条件组转换为条件字符串,使用括号包裹,并回填占位符对应的参数值
|
||||
*
|
||||
* @param paramValues 参数列表,用于回填占位符对应参数值
|
||||
* @return 条件字符串
|
||||
*/
|
||||
@Override
|
||||
public String toString(List<Object> paramValues) {
|
||||
if (ArrayUtil.isEmpty(conditions)) {
|
||||
return StrUtil.EMPTY;
|
||||
}
|
||||
|
||||
final StringBuilder conditionStrBuilder = StrUtil.builder();
|
||||
conditionStrBuilder.append("(");
|
||||
// 将组内的条件构造为SQL,因为toString,会进行递归,处理所有的条件组
|
||||
conditionStrBuilder.append(ConditionBuilder.of(this.conditions).build(paramValues));
|
||||
conditionStrBuilder.append(")");
|
||||
|
||||
return conditionStrBuilder.toString();
|
||||
}
|
||||
}
|
@ -0,0 +1,28 @@
|
||||
package cn.hutool.db.sql;
|
||||
|
||||
import cn.hutool.core.collection.ListUtil;
|
||||
import org.junit.Assert;
|
||||
import org.junit.Test;
|
||||
|
||||
public class ConditionGroupTest {
|
||||
@Test
|
||||
public void ConditionGroupToStringTest() {
|
||||
Condition condition1 = new Condition("a", "A");
|
||||
Condition condition2 = new Condition("b", "B");
|
||||
condition2.setLinkOperator(LogicalOperator.OR);
|
||||
Condition condition3 = new Condition("c", "C");
|
||||
Condition condition4 = new Condition("d", "D");
|
||||
|
||||
ConditionGroup cg = new ConditionGroup();
|
||||
cg.addConditions(condition1, condition2);
|
||||
|
||||
// 条件组嵌套情况
|
||||
ConditionGroup cg2 = new ConditionGroup();
|
||||
cg2.addConditions(cg, condition3);
|
||||
|
||||
final ConditionBuilder conditionBuilder = ConditionBuilder.of(cg2, condition4);
|
||||
|
||||
Assert.assertEquals("((a = ? OR b = ?) AND c = ?) AND d = ?", conditionBuilder.build());
|
||||
Assert.assertEquals(ListUtil.of("A", "B", "C", "D"), conditionBuilder.getParamValues());
|
||||
}
|
||||
}
|
@ -17,11 +17,6 @@
|
||||
<description>Hutool 基于DFA的关键词查找</description>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>cn.hutool</groupId>
|
||||
<artifactId>hutool-core</artifactId>
|
||||
<version>${project.parent.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>cn.hutool</groupId>
|
||||
<artifactId>hutool-json</artifactId>
|
||||
|
@ -195,11 +195,21 @@ public final class SensitiveUtil {
|
||||
*/
|
||||
public static <T> T sensitiveFilter(T bean, boolean isGreedMatch, SensitiveProcessor sensitiveProcessor) {
|
||||
String jsonText = JSONUtil.toJsonStr(bean);
|
||||
@SuppressWarnings("unchecked")
|
||||
final Class<T> c = (Class<T>) bean.getClass();
|
||||
@SuppressWarnings("unchecked") final Class<T> c = (Class<T>) bean.getClass();
|
||||
return JSONUtil.toBean(sensitiveFilter(jsonText, isGreedMatch, sensitiveProcessor), c);
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理过滤文本中的敏感词,默认替换成*
|
||||
*
|
||||
* @param text 文本
|
||||
* @return 敏感词过滤处理后的文本
|
||||
* @since 5.7.21
|
||||
*/
|
||||
public static String sensitiveFilter(String text) {
|
||||
return sensitiveFilter(text, true, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理过滤文本中的敏感词,默认替换成*
|
||||
*
|
||||
@ -214,13 +224,14 @@ public final class SensitiveUtil {
|
||||
}
|
||||
|
||||
//敏感词过滤场景下,不需要密集匹配
|
||||
List<FoundWord> foundWordList = getFoundAllSensitive(text, false, isGreedMatch);
|
||||
List<FoundWord> foundWordList = getFoundAllSensitive(text, true, isGreedMatch);
|
||||
if (CollUtil.isEmpty(foundWordList)) {
|
||||
return text;
|
||||
}
|
||||
sensitiveProcessor = sensitiveProcessor == null ? new SensitiveProcessor() {
|
||||
} : sensitiveProcessor;
|
||||
Map<Integer, FoundWord> foundWordMap = new HashMap<>(foundWordList.size());
|
||||
|
||||
final Map<Integer, FoundWord> foundWordMap = new HashMap<>(foundWordList.size(), 1);
|
||||
foundWordList.forEach(foundWord -> foundWordMap.put(foundWord.getStartIndex(), foundWord));
|
||||
int length = text.length();
|
||||
StringBuilder textStringBuilder = new StringBuilder();
|
||||
|
@ -3,7 +3,6 @@ package cn.hutool.dfa;
|
||||
import cn.hutool.core.collection.CollUtil;
|
||||
import cn.hutool.core.collection.CollectionUtil;
|
||||
import cn.hutool.core.lang.Filter;
|
||||
import cn.hutool.core.text.StrBuilder;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
|
||||
import java.util.ArrayList;
|
||||
@ -247,15 +246,15 @@ public class WordTree extends HashMap<Character, WordTree> {
|
||||
|
||||
List<FoundWord> foundWords = new ArrayList<>();
|
||||
WordTree current = this;
|
||||
int length = text.length();
|
||||
final int length = text.length();
|
||||
final Filter<Character> charFilter = this.charFilter;
|
||||
//存放查找到的字符缓存。完整出现一个词时加到findedWords中,否则清空
|
||||
final StrBuilder wordBuffer = StrUtil.strBuilder();
|
||||
final StrBuilder keyBuffer = StrUtil.strBuilder();
|
||||
final StringBuilder wordBuffer = StrUtil.builder();
|
||||
final StringBuilder keyBuffer = StrUtil.builder();
|
||||
char currentChar;
|
||||
for (int i = 0; i < length; i++) {
|
||||
wordBuffer.reset();
|
||||
keyBuffer.reset();
|
||||
wordBuffer.setLength(0);
|
||||
keyBuffer.setLength(0);
|
||||
for (int j = i; j < length; j++) {
|
||||
currentChar = text.charAt(j);
|
||||
// Console.log("i: {}, j: {}, currentChar: {}", i, j, currentChar);
|
||||
@ -284,6 +283,7 @@ public class WordTree extends HashMap<Character, WordTree> {
|
||||
if (false == isDensityMatch) {
|
||||
//如果非密度匹配,跳过匹配到的词
|
||||
i = j;
|
||||
break;
|
||||
}
|
||||
if (false == isGreedMatch) {
|
||||
//如果懒惰匹配(非贪婪匹配)。当遇到第一个结尾标记就结束本轮匹配
|
||||
|
@ -47,7 +47,7 @@ public class DfaTest {
|
||||
}
|
||||
|
||||
/**
|
||||
* 贪婪匹配原则测试
|
||||
* 贪婪非密集匹配原则测试
|
||||
*/
|
||||
@Test
|
||||
public void greedMatchTest() {
|
||||
@ -56,15 +56,15 @@ public class DfaTest {
|
||||
|
||||
// -----------------------------------------------------------------------------------------------------------------------------------
|
||||
// 情况三:匹配到最长关键词,跳过已经匹配的关键词
|
||||
// 匹配到【大】,由于到最长匹配,因此【大土豆】接着被匹配
|
||||
// 由于【大土豆】被匹配,【土豆】被跳过,由于【刚出锅】被匹配,【出锅】被跳过
|
||||
// 匹配到【大】,由于非密集匹配,因此从下一个字符开始查找,匹配到【土豆】接着被匹配
|
||||
// 由于【刚出锅】被匹配,由于非密集匹配,【出锅】被跳过
|
||||
List<String> matchAll = tree.matchAll(text, -1, false, true);
|
||||
Assert.assertEquals(matchAll, CollUtil.newArrayList("大", "大土^豆", "刚出锅"));
|
||||
Assert.assertEquals(matchAll, CollUtil.newArrayList("大", "土^豆", "刚出锅"));
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* 密集匹配原则(最短匹配)和贪婪匹配原则测试
|
||||
* 密集匹配原则(最长匹配)和贪婪匹配原则测试
|
||||
*/
|
||||
@Test
|
||||
public void densityAndGreedMatchTest() {
|
||||
@ -80,6 +80,29 @@ public class DfaTest {
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void densityAndGreedMatchTest2(){
|
||||
WordTree tree = new WordTree();
|
||||
tree.addWord("赵");
|
||||
tree.addWord("赵阿");
|
||||
tree.addWord("赵阿三");
|
||||
|
||||
final List<FoundWord> result = tree.matchAllWords("赵阿三在做什么", -1, true, true);
|
||||
Assert.assertEquals(3, result.size());
|
||||
|
||||
Assert.assertEquals("赵", result.get(0).getWord());
|
||||
Assert.assertEquals(0, result.get(0).getStartIndex().intValue());
|
||||
Assert.assertEquals(0, result.get(0).getEndIndex().intValue());
|
||||
|
||||
Assert.assertEquals("赵阿", result.get(1).getWord());
|
||||
Assert.assertEquals(0, result.get(1).getStartIndex().intValue());
|
||||
Assert.assertEquals(1, result.get(1).getEndIndex().intValue());
|
||||
|
||||
Assert.assertEquals("赵阿三", result.get(2).getWord());
|
||||
Assert.assertEquals(0, result.get(2).getStartIndex().intValue());
|
||||
Assert.assertEquals(2, result.get(2).getEndIndex().intValue());
|
||||
}
|
||||
|
||||
/**
|
||||
* 停顿词测试
|
||||
*/
|
||||
|
@ -1,5 +1,7 @@
|
||||
package cn.hutool.dfa;
|
||||
|
||||
import cn.hutool.core.collection.ListUtil;
|
||||
import lombok.Data;
|
||||
import org.junit.Assert;
|
||||
import org.junit.Test;
|
||||
|
||||
@ -24,25 +26,17 @@ public class SensitiveUtilTest {
|
||||
Assert.assertEquals(bean.getStr(), "我有一颗$****,***的");
|
||||
}
|
||||
|
||||
@Data
|
||||
public static class TestBean {
|
||||
private String str;
|
||||
private Integer num;
|
||||
|
||||
public String getStr() {
|
||||
return str;
|
||||
}
|
||||
|
||||
public void setStr(String str) {
|
||||
this.str = str;
|
||||
}
|
||||
|
||||
public Integer getNum() {
|
||||
return num;
|
||||
}
|
||||
|
||||
public void setNum(Integer num) {
|
||||
this.num = num;
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void issue2126(){
|
||||
SensitiveUtil.init(ListUtil.of("赵", "赵阿", "赵阿三"));
|
||||
|
||||
String result = SensitiveUtil.sensitiveFilter("赵阿三在做什么。", true, null);
|
||||
Assert.assertEquals("***在做什么。", result);
|
||||
}
|
||||
}
|
||||
|
@ -23,7 +23,7 @@
|
||||
<rythm.version>1.4.1</rythm.version>
|
||||
<freemarker.version>2.3.31</freemarker.version>
|
||||
<enjoy.version>4.9.16</enjoy.version>
|
||||
<thymeleaf.version>3.0.14.RELEASE</thymeleaf.version>
|
||||
<thymeleaf.version>3.0.15.RELEASE</thymeleaf.version>
|
||||
<mail.version>1.6.2</mail.version>
|
||||
<jsch.version>0.1.55</jsch.version>
|
||||
<sshj.version>0.32.0</sshj.version>
|
||||
@ -31,7 +31,7 @@
|
||||
<net.version>3.8.0</net.version>
|
||||
<emoji-java.version>5.1.1</emoji-java.version>
|
||||
<servlet-api.version>4.0.1</servlet-api.version>
|
||||
<spring-boot.version>2.6.2</spring-boot.version>
|
||||
<spring-boot.version>2.6.3</spring-boot.version>
|
||||
<cglib.version>3.3.0</cglib.version>
|
||||
</properties>
|
||||
|
||||
@ -340,7 +340,7 @@
|
||||
<dependency>
|
||||
<groupId>com.github.houbb</groupId>
|
||||
<artifactId>pinyin</artifactId>
|
||||
<version>0.2.2</version>
|
||||
<version>0.3.1</version>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<dependency>
|
||||
@ -446,7 +446,7 @@
|
||||
<dependency>
|
||||
<groupId>org.springframework</groupId>
|
||||
<artifactId>spring-expression</artifactId>
|
||||
<version>5.3.14</version>
|
||||
<version>5.3.15</version>
|
||||
<scope>compile</scope>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
|
@ -126,11 +126,11 @@ public class EmojiUtil {
|
||||
* @return 替换后的字符串
|
||||
*/
|
||||
public static String toHtmlHex(String str) {
|
||||
return EmojiParser.parseToHtmlHexadecimal(str);
|
||||
return toHtml(str, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* 将字符串中的Unicode Emoji字符转换为HTML表现形式
|
||||
* 将字符串中的Unicode Emoji字符转换为HTML表现形式(Hex方式)
|
||||
* <p>
|
||||
* 例如:<code>👦🏿</code> 转换为 <code>&#128102;</code>
|
||||
*
|
||||
@ -138,7 +138,24 @@ public class EmojiUtil {
|
||||
* @return 替换后的字符串
|
||||
*/
|
||||
public static String toHtml(String str) {
|
||||
return EmojiParser.parseToHtmlHexadecimal(str);
|
||||
return toHtml(str, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* 将字符串中的Unicode Emoji字符转换为HTML表现形式,例如:
|
||||
* <pre>
|
||||
* 如果为hex形式,<code>👦🏿</code> 转换为 <code>&#x1f466;</code>
|
||||
* 否则,<code>👦🏿</code> 转换为 <code>&#128102;</code>
|
||||
* </pre>
|
||||
*
|
||||
* @param str 包含Emoji Unicode字符的字符串
|
||||
* @param isHex 是否hex形式
|
||||
* @return 替换后的字符串
|
||||
* @since 5.7.21
|
||||
*/
|
||||
public static String toHtml(String str, boolean isHex) {
|
||||
return isHex ? EmojiParser.parseToHtmlHexadecimal(str) :
|
||||
EmojiParser.parseToHtmlDecimal(str);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -94,4 +94,6 @@ public class QrCodeUtilTest {
|
||||
final BufferedImage image = QrCodeUtil.generate("content111", BarcodeFormat.PDF_417, QrConfig.create());
|
||||
Assert.assertNotNull(image);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
@ -249,22 +249,20 @@ public class JSONWriter extends Writer {
|
||||
* 此方法输出的值不包装引号。
|
||||
*
|
||||
* @param number 数字
|
||||
* @return this
|
||||
*/
|
||||
private JSONWriter writeNumberValue(Number number) {
|
||||
private void writeNumberValue(Number number) {
|
||||
// since 5.6.2可配置是否去除末尾多余0,例如如果为true,5.0返回5
|
||||
final boolean isStripTrailingZeros = null == config || config.isStripTrailingZeros();
|
||||
return writeRaw(NumberUtil.toStr(number, isStripTrailingZeros));
|
||||
writeRaw(NumberUtil.toStr(number, isStripTrailingZeros));
|
||||
}
|
||||
|
||||
/**
|
||||
* 写出Boolean值,直接写出true或false,不适用引号包装
|
||||
*
|
||||
* @param value Boolean值
|
||||
* @return this
|
||||
*/
|
||||
private JSONWriter writeBooleanValue(Boolean value) {
|
||||
return writeRaw(value.toString());
|
||||
private void writeBooleanValue(Boolean value) {
|
||||
writeRaw(value.toString());
|
||||
}
|
||||
|
||||
/**
|
||||
@ -273,9 +271,8 @@ public class JSONWriter extends Writer {
|
||||
* 如果toJSONString()返回null,调用toString()方法并使用双引号包装。
|
||||
*
|
||||
* @param jsonString {@link JSONString}
|
||||
* @return this
|
||||
*/
|
||||
private JSONWriter writeJSONStringValue(JSONString jsonString) {
|
||||
private void writeJSONStringValue(JSONString jsonString) {
|
||||
String valueStr;
|
||||
try {
|
||||
valueStr = jsonString.toJSONString();
|
||||
@ -287,7 +284,6 @@ public class JSONWriter extends Writer {
|
||||
} else {
|
||||
writeStrValue(jsonString.toString());
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -297,30 +293,26 @@ public class JSONWriter extends Writer {
|
||||
* JSON字符串中不能包含控制字符和未经转义的引号和反斜杠
|
||||
*
|
||||
* @param csq 字符串
|
||||
* @return this
|
||||
*/
|
||||
private JSONWriter writeStrValue(String csq) {
|
||||
private void writeStrValue(String csq) {
|
||||
try {
|
||||
JSONUtil.quote(csq, writer);
|
||||
} catch (IOException e) {
|
||||
throw new IORuntimeException(e);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 写出空格
|
||||
*
|
||||
* @param count 空格数
|
||||
* @return this
|
||||
*/
|
||||
private JSONWriter writeSpace(int count) {
|
||||
private void writeSpace(int count) {
|
||||
if (indentFactor > 0) {
|
||||
for (int i = 0; i < count; i++) {
|
||||
writeRaw(CharUtil.SPACE);
|
||||
}
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -19,7 +19,7 @@ public class Issue677Test {
|
||||
|
||||
final String jsonStr = JSONUtil.toJsonStr(dto);
|
||||
final AuditResultDto auditResultDto = JSONUtil.toBean(jsonStr, AuditResultDto.class);
|
||||
Assert.assertEquals("Mon Dec 15 00:00:00 CST 1969", auditResultDto.getDate().toString());
|
||||
Assert.assertEquals("Mon Dec 15 00:00:00 CST 1969", auditResultDto.getDate().toString().replace("GMT+08:00", "CST"));
|
||||
}
|
||||
|
||||
@Data
|
||||
|
@ -24,7 +24,7 @@
|
||||
<log4j2.version>2.17.1</log4j2.version>
|
||||
<commons-logging.version>1.2</commons-logging.version>
|
||||
<tinylog.version>1.3.6</tinylog.version>
|
||||
<jboss-logging.version>3.4.2.Final</jboss-logging.version>
|
||||
<jboss-logging.version>3.4.3.Final</jboss-logging.version>
|
||||
<logtube.version>0.43.0</logtube.version>
|
||||
</properties>
|
||||
|
||||
|
@ -1,8 +1,8 @@
|
||||
/**
|
||||
* Hutool-log只是一个日志的通用门面,功能类似于Slf4j。<br>
|
||||
* 根据加入ClassPath中的jar包动态检测日志实现的方式,使日志使用个更加便利灵活。
|
||||
*
|
||||
* 根据加入ClassPath中的jar包动态检测日志实现的方式,使日志使用更加便利灵活。
|
||||
*
|
||||
* @author looly
|
||||
*
|
||||
*/
|
||||
package cn.hutool.log;
|
||||
package cn.hutool.log;
|
||||
|
@ -45,7 +45,7 @@
|
||||
<dependency>
|
||||
<groupId>org.ofdrw</groupId>
|
||||
<artifactId>ofdrw-full</artifactId>
|
||||
<version>1.17.6</version>
|
||||
<version>1.17.8</version>
|
||||
<scope>compile</scope>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
|
@ -30,7 +30,7 @@
|
||||
<dependency>
|
||||
<groupId>com.github.oshi</groupId>
|
||||
<artifactId>oshi-core</artifactId>
|
||||
<version>6.0.0</version>
|
||||
<version>6.1.0</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
|
@ -1,5 +1,6 @@
|
||||
package cn.hutool.system.oshi;
|
||||
|
||||
import cn.hutool.core.util.NumberUtil;
|
||||
import oshi.hardware.CentralProcessor;
|
||||
|
||||
import java.text.DecimalFormat;
|
||||
@ -31,7 +32,7 @@ public class CpuInfo {
|
||||
/**
|
||||
* CPU用户使用率
|
||||
*/
|
||||
private double used;
|
||||
private double user;
|
||||
|
||||
/**
|
||||
* CPU当前等待率
|
||||
@ -75,16 +76,16 @@ public class CpuInfo {
|
||||
* @param cpuNum CPU核心数
|
||||
* @param toTal CPU总的使用率
|
||||
* @param sys CPU系统使用率
|
||||
* @param used CPU用户使用率
|
||||
* @param user CPU用户使用率
|
||||
* @param wait CPU当前等待率
|
||||
* @param free CPU当前空闲率
|
||||
* @param cpuModel CPU型号信息
|
||||
*/
|
||||
public CpuInfo(Integer cpuNum, double toTal, double sys, double used, double wait, double free, String cpuModel) {
|
||||
public CpuInfo(Integer cpuNum, double toTal, double sys, double user, double wait, double free, String cpuModel) {
|
||||
this.cpuNum = cpuNum;
|
||||
this.toTal = toTal;
|
||||
this.sys = sys;
|
||||
this.used = used;
|
||||
this.user = user;
|
||||
this.wait = wait;
|
||||
this.free = free;
|
||||
this.cpuModel = cpuModel;
|
||||
@ -114,12 +115,12 @@ public class CpuInfo {
|
||||
this.sys = sys;
|
||||
}
|
||||
|
||||
public double getUsed() {
|
||||
return used;
|
||||
public double getUser() {
|
||||
return user;
|
||||
}
|
||||
|
||||
public void setUsed(double used) {
|
||||
this.used = used;
|
||||
public void setUser(double user) {
|
||||
this.user = user;
|
||||
}
|
||||
|
||||
public double getWait() {
|
||||
@ -154,16 +155,25 @@ public class CpuInfo {
|
||||
this.ticks = ticks;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取用户+系统的总的CPU使用率
|
||||
*
|
||||
* @return 总CPU使用率
|
||||
*/
|
||||
public double getUsed() {
|
||||
return NumberUtil.sub(100, this.free);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "CpuInfo{" +
|
||||
"CPU核心数=" + cpuNum +
|
||||
", CPU总的使用率=" + toTal +
|
||||
", CPU系统使用率=" + sys +
|
||||
", CPU用户使用率=" + used +
|
||||
", CPU用户使用率=" + user +
|
||||
", CPU当前等待率=" + wait +
|
||||
", CPU当前空闲率=" + free +
|
||||
", CPU利用率=" + LOAD_FORMAT.format(100 - free) +
|
||||
", CPU利用率=" + getUsed() +
|
||||
", CPU型号信息='" + cpuModel + '\'' +
|
||||
'}';
|
||||
}
|
||||
@ -185,7 +195,7 @@ public class CpuInfo {
|
||||
final long totalCpu = ticks.totalCpu();
|
||||
this.toTal = totalCpu;
|
||||
this.sys = formatDouble(ticks.cSys, totalCpu);
|
||||
this.used = formatDouble(ticks.user, totalCpu);
|
||||
this.user = formatDouble(ticks.user, totalCpu);
|
||||
this.wait = formatDouble(ticks.ioWait, totalCpu);
|
||||
this.free = formatDouble(ticks.idle, totalCpu);
|
||||
}
|
||||
|
@ -1,8 +1,10 @@
|
||||
package cn.hutool.system;
|
||||
|
||||
import cn.hutool.core.lang.Console;
|
||||
import cn.hutool.system.oshi.CpuInfo;
|
||||
import cn.hutool.system.oshi.OshiUtil;
|
||||
import org.junit.Assert;
|
||||
import org.junit.Ignore;
|
||||
import org.junit.Test;
|
||||
import oshi.software.os.OSProcess;
|
||||
|
||||
@ -28,4 +30,12 @@ public class OshiTest {
|
||||
final OSProcess currentProcess = OshiUtil.getCurrentProcess();
|
||||
Assert.assertEquals("java", currentProcess.getName());
|
||||
}
|
||||
|
||||
@Test
|
||||
@Ignore
|
||||
public void getUsedTest(){
|
||||
while (true){
|
||||
Console.log(OshiUtil.getCpuInfo().getUsed());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user