Merge branch 'v6-dev' into v6-dev-List

# Conflicts:
#	hutool-core/src/test/java/org/dromara/hutool/core/collection/CollUtilTest.java
This commit is contained in:
kongweiguang 2023-06-30 19:12:46 +08:00
commit 98a6a3686d
43 changed files with 1263 additions and 254 deletions

View File

@ -298,7 +298,7 @@ public class CollUtil {
* @return 并集的集合返回 {@link ArrayList}
*/
@SafeVarargs
public static <T> Collection<T> union(final Collection<T>... colls) {
public static <T> Collection<T> union(final Collection<? extends T>... colls) {
return CollectionOperation.of(colls).union();
}
@ -313,7 +313,7 @@ public class CollUtil {
* @return 并集的集合返回 {@link LinkedHashSet}
*/
@SafeVarargs
public static <T> Set<T> unionDistinct(final Collection<T>... colls) {
public static <T> Set<T> unionDistinct(final Collection<? extends T>... colls) {
return CollectionOperation.of(colls).unionDistinct();
}
@ -328,7 +328,7 @@ public class CollUtil {
* @return 并集的集合返回 {@link ArrayList}
*/
@SafeVarargs
public static <T> List<T> unionAll(final Collection<T>... colls) {
public static <T> List<T> unionAll(final Collection<? extends T>... colls) {
return CollectionOperation.of(colls).unionAll();
}

View File

@ -45,7 +45,7 @@ public class CollectionOperation<E> {
* @return CollectionOperation
*/
@SafeVarargs
public static <E> CollectionOperation<E> of(final Collection<E>... colls) {
public static <E> CollectionOperation<E> of(final Collection<? extends E>... colls) {
return new CollectionOperation<>(colls);
}
@ -56,8 +56,9 @@ public class CollectionOperation<E> {
*
* @param colls 集合数组
*/
public CollectionOperation(final Collection<E>[] colls) {
this.colls = colls;
@SuppressWarnings("unchecked")
public CollectionOperation(final Collection<? extends E>[] colls) {
this.colls = (Collection<E>[]) colls;
}
/**

View File

@ -0,0 +1,97 @@
/*
* Copyright (c) 2023 looly(loolly@aliyun.com)
* Hutool is licensed under Mulan PSL v2.
* You can use this software according to the terms and conditions of the Mulan PSL v2.
* You may obtain a copy of Mulan PSL v2 at:
* http://license.coscl.org.cn/MulanPSL2
* THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
* EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
* MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
* See the Mulan PSL v2 for more details.
*/
package org.dromara.hutool.core.compress;
import org.dromara.hutool.core.io.file.FileUtil;
import org.dromara.hutool.core.io.resource.Resource;
import org.dromara.hutool.core.text.StrUtil;
import java.io.Closeable;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
/**
* Zip文件替换用户替换源Zip文件并生成新的文件
*
* @author looly
* @since 6.0.0
*/
public class ZipReplacer implements Closeable {
private final ZipReader zipReader;
private final boolean ignoreCase;
private final Map<String, Resource> replacedResources = new HashMap<>();
/**
* 构造
*
* @param zipReader ZipReader
* @param ignoreCase 是否忽略path大小写
*/
public ZipReplacer(final ZipReader zipReader, final boolean ignoreCase) {
this.zipReader = zipReader;
this.ignoreCase = ignoreCase;
}
/**
* 增加替换的内容如果路径不匹配则不做替换也不加入
*
* @param entryPath 路径
* @param resource 被压缩的内容
* @return this
*/
public ZipReplacer addReplace(final String entryPath, final Resource resource) {
replacedResources.put(entryPath, resource);
return this;
}
/**
* 写出到{@link ZipWriter}
*
* @param writer {@link ZipWriter}
*/
public void write(final ZipWriter writer) {
zipReader.read((entry) -> {
String entryName;
for (final String key : replacedResources.keySet()) {
entryName = entry.getName();
if (isSamePath(entryName, key, ignoreCase)) {
writer.add(key, replacedResources.get(key).getStream());
} else {
writer.add(entryName, zipReader.get(entryName));
}
}
});
}
@Override
public void close() throws IOException {
this.zipReader.close();
}
/**
* 判断路径是否相等
*
* @param entryPath 路径A
* @param targetPath 路径B
* @param ignoreCase 是否忽略大小写
* @return ture 路径相等
*/
private static boolean isSamePath(String entryPath, String targetPath, final boolean ignoreCase) {
entryPath = StrUtil.removePrefix(FileUtil.normalize(entryPath), StrUtil.SLASH);
targetPath = StrUtil.removePrefix(FileUtil.normalize(targetPath), StrUtil.SLASH);
return StrUtil.equals(entryPath, targetPath, ignoreCase);
}
}

View File

@ -231,6 +231,11 @@ public class CompositeConverter extends RegisterConverter {
return (T) KBeanConverter.INSTANCE.convert(type, value);
}
// issue#I7FQ29 Class
if("java.lang.Class".equals(rowType.getName())){
return (T) ClassConverter.INSTANCE.convert(type, value);
}
// 表示非需要特殊转换的对象
return null;
}

View File

@ -12,36 +12,14 @@
package org.dromara.hutool.core.convert;
import org.dromara.hutool.core.convert.impl.AtomicBooleanConverter;
import org.dromara.hutool.core.convert.impl.AtomicIntegerArrayConverter;
import org.dromara.hutool.core.convert.impl.AtomicLongArrayConverter;
import org.dromara.hutool.core.convert.impl.AtomicReferenceConverter;
import org.dromara.hutool.core.convert.impl.BooleanConverter;
import org.dromara.hutool.core.convert.impl.CalendarConverter;
import org.dromara.hutool.core.convert.impl.CharacterConverter;
import org.dromara.hutool.core.convert.impl.CharsetConverter;
import org.dromara.hutool.core.convert.impl.ClassConverter;
import org.dromara.hutool.core.convert.impl.CurrencyConverter;
import org.dromara.hutool.core.convert.impl.DateConverter;
import org.dromara.hutool.core.convert.impl.DurationConverter;
import org.dromara.hutool.core.convert.impl.LocaleConverter;
import org.dromara.hutool.core.convert.impl.OptConverter;
import org.dromara.hutool.core.convert.impl.OptionalConverter;
import org.dromara.hutool.core.convert.impl.PathConverter;
import org.dromara.hutool.core.convert.impl.PeriodConverter;
import org.dromara.hutool.core.convert.impl.ReferenceConverter;
import org.dromara.hutool.core.convert.impl.StackTraceElementConverter;
import org.dromara.hutool.core.convert.impl.StringConverter;
import org.dromara.hutool.core.convert.impl.TemporalAccessorConverter;
import org.dromara.hutool.core.convert.impl.TimeZoneConverter;
import org.dromara.hutool.core.convert.impl.URIConverter;
import org.dromara.hutool.core.convert.impl.URLConverter;
import org.dromara.hutool.core.convert.impl.UUIDConverter;
import org.dromara.hutool.core.convert.impl.XMLGregorianCalendarConverter;
import org.dromara.hutool.core.convert.impl.ZoneIdConverter;
import org.dromara.hutool.core.convert.impl.*;
import org.dromara.hutool.core.date.DateTime;
import org.dromara.hutool.core.lang.Opt;
import org.dromara.hutool.core.lang.tuple.Pair;
import org.dromara.hutool.core.lang.tuple.Triple;
import org.dromara.hutool.core.lang.tuple.Tuple;
import org.dromara.hutool.core.map.SafeConcurrentHashMap;
import org.dromara.hutool.core.reflect.TypeUtil;
import javax.xml.datatype.XMLGregorianCalendar;
import java.io.Serializable;
@ -52,27 +30,9 @@ import java.net.URI;
import java.net.URL;
import java.nio.charset.Charset;
import java.nio.file.Path;
import java.time.DayOfWeek;
import java.time.Duration;
import java.time.Instant;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.Month;
import java.time.MonthDay;
import java.time.OffsetDateTime;
import java.time.OffsetTime;
import java.time.Period;
import java.time.ZoneId;
import java.time.ZonedDateTime;
import java.time.*;
import java.time.temporal.TemporalAccessor;
import java.util.Calendar;
import java.util.Currency;
import java.util.Locale;
import java.util.Map;
import java.util.Optional;
import java.util.TimeZone;
import java.util.UUID;
import java.util.*;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicIntegerArray;
import java.util.concurrent.atomic.AtomicLongArray;
@ -109,7 +69,7 @@ public class RegisterConverter implements Converter, Serializable {
/**
* 默认类型转换器
*/
private Map<Type, Converter> defaultConverterMap;
private Map<Class<?>, Converter> defaultConverterMap;
/**
* 用户自定义类型转换器
*/
@ -164,7 +124,7 @@ public class RegisterConverter implements Converter, Serializable {
* @return 转换器
*/
public Converter getDefaultConverter(final Type type) {
return (null == defaultConverterMap) ? null : defaultConverterMap.get(type);
return (null == defaultConverterMap) ? null : defaultConverterMap.get(TypeUtil.getClass(type));
}
/**
@ -248,7 +208,6 @@ public class RegisterConverter implements Converter, Serializable {
defaultConverterMap.put(AtomicLongArray.class, new AtomicLongArrayConverter());
// 其它类型
defaultConverterMap.put(Class.class, new ClassConverter());
defaultConverterMap.put(TimeZone.class, new TimeZoneConverter());
defaultConverterMap.put(ZoneId.class, new ZoneIdConverter());
defaultConverterMap.put(Locale.class, new LocaleConverter());
@ -259,5 +218,8 @@ public class RegisterConverter implements Converter, Serializable {
defaultConverterMap.put(StackTraceElement.class, new StackTraceElementConverter());// since 4.5.2
defaultConverterMap.put(Optional.class, new OptionalConverter());// since 5.0.0
defaultConverterMap.put(Opt.class, new OptConverter());// since 5.7.16
defaultConverterMap.put(Pair.class, PairConverter.INSTANCE);// since 6.0.0
defaultConverterMap.put(Triple.class, TripleConverter.INSTANCE);// since 6.0.0
defaultConverterMap.put(Tuple.class, TupleConverter.INSTANCE);// since 6.0.0
}
}

View File

@ -24,6 +24,11 @@ import org.dromara.hutool.core.classloader.ClassLoaderUtil;
public class ClassConverter extends AbstractConverter {
private static final long serialVersionUID = 1L;
/**
* 单例
*/
public static ClassConverter INSTANCE = new ClassConverter();
private final boolean isInitialized;
/**

View File

@ -16,6 +16,7 @@ import org.dromara.hutool.core.bean.BeanUtil;
import org.dromara.hutool.core.convert.CompositeConverter;
import org.dromara.hutool.core.convert.ConvertException;
import org.dromara.hutool.core.convert.Converter;
import org.dromara.hutool.core.lang.tuple.Pair;
import org.dromara.hutool.core.map.MapUtil;
import org.dromara.hutool.core.reflect.ConstructorUtil;
import org.dromara.hutool.core.reflect.TypeReference;
@ -72,6 +73,9 @@ public class EntryConverter implements Converter {
if (value instanceof Map.Entry) {
final Map.Entry entry = (Map.Entry) value;
map = MapUtil.of(entry.getKey(), entry.getValue());
}else if (value instanceof Pair) {
final Pair entry = (Pair<?, ?>) value;
map = MapUtil.of(entry.getLeft(), entry.getRight());
}else if (value instanceof Map) {
map = (Map) value;
} else if (value instanceof CharSequence) {

View File

@ -0,0 +1,139 @@
/*
* Copyright (c) 2023 looly(loolly@aliyun.com)
* Hutool is licensed under Mulan PSL v2.
* You can use this software according to the terms and conditions of the Mulan PSL v2.
* You may obtain a copy of Mulan PSL v2 at:
* http://license.coscl.org.cn/MulanPSL2
* THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
* EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
* MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
* See the Mulan PSL v2 for more details.
*/
package org.dromara.hutool.core.convert.impl;
import org.dromara.hutool.core.bean.BeanUtil;
import org.dromara.hutool.core.convert.CompositeConverter;
import org.dromara.hutool.core.convert.ConvertException;
import org.dromara.hutool.core.convert.Converter;
import org.dromara.hutool.core.lang.tuple.Pair;
import org.dromara.hutool.core.map.MapUtil;
import org.dromara.hutool.core.reflect.TypeReference;
import org.dromara.hutool.core.reflect.TypeUtil;
import org.dromara.hutool.core.text.CharUtil;
import org.dromara.hutool.core.text.StrUtil;
import java.lang.reflect.Type;
import java.util.Map;
/**
* {@link Pair} 转换器支持以下类型转为Pair
* <ul>
* <li>{@link Map}</li>
* <li>{@link Map.Entry}</li>
* <li>带分隔符的字符串支持分隔符{@code :}{@code =}{@code ,}</li>
* <li>Bean包含{@code getLeft}{@code getRight}方法</li>
* </ul>
*
* @author looly
*/
public class PairConverter implements Converter {
/**
* 单例
*/
public static final PairConverter INSTANCE = new PairConverter();
@Override
public Object convert(Type targetType, final Object value) throws ConvertException {
if (targetType instanceof TypeReference) {
targetType = ((TypeReference<?>) targetType).getType();
}
final Type leftType = TypeUtil.getTypeArgument(targetType, 0);
final Type rightType = TypeUtil.getTypeArgument(targetType, 1);
return convert(leftType, rightType, value);
}
/**
* 转换对象为指定键值类型的指定类型Map
*
* @param leftType 键类型
* @param rightType 值类型
* @param value 被转换的值
* @return 转换后的Map
* @throws ConvertException 转换异常或不支持的类型
*/
@SuppressWarnings("rawtypes")
public Pair<?, ?> convert(final Type leftType, final Type rightType, final Object value)
throws ConvertException {
Map map = null;
if (value instanceof Map.Entry) {
final Map.Entry entry = (Map.Entry) value;
map = MapUtil.of(entry.getKey(), entry.getValue());
} else if (value instanceof Pair) {
final Pair entry = (Pair<?, ?>) value;
map = MapUtil.of(entry.getLeft(), entry.getRight());
} else if (value instanceof Map) {
map = (Map) value;
} else if (value instanceof CharSequence) {
final CharSequence str = (CharSequence) value;
map = strToMap(str);
} else if (BeanUtil.isReadableBean(value.getClass())) {
map = BeanUtil.beanToMap(value);
}
if (null != map) {
return mapToPair(leftType, rightType, map);
}
throw new ConvertException("Unsupported to map from [{}] of type: {}", value, value.getClass().getName());
}
/**
* 字符串转单个键值对的Map支持分隔符{@code :}{@code =}{@code ,}
*
* @param str 字符串
* @return map or null
*/
private static Map<CharSequence, CharSequence> strToMap(final CharSequence str) {
// key:value key=value key,value
final int index = StrUtil.indexOf(str,
c -> c == CharUtil.COLON || c == CharUtil.EQUAL || c == CharUtil.COMMA,
0, str.length());
if (index > -1) {
return MapUtil.of(str.subSequence(0, index), str.subSequence(index + 1, str.length()));
}
return null;
}
/**
* Map转Pair
*
* @param keyType 键类型
* @param valueType 值类型
* @param map 被转换的map
* @return Pair
*/
@SuppressWarnings("rawtypes")
private static Pair<?, ?> mapToPair(final Type keyType, final Type valueType, final Map map) {
Object left = null;
Object right = null;
if (1 == map.size()) {
final Map.Entry entry = (Map.Entry) map.entrySet().iterator().next();
left = entry.getKey();
right = entry.getValue();
} else if (2 == map.size()) {
left = map.get("left");
right = map.get("right");
}
final CompositeConverter convert = CompositeConverter.getInstance();
return Pair.of(
TypeUtil.isUnknown(keyType) ? left : convert.convert(keyType, left),
TypeUtil.isUnknown(valueType) ? right : convert.convert(valueType, right)
);
}
}

View File

@ -0,0 +1,102 @@
/*
* Copyright (c) 2023 looly(loolly@aliyun.com)
* Hutool is licensed under Mulan PSL v2.
* You can use this software according to the terms and conditions of the Mulan PSL v2.
* You may obtain a copy of Mulan PSL v2 at:
* http://license.coscl.org.cn/MulanPSL2
* THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
* EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
* MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
* See the Mulan PSL v2 for more details.
*/
package org.dromara.hutool.core.convert.impl;
import org.dromara.hutool.core.bean.BeanUtil;
import org.dromara.hutool.core.convert.CompositeConverter;
import org.dromara.hutool.core.convert.ConvertException;
import org.dromara.hutool.core.convert.Converter;
import org.dromara.hutool.core.lang.tuple.Pair;
import org.dromara.hutool.core.lang.tuple.Triple;
import org.dromara.hutool.core.reflect.TypeReference;
import org.dromara.hutool.core.reflect.TypeUtil;
import java.lang.reflect.Type;
import java.util.Map;
/**
* {@link Triple} 转换器支持以下类型转为Triple
* <ul>
* <li>Bean包含{@code getLeft}{@code getMiddle}{@code getRight}方法</li>
* </ul>
*
* @author looly
* @since 6.0.0
*/
public class TripleConverter implements Converter {
/**
* 单例
*/
public static final TripleConverter INSTANCE = new TripleConverter();
@Override
public Object convert(Type targetType, final Object value) throws ConvertException {
if (targetType instanceof TypeReference) {
targetType = ((TypeReference<?>) targetType).getType();
}
final Type leftType = TypeUtil.getTypeArgument(targetType, 0);
final Type middileType = TypeUtil.getTypeArgument(targetType, 1);
final Type rightType = TypeUtil.getTypeArgument(targetType, 2);
return convert(leftType, middileType, rightType, value);
}
/**
* 转换对象为指定键值类型的指定类型Map
*
* @param leftType 键类型
* @param middleType 中值类型
* @param rightType 值类型
* @param value 被转换的值
* @return 转换后的Map
* @throws ConvertException 转换异常或不支持的类型
*/
@SuppressWarnings("rawtypes")
public Triple<?, ?, ?> convert(final Type leftType, final Type middleType, final Type rightType, final Object value)
throws ConvertException {
Map map = null;
if (BeanUtil.isReadableBean(value.getClass())) {
map = BeanUtil.beanToMap(value);
}
if (null != map) {
return mapToTriple(leftType, middleType, rightType, map);
}
throw new ConvertException("Unsupported to map from [{}] of type: {}", value, value.getClass().getName());
}
/**
* Map转Entry
*
* @param leftType 键类型
* @param rightType 值类型
* @param map 被转换的map
* @return Entry
*/
@SuppressWarnings("rawtypes")
private static Triple<?, ?, ?> mapToTriple(final Type leftType, final Type middleType, final Type rightType, final Map map) {
final Object left = map.get("left");
final Object middle = map.get("middle");
final Object right = map.get("right");
final CompositeConverter convert = CompositeConverter.getInstance();
return Triple.of(
TypeUtil.isUnknown(leftType) ? left : convert.convert(leftType, left),
TypeUtil.isUnknown(middleType) ? middle : convert.convert(middleType, middle),
TypeUtil.isUnknown(rightType) ? right : convert.convert(rightType, right)
);
}
}

View File

@ -0,0 +1,39 @@
/*
* Copyright (c) 2023 looly(loolly@aliyun.com)
* Hutool is licensed under Mulan PSL v2.
* You can use this software according to the terms and conditions of the Mulan PSL v2.
* You may obtain a copy of Mulan PSL v2 at:
* http://license.coscl.org.cn/MulanPSL2
* THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
* EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
* MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
* See the Mulan PSL v2 for more details.
*/
package org.dromara.hutool.core.convert.impl;
import org.dromara.hutool.core.convert.ConvertException;
import org.dromara.hutool.core.convert.Converter;
import org.dromara.hutool.core.lang.tuple.Tuple;
import java.lang.reflect.Type;
/**
* {@link Tuple}转换器
*
* @author looly
* @since 6.0.0
*/
public class TupleConverter implements Converter {
/**
* 单例
*/
public static final TupleConverter INSTANCE = new TupleConverter();
@Override
public Object convert(final Type targetType, final Object value) throws ConvertException {
final Object[] convert = (Object[]) ArrayConverter.INSTANCE.convert(Object[].class, value);
return Tuple.of(convert);
}
}

View File

@ -57,6 +57,7 @@ public class DateUtil extends CalendarUtil {
"gmt", "ut", "utc", "est", "edt", "cst", "cdt", "mst", "mdt", "pst", "pdt"// 时间标准
};
// region ----- date
/**
* 当前时间转换为{@link DateTime}对象
*
@ -184,6 +185,7 @@ public class DateUtil extends CalendarUtil {
}
return new DateTime(temporalAccessor);
}
// endregion
/**
* 当前时间的时间戳
@ -1688,6 +1690,7 @@ public class DateUtil extends CalendarUtil {
return sb.toString();
}
// region ----- range
/**
* 创建日期范围生成器
*
@ -1793,6 +1796,7 @@ public class DateUtil extends CalendarUtil {
public static List<DateTime> rangeToList(final Date start, final Date end, final DateField unit, final int step) {
return ListUtil.of((Iterable<DateTime>) new DateRange(start, end, unit, step));
}
// endregion
/**
* 通过生日计算星座

View File

@ -16,6 +16,7 @@ import org.dromara.hutool.core.date.DateException;
import org.dromara.hutool.core.date.DatePattern;
import org.dromara.hutool.core.date.DateTime;
import org.dromara.hutool.core.date.format.DefaultDateBasic;
import org.dromara.hutool.core.lang.Console;
import org.dromara.hutool.core.regex.ReUtil;
import org.dromara.hutool.core.text.StrUtil;
import org.dromara.hutool.core.text.CharUtil;
@ -53,8 +54,9 @@ public class ISO8601DateParser extends DefaultDateBasic implements DateParser {
final int patternLength = DatePattern.UTC_MS_PATTERN.length();
// 格式类似2018-09-13T05:34:31.999Z-4表示减去4个单引号的长度
// -4 ~ -6范围表示匹配毫秒1~3位的情况
if (length <= patternLength - 4 && length >= patternLength - 6) {
// 2018-09-13T05:34:31.1Z - 2018-09-13T05:34:31.000000Z
if (length <= patternLength && length >= patternLength - 6) {
// 毫秒部分1-7位支持
return new DateTime(source, DatePattern.UTC_MS_FORMAT);
}
} else if (StrUtil.contains(source, '+')) {

View File

@ -90,7 +90,7 @@ public class FileTypeUtil {
/**
* 根据文件流的头部信息获得文件类型<br>
* 注意此方法会读取头部一些bytes造成此流接下来读取时缺少部分bytes<br>
* 因此如果想用此流流需支持{@link InputStream#reset()}方法
* 因此如果想用此流流需支持{@link InputStream#reset()}方法
* @param in {@link InputStream}
* @param isExact 是否精确匹配如果为false使用前64个bytes匹配如果为true使用前8192bytes匹配
* @return 类型文件的扩展名未找到为{@code null}
@ -105,7 +105,7 @@ public class FileTypeUtil {
/**
* 根据文件流的头部信息获得文件类型<br>
* 注意此方法会读取头部64个bytes造成此流接下来读取时缺少部分bytes<br>
* 因此如果想用此流流需支持{@link InputStream#reset()}方法
* 因此如果想用此流流需支持{@link InputStream#reset()}方法
* @param in {@link InputStream}
* @return 类型文件的扩展名未找到为{@code null}
* @throws IORuntimeException 读取流引起的异常
@ -117,7 +117,7 @@ public class FileTypeUtil {
/**
* 根据文件流的头部信息获得文件类型
* 注意此方法会读取头部64个bytes造成此流接下来读取时缺少部分bytes<br>
* 因此如果想用此流流需支持{@link InputStream#reset()}方法
* 因此如果想用此流流需支持{@link InputStream#reset()}方法
*
* <pre>
* 1无法识别类型默认按照扩展名识别
@ -137,7 +137,7 @@ public class FileTypeUtil {
/**
* 根据文件流的头部信息获得文件类型
* 注意此方法会读取头部一些bytes造成此流接下来读取时缺少部分bytes<br>
* 因此如果想用此流流需支持{@link InputStream#reset()}方法
* 因此如果想用此流流需支持{@link InputStream#reset()}方法
*
* <pre>
* 1无法识别类型默认按照扩展名识别

View File

@ -594,8 +594,9 @@ public class PathUtil {
public static String getMimeType(final Path file) {
try {
return Files.probeContentType(file);
} catch (final IOException e) {
throw new IORuntimeException(e);
} catch (final IOException ignore) {
// issue#3179使用OpenJDK可能抛出NoSuchFileException此处返回null
return null;
}
}

View File

@ -26,8 +26,6 @@ import java.util.Objects;
public class Triple<L, M, R> extends Pair<L, R> {
private static final long serialVersionUID = 1L;
protected M middle;
/**
* 构建Triple对象
*
@ -44,6 +42,8 @@ public class Triple<L, M, R> extends Pair<L, R> {
return new Triple<>(left, middle, right);
}
protected M middle;
/**
* 构造
*

View File

@ -35,6 +35,17 @@ import java.util.stream.StreamSupport;
public class Tuple implements Iterable<Object>, Serializable, Cloneable {
private static final long serialVersionUID = -7689304393482182157L;
/**
* 构建Tuple对象
*
* @param members 成员数组
* @return Tuple
* @since 6.0.0
*/
public static Tuple of(final Object... members) {
return new Tuple(members);
}
private final Object[] members;
private int hashCode;
private boolean cacheHash;

View File

@ -253,16 +253,21 @@ public class MapUtil extends MapGetUtil {
*/
@SuppressWarnings("unchecked")
public static <K, V> Map<K, V> createMap(final Class<?> mapType, final Supplier<Map<K, V>> defaultMap) {
if (null == mapType || mapType.isAssignableFrom(AbstractMap.class)) {
return defaultMap.get();
} else {
try {
return (Map<K, V>) ConstructorUtil.newInstance(mapType);
} catch (final Exception e) {
// 不支持的map类型返回默认的HashMap
return defaultMap.get();
}
Map<K, V> result = null;
if (null != mapType && !mapType.isAssignableFrom(AbstractMap.class)) {
result = (Map<K, V>) ConstructorUtil.newInstanceIfPossible(mapType);
}
if(null == result){
result = defaultMap.get();
}
if(!result.isEmpty()){
// issue#3162@Github在构造中put值会导致新建map带有值内容此处清空
result.clear();
}
return result;
}
// ----------------------------------------------------------------------------------------------- value of
@ -657,16 +662,12 @@ public class MapUtil extends MapGetUtil {
* @param editor 编辑器接口
* @return 编辑后的Map
*/
@SuppressWarnings("unchecked")
public static <K, V> Map<K, V> edit(final Map<K, V> map, final UnaryOperator<Entry<K, V>> editor) {
if (null == map || null == editor) {
return map;
}
Map<K, V> map2 = ConstructorUtil.newInstanceIfPossible(map.getClass());
if (null == map2) {
map2 = new HashMap<>(map.size(), 1f);
}
final Map<K, V> map2 = createMap(map.getClass(), ()-> new HashMap<>(map.size(), 1f));
if (isEmpty(map)) {
return map2;
}
@ -681,6 +682,8 @@ public class MapUtil extends MapGetUtil {
return map2;
}
/**
* 过滤<br>
* 过滤过程通过传入的Editor实现来返回需要的元素内容这个Filter实现可以实现以下功能
@ -739,10 +742,7 @@ public class MapUtil extends MapGetUtil {
return map;
}
Map<K, V> map2 = ConstructorUtil.newInstanceIfPossible(map.getClass());
if (null == map2) {
map2 = new HashMap<>(map.size(), 1f);
}
final Map<K, V> map2 = createMap(map.getClass(), ()-> new HashMap<>(map.size(), 1f));
if (isEmpty(map)) {
return map2;
}
@ -1013,15 +1013,7 @@ public class MapUtil extends MapGetUtil {
return map;
}
final Iterator<Entry<K, V>> iter = map.entrySet().iterator();
Entry<K, V> entry;
while (iter.hasNext()) {
entry = iter.next();
if (null == entry.getValue()) {
iter.remove();
}
}
map.entrySet().removeIf(entry -> null == entry.getValue());
return map;
}

View File

@ -18,6 +18,8 @@ import org.dromara.hutool.core.text.StrUtil;
import java.math.BigInteger;
import java.util.List;
import static java.lang.Math.min;
/**
* 数学相关方法工具类<br>
* 此工具类与{@link NumberUtil}属于一类工具NumberUtil偏向于简单数学计算的封装MathUtil偏向复杂数学计算
@ -31,11 +33,12 @@ public class MathUtil {
* 0-20对应的阶乘超过20的阶乘会超过Long.MAX_VALUE
*/
private static final long[] FACTORIALS = new long[]{
1L, 1L, 2L, 6L, 24L, 120L, 720L, 5040L, 40320L, 362880L, 3628800L, 39916800L, 479001600L, 6227020800L,
87178291200L, 1307674368000L, 20922789888000L, 355687428096000L, 6402373705728000L, 121645100408832000L,
2432902008176640000L};
1L, 1L, 2L, 6L, 24L, 120L, 720L, 5040L, 40320L, 362880L, 3628800L, 39916800L, 479001600L, 6227020800L,
87178291200L, 1307674368000L, 20922789888000L, 355687428096000L, 6402373705728000L, 121645100408832000L,
2432902008176640000L};
//--------------------------------------------------------------------------------------------- Arrangement
/**
* 计算排列数即A(n, m) = n!/(n-m)!
*
@ -61,7 +64,7 @@ public class MathUtil {
* 排列选择从列表中选择n个排列
*
* @param datas 待选列表
* @param m 选择个数
* @param m 选择个数
* @return 所有排列列表
*/
public static List<String[]> arrangementSelect(final String[] datas, final int m) {
@ -79,6 +82,7 @@ public class MathUtil {
}
//--------------------------------------------------------------------------------------------- Combination
/**
* 计算组合数即C(n, m) = n!/((n-m)!* m!)
*
@ -94,7 +98,7 @@ public class MathUtil {
* 组合选择从列表中选择n个组合
*
* @param datas 待选列表
* @param m 选择个数
* @param m 选择个数
* @return 所有组合列表
*/
public static List<String[]> combinationSelect(final String[] datas, final int m) {
@ -270,19 +274,57 @@ public class MathUtil {
}
/**
* 最大公约数
* 最大公约数<br>
* https://stackoverflow.com/questions/4009198/java-get-greatest-common-divisor<br>
* 来自Guava的IntMath.gcd
*
* @param m 第一个值
* @param n 第二个值
* @param a 第一个值
* @param b 第二个值
* @return 最大公约数
*/
public static int divisor(int m, int n) {
while (m % n != 0) {
final int temp = m % n;
m = n;
n = temp;
public static int gcd(int a, int b) {
/*
* The reason we require both arguments to be >= 0 is because otherwise, what do you return on
* gcd(0, Integer.MIN_VALUE)? BigInteger.gcd would return positive 2^31, but positive 2^31
* isn't an int.
*/
Assert.isTrue(a >= 0, "a must be >= 0");
Assert.isTrue(b >= 0, "b must be >= 0");
if (a == 0) {
// 0 % b == 0, so b divides a, but the converse doesn't hold.
// BigInteger.gcd is consistent with this decision.
return b;
} else if (b == 0) {
return a; // similar logic
}
return n;
/*
* Uses the binary GCD algorithm; see http://en.wikipedia.org/wiki/Binary_GCD_algorithm.
* This is >40% faster than the Euclidean algorithm in benchmarks.
*/
final int aTwos = Integer.numberOfTrailingZeros(a);
a >>= aTwos; // divide out all 2s
final int bTwos = Integer.numberOfTrailingZeros(b);
b >>= bTwos; // divide out all 2s
while (a != b) { // both a, b are odd
// The key to the binary GCD algorithm is as follows:
// Both a and b are odd. Assume a > b; then gcd(a - b, b) = gcd(a, b).
// But in gcd(a - b, b), a - b is even and b is odd, so we can divide out powers of two.
// We bend over backwards to avoid branching, adapting a technique from
// http://graphics.stanford.edu/~seander/bithacks.html#IntegerMinOrMax
final int delta = a - b; // can't overflow, since a and b are nonnegative
final int minDeltaOrZero = delta & (delta >> (Integer.SIZE - 1));
// equivalent to Math.min(delta, 0)
a = delta - minDeltaOrZero - minDeltaOrZero; // sets a to Math.abs(a - b)
// a is now nonnegative and even
b += minDeltaOrZero; // sets b to min(old a, b)
a >>= Integer.numberOfTrailingZeros(a); // divide out all 2s, since 2 doesn't divide b
}
return a << min(aTwos, bTwos);
}
/**
@ -293,7 +335,7 @@ public class MathUtil {
* @return 最小公倍数
*/
public static int multiple(final int m, final int n) {
return m * n / divisor(m, n);
return m * n / gcd(m, n);
}
private static int mathSubNode(final int selectNum, final int minNum) {

View File

@ -34,7 +34,7 @@ import java.util.Objects;
* JDK7中<strong>BigDecimal(double val)</strong>构造方法的结果有一定的不可预知性例如
*
* <pre>
* new BigDecimal(0.1)
* new BigDecimal(0.1) BigDecimal.valueOf(0.1)
* </pre>
* <p>
* 表示的不是<strong>0.1</strong>而是<strong>0.1000000000000000055511151231257827021181583404541015625</strong>

View File

@ -17,9 +17,10 @@ import org.dromara.hutool.core.collection.ListUtil;
import org.dromara.hutool.core.lang.Assert;
import org.dromara.hutool.core.lang.Singleton;
import org.dromara.hutool.core.regex.PatternPool;
import org.dromara.hutool.core.regex.ReUtil;
import org.dromara.hutool.core.text.CharUtil;
import org.dromara.hutool.core.text.StrUtil;
import org.dromara.hutool.core.text.split.SplitUtil;
import org.dromara.hutool.core.text.CharUtil;
import java.net.Inet4Address;
import java.net.InetAddress;
@ -567,6 +568,34 @@ public class Ipv4Util implements Ipv4Pool {
throw new IllegalArgumentException("Illegal position of ip Long: " + position);
}
}
/**
* 检测指定 IP 地址是否匹配通配符 wildcard
*
* @param wildcard 通配符 192.168.*.1
* @param ipAddress 待检测的 IP 地址
* @return 是否匹配
*/
public static boolean matches(final String wildcard, final String ipAddress) {
if (!ReUtil.isMatch(PatternPool.IPV4, ipAddress)) {
return false;
}
final String[] wildcardSegments = SplitUtil.splitToArray(wildcard, StrUtil.DOT);
final String[] ipSegments = SplitUtil.splitToArray(ipAddress, StrUtil.DOT);
if (wildcardSegments.length != ipSegments.length) {
return false;
}
for (int i = 0; i < wildcardSegments.length; i++) {
if (!"*".equals(wildcardSegments[i]) && !wildcardSegments[i].equals(ipSegments[i])) {
return false;
}
}
return true;
}
//-------------------------------------------------------------------------------- Private method start
/**

View File

@ -13,6 +13,7 @@
package org.dromara.hutool.core.reflect;
import org.dromara.hutool.core.array.ArrayUtil;
import org.dromara.hutool.core.collection.ListUtil;
import org.dromara.hutool.core.util.ObjUtil;
import java.lang.reflect.Field;
@ -21,6 +22,7 @@ import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.lang.reflect.TypeVariable;
import java.lang.reflect.WildcardType;
import java.util.List;
import java.util.Map;
/**
@ -276,23 +278,69 @@ public class TypeUtil {
* @since 4.5.2
*/
public static ParameterizedType toParameterizedType(final Type type) {
ParameterizedType result = null;
return toParameterizedType(type, 0);
}
/**
* {@link Type} 转换为{@link ParameterizedType}<br>
* {@link ParameterizedType}用于获取当前类或父类中泛型参数化后的类型<br>
* 一般用于获取泛型参数具体的参数类型例如
*
* <pre>{@code
* class A<T>
* class B extends A<String>;
* }</pre>
* <p>
* 通过此方法传入B.class即可得到B对应的{@link ParameterizedType}从而获取到String
*
* @param type {@link Type}
* @param interfaceIndex 实现的第几个接口
* @return {@link ParameterizedType}
* @since 4.5.2
*/
public static ParameterizedType toParameterizedType(final Type type, final int interfaceIndex) {
if (type instanceof ParameterizedType) {
result = (ParameterizedType) type;
} else if (type instanceof Class) {
final Class<?> clazz = (Class<?>) type;
Type genericSuper = clazz.getGenericSuperclass();
if (null == genericSuper || Object.class.equals(genericSuper)) {
// 如果类没有父类而是实现一些定义好的泛型接口则取接口的Type
final Type[] genericInterfaces = clazz.getGenericInterfaces();
if (ArrayUtil.isNotEmpty(genericInterfaces)) {
// 默认取第一个实现接口的泛型Type
genericSuper = genericInterfaces[0];
return (ParameterizedType) type;
}
if (type instanceof Class) {
final ParameterizedType[] generics = getGenerics((Class<?>) type);
if(generics.length > interfaceIndex){
return generics[interfaceIndex];
}
}
return null;
}
/**
* 获取指定类所有泛型父类和泛型接口
*
* @param clazz
* @return 泛型父类或接口数组
* @since 6.0.0
*/
public static ParameterizedType[] getGenerics(final Class<?> clazz) {
final List<ParameterizedType> result = ListUtil.of(false);
// 泛型父类父类及祖类优先级高
final Type genericSuper = clazz.getGenericSuperclass();
if(null != genericSuper && !Object.class.equals(genericSuper)){
final ParameterizedType parameterizedType = toParameterizedType(genericSuper);
if(null != parameterizedType){
result.add(parameterizedType);
}
}
// 泛型接口
final Type[] genericInterfaces = clazz.getGenericInterfaces();
if (ArrayUtil.isNotEmpty(genericInterfaces)) {
for (final Type genericInterface : genericInterfaces) {
if (genericInterface instanceof ParameterizedType) {
result.add((ParameterizedType) genericInterface);
}
}
result = toParameterizedType(genericSuper);
}
return result;
return result.toArray(new ParameterizedType[0]);
}
/**

View File

@ -32,7 +32,11 @@ import org.dromara.hutool.core.util.ByteUtil;
import org.dromara.hutool.core.util.CharsetUtil;
import org.dromara.hutool.core.util.ObjUtil;
import java.nio.ByteBuffer;
import java.nio.CharBuffer;
import java.nio.charset.Charset;
import java.nio.charset.CharsetDecoder;
import java.nio.charset.CodingErrorAction;
import java.text.MessageFormat;
import java.text.Normalizer;
import java.util.HashSet;
@ -3183,6 +3187,59 @@ public class CharSequenceUtil extends StrValidator {
}
return sub(string, 0, length) + "...";
}
/**
* 截断字符串使用UTF8编码为字节后不超过maxBytes长度
*
* @param str 原始字符串
* @param maxBytesLength 最大字节数
* @param appendDots 截断后是否追加省略号(...)
* @return 限制后的长度
*/
public static String limitByteLengthUtf8(final CharSequence str, final int maxBytesLength, final boolean appendDots) {
return limitByteLength(str, CharsetUtil.UTF_8, maxBytesLength, 4, appendDots);
}
/**
* 截断字符串使用其按照指定编码为字节后不超过maxBytes长度
*
* @param str 原始字符串
* @param charset 指定编码
* @param maxBytesLength 最大字节数
* @param factor 速算因子取该编码下单个字符的最大可能字节数
* @param appendDots 截断后是否追加省略号(...)
* @return 限制后的长度
*/
public static String limitByteLength(final CharSequence str, final Charset charset, final int maxBytesLength,
final int factor, final boolean appendDots) {
//字符数*速算因子<=最大字节数
if (str == null || str.length() * factor <= maxBytesLength) {
return str(str);
}
final byte[] sba = ByteUtil.toBytes(str, charset);
if (sba.length <= maxBytesLength) {
return str(str);
}
//限制字节数
final int limitBytes;
if (appendDots) {
limitBytes = maxBytesLength - "...".getBytes(charset).length;
} else {
limitBytes = maxBytesLength;
}
final ByteBuffer bb = ByteBuffer.wrap(sba, 0, limitBytes);
final CharBuffer cb = CharBuffer.allocate(limitBytes);
final CharsetDecoder decoder = charset.newDecoder();
//忽略被截断的字符
decoder.onMalformedInput(CodingErrorAction.IGNORE);
decoder.decode(bb, cb, true);
decoder.flush(cb);
final String result = new String(cb.array(), 0, cb.position());
if (appendDots) {
return result + "...";
}
return result;
}
// endregion
// region ----- firstXXX

View File

@ -15,16 +15,7 @@ package org.dromara.hutool.core.thread;
import org.dromara.hutool.core.lang.builder.Builder;
import org.dromara.hutool.core.util.ObjUtil;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.RejectedExecutionHandler;
import java.util.concurrent.SynchronousQueue;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.*;
/**
* {@link ThreadPoolExecutor} 建造者
@ -42,8 +33,10 @@ import java.util.concurrent.TimeUnit;
public class ExecutorBuilder implements Builder<ThreadPoolExecutor> {
private static final long serialVersionUID = 1L;
/** 默认的等待队列容量 */
public static final int DEFAULT_QUEUE_CAPACITY = 1024;
/**
* 默认的等待队列容量
*/
public static final int DEFAULT_QUEUE_CAPACITY = Integer.MAX_VALUE;
/**
* 初始池大小
@ -137,6 +130,18 @@ public class ExecutorBuilder implements Builder<ThreadPoolExecutor> {
return this;
}
/**
* 使用{@link LinkedBlockingQueue} 作为等待队列<br>
* 队列满时运行线程小于maxPoolSize时会创建新线程否则触发异常策略
*
* @param capacity 队列容量
* @return this
* @since 6.0.0
*/
public ExecutorBuilder useLinkedBlockingQueue(final int capacity) {
return setWorkQueue(new LinkedBlockingQueue<>(capacity));
}
/**
* 使用{@link ArrayBlockingQueue} 做为等待队列<br>
* 有界队列相对无界队列有利于控制队列大小队列满时运行线程小于maxPoolSize时会创建新线程否则触发异常策略
@ -257,12 +262,12 @@ public class ExecutorBuilder implements Builder<ThreadPoolExecutor> {
final RejectedExecutionHandler handler = ObjUtil.defaultIfNull(builder.handler, RejectPolicy.ABORT.getValue());
final ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(//
corePoolSize, //
maxPoolSize, //
keepAliveTime, TimeUnit.NANOSECONDS, //
workQueue, //
threadFactory, //
handler//
corePoolSize, //
maxPoolSize, //
keepAliveTime, TimeUnit.NANOSECONDS, //
workQueue, //
threadFactory, //
handler//
);
if (null != builder.allowCoreThreadTimeOut) {
threadPoolExecutor.allowCoreThreadTimeOut(builder.allowCoreThreadTimeOut);

View File

@ -39,25 +39,6 @@ import java.util.function.Supplier;
*/
public class ThreadUtil {
/**
* 新建一个线程池默认的策略如下
* <pre>
* 1. 初始线程数为corePoolSize指定的大小
* 2. 没有最大线程数限制
* 3. 默认使用LinkedBlockingQueue默认队列大小为1024
* </pre>
*
* @param corePoolSize 同时执行的线程数大小
* @return ExecutorService
*/
public static ExecutorService newExecutor(final int corePoolSize) {
final ExecutorBuilder builder = ExecutorBuilder.of();
if (corePoolSize > 0) {
builder.setCorePoolSize(corePoolSize);
}
return builder.build();
}
/**
* 获得一个新的线程池默认的策略如下
* <pre>
@ -86,10 +67,25 @@ public class ThreadUtil {
*/
public static ExecutorService newSingleExecutor() {
return ExecutorBuilder.of()//
.setCorePoolSize(1)//
.setMaxPoolSize(1)//
.setKeepAliveTime(0)//
.buildFinalizable();
.setCorePoolSize(1)//
.setMaxPoolSize(1)//
.setKeepAliveTime(0)//
.buildFinalizable();
}
/**
* 新建一个线程池默认的策略如下
* <pre>
* 1. 初始线程数为poolSize指定的大小
* 2. 最大线程数为poolSize指定的大小
* 3. 默认使用LinkedBlockingQueue默认无界队列
* </pre>
*
* @param poolSize 同时执行的线程数大小
* @return ExecutorService
*/
public static ExecutorService newExecutor(final int poolSize) {
return newExecutor(poolSize, poolSize);
}
/**
@ -102,9 +98,9 @@ public class ThreadUtil {
*/
public static ThreadPoolExecutor newExecutor(final int corePoolSize, final int maximumPoolSize) {
return ExecutorBuilder.of()
.setCorePoolSize(corePoolSize)
.setMaxPoolSize(maximumPoolSize)
.build();
.setCorePoolSize(corePoolSize)
.setMaxPoolSize(maximumPoolSize)
.build();
}
/**
@ -119,10 +115,10 @@ public class ThreadUtil {
*/
public static ExecutorService newExecutor(final int corePoolSize, final int maximumPoolSize, final int maximumQueueSize) {
return ExecutorBuilder.of()
.setCorePoolSize(corePoolSize)
.setMaxPoolSize(maximumPoolSize)
.setWorkQueue(new LinkedBlockingQueue<>(maximumQueueSize))
.build();
.setCorePoolSize(corePoolSize)
.setMaxPoolSize(maximumPoolSize)
.useLinkedBlockingQueue(maximumQueueSize)
.build();
}
/**
@ -184,7 +180,7 @@ public class ThreadUtil {
*/
public static ExecutorService newFixedExecutor(final int nThreads, final int maximumQueueSize, final String threadNamePrefix, final boolean isBlocked) {
return newFixedExecutor(nThreads, maximumQueueSize, threadNamePrefix,
(isBlocked ? RejectPolicy.BLOCK : RejectPolicy.ABORT).getValue());
(isBlocked ? RejectPolicy.BLOCK : RejectPolicy.ABORT).getValue());
}
/**
@ -207,11 +203,11 @@ public class ThreadUtil {
final String threadNamePrefix,
final RejectedExecutionHandler handler) {
return ExecutorBuilder.of()
.setCorePoolSize(nThreads).setMaxPoolSize(nThreads)
.setWorkQueue(new LinkedBlockingQueue<>(maximumQueueSize))
.setThreadFactory(createThreadFactory(threadNamePrefix))
.setHandler(handler)
.build();
.setCorePoolSize(nThreads).setMaxPoolSize(nThreads)
.setWorkQueue(new LinkedBlockingQueue<>(maximumQueueSize))
.setThreadFactory(createThreadFactory(threadNamePrefix))
.setHandler(handler)
.build();
}
/**

View File

@ -56,6 +56,8 @@ public class RandomUtil {
*/
public static final String BASE_CHAR_NUMBER = BASE_CHAR.toUpperCase() + BASE_CHAR_NUMBER_LOWER;
// region ----- get or create Random
/**
* 获取随机数生成器对象<br>
* ThreadLocalRandom是JDK 7之后提供并发产生随机数能够解决多个线程发生的竞争争夺
@ -165,6 +167,7 @@ public class RandomUtil {
public static Random getRandom(final boolean isSecure) {
return isSecure ? getSecureRandom() : getRandom();
}
// endregion
/**
* 获得随机Boolean值
@ -176,6 +179,18 @@ public class RandomUtil {
return 0 == randomInt(2);
}
/**
* 随机bytes
*
* @param length 长度
* @return bytes
*/
public static byte[] randomBytes(final int length) {
final byte[] bytes = new byte[length];
getRandom().nextBytes(bytes);
return bytes;
}
/**
* 随机汉字'\u4E00'-'\u9FFF'
*
@ -186,16 +201,7 @@ public class RandomUtil {
return (char) randomInt('\u4E00', '\u9FFF');
}
/**
* 获得指定范围内的随机数
*
* @param min 最小数包含
* @param max 最大数不包含
* @return 随机数
*/
public static int randomInt(final int min, final int max) {
return getRandom().nextInt(min, max);
}
// region ----- randomInt
/**
* 获得随机数int值
@ -210,27 +216,63 @@ public class RandomUtil {
/**
* 获得指定范围内的随机数 [0,limit)
*
* @param limit 限制随机数的范围不包括这个数
* @param limitExclude 限制随机数的范围不包括这个数
* @return 随机数
* @see Random#nextInt(int)
*/
public static int randomInt(final int limit) {
return getRandom().nextInt(limit);
public static int randomInt(final int limitExclude) {
return getRandom().nextInt(limitExclude);
}
/**
* 获得指定范围内的随机数[min, max)
* 获得指定范围内的随机数
*
* @param min 最小数包含
* @param max 最大数不包含
* @param minInclude 最小数包含
* @param maxExclude 最大数不包含
* @return 随机数
* @see ThreadLocalRandom#nextLong(long, long)
* @since 3.3.0
*/
public static long randomLong(final long min, final long max) {
return getRandom().nextLong(min, max);
public static int randomInt(final int minInclude, final int maxExclude) {
return randomInt(minInclude, maxExclude, true, false);
}
/**
* 获得指定范围内的随机数
*
* @param min 最小数
* @param max 最大数
* @param includeMin 是否包含最小值
* @param includeMax 是否包含最大值
* @return 随机数
*/
public static int randomInt(int min, int max, final boolean includeMin, final boolean includeMax) {
if (!includeMin) {
min++;
}
if (includeMax) {
max--;
}
return getRandom().nextInt(min, max);
}
/**
* 创建指定长度的随机索引
*
* @param length 长度
* @return 随机索引
* @since 5.2.1
*/
public static int[] randomInts(final int length) {
final int[] range = NumberUtil.range(length);
for (int i = 0; i < length; i++) {
final int random = randomInt(i, length);
ArrayUtil.swap(range, i, random);
}
return range;
}
// endregion
// region ----- randomLong
/**
* 获得随机数
*
@ -245,39 +287,114 @@ public class RandomUtil {
/**
* 获得指定范围内的随机数 [0,limit)
*
* @param limit 限制随机数的范围不包括这个数
* @param limitExclude 限制随机数的范围不包括这个数
* @return 随机数
* @see ThreadLocalRandom#nextLong(long)
*/
public static long randomLong(final long limit) {
return getRandom().nextLong(limit);
public static long randomLong(final long limitExclude) {
return getRandom().nextLong(limitExclude);
}
/**
* 获得指定范围内的随机数[min, max)
*
* @param minInclude 最小数包含
* @param maxExclude 最大数不包含
* @return 随机数
* @see ThreadLocalRandom#nextLong(long, long)
* @since 3.3.0
*/
public static long randomLong(final long minInclude, final long maxExclude) {
return randomLong(minInclude, maxExclude, true, false);
}
/**
* 获得指定范围内的随机数
*
* @param min 最小数包含
* @param max 最大数不包含
* @param min 最小数
* @param max 最大数
* @param includeMin 是否包含最小值
* @param includeMax 是否包含最大值
* @return 随机数
*/
public static long randomLong(long min, long max, final boolean includeMin, final boolean includeMax) {
if (!includeMin) {
min++;
}
if (includeMax) {
max--;
}
return getRandom().nextLong(min, max);
}
// endregion
// region ----- randomFloat
/**
* 获得随机数[0, 1)
*
* @return 随机数
* @see ThreadLocalRandom#nextFloat()
*/
public static float randomFloat() {
return getRandom().nextFloat();
}
/**
* 获得指定范围内的随机数 [0,limit)
*
* @param limitExclude 限制随机数的范围不包括这个数
* @return 随机数
*/
public static float randomFloat(final float limitExclude) {
return randomFloat(0, limitExclude);
}
/**
* 获得指定范围内的随机数[min, max)
*
* @param minInclude 最小数包含
* @param maxExclude 最大数不包含
* @return 随机数
* @see ThreadLocalRandom#nextFloat()
*/
public static float randomFloat(final float minInclude, final float maxExclude) {
if (minInclude == maxExclude) {
return minInclude;
}
return minInclude + ((maxExclude - minInclude) * getRandom().nextFloat());
}
// endregion
// region ----- randomDouble
/**
* 获得指定范围内的随机数
*
* @param minInclude 最小数包含
* @param maxExclude 最大数不包含
* @return 随机数
* @see ThreadLocalRandom#nextDouble(double, double)
* @since 3.3.0
*/
public static double randomDouble(final double min, final double max) {
return getRandom().nextDouble(min, max);
public static double randomDouble(final double minInclude, final double maxExclude) {
return getRandom().nextDouble(minInclude, maxExclude);
}
/**
* 获得指定范围内的随机数
*
* @param min 最小数包含
* @param max 最大数不包含
* @param minInclude 最小数包含
* @param maxExclude 最大数不包含
* @param scale 保留小数位数
* @param roundingMode 保留小数的模式 {@link RoundingMode}
* @return 随机数
* @since 4.0.8
*/
public static double randomDouble(final double min, final double max, final int scale, final RoundingMode roundingMode) {
return NumberUtil.round(randomDouble(min, max), scale, roundingMode).doubleValue();
public static double randomDouble(final double minInclude, final double maxExclude, final int scale,
final RoundingMode roundingMode) {
return NumberUtil.round(randomDouble(minInclude, maxExclude), scale, roundingMode).doubleValue();
}
/**
@ -327,6 +444,9 @@ public class RandomUtil {
public static double randomDouble(final double limit, final int scale, final RoundingMode roundingMode) {
return NumberUtil.round(randomDouble(limit), scale, roundingMode).doubleValue();
}
// endregion
// region ----- randomBigDecimal
/**
* 获得指定范围内的随机数[0, 1)
@ -341,37 +461,28 @@ public class RandomUtil {
/**
* 获得指定范围内的随机数 [0,limit)
*
* @param limit 最大数不包含
* @param limitExclude 最大数不包含
* @return 随机数
* @since 4.0.9
*/
public static BigDecimal randomBigDecimal(final BigDecimal limit) {
return NumberUtil.toBigDecimal(getRandom().nextDouble(limit.doubleValue()));
public static BigDecimal randomBigDecimal(final BigDecimal limitExclude) {
return NumberUtil.toBigDecimal(getRandom().nextDouble(limitExclude.doubleValue()));
}
/**
* 获得指定范围内的随机数
*
* @param min 最小数包含
* @param max 最大数不包含
* @param minInclude 最小数包含
* @param maxExclude 最大数不包含
* @return 随机数
* @since 4.0.9
*/
public static BigDecimal randomBigDecimal(final BigDecimal min, final BigDecimal max) {
return NumberUtil.toBigDecimal(getRandom().nextDouble(min.doubleValue(), max.doubleValue()));
public static BigDecimal randomBigDecimal(final BigDecimal minInclude, final BigDecimal maxExclude) {
return NumberUtil.toBigDecimal(getRandom().nextDouble(minInclude.doubleValue(), maxExclude.doubleValue()));
}
// endregion
/**
* 随机bytes
*
* @param length 长度
* @return bytes
*/
public static byte[] randomBytes(final int length) {
final byte[] bytes = new byte[length];
getRandom().nextBytes(bytes);
return bytes;
}
// region ----- randomEle
/**
* 随机获得列表中的元素
@ -470,8 +581,8 @@ public class RandomUtil {
/**
* 生成从种子中获取随机数字
*
* @param size 指定产生随机数的个数
* @param seed 种子用于取随机数的int池
* @param size 指定产生随机数的个数
* @param seed 种子用于取随机数的int池
* @return 随机int数组
* @since 5.4.5
*/
@ -514,22 +625,9 @@ public class RandomUtil {
return result;
}
// endregion
/**
* 创建指定长度的随机索引
*
* @param length 长度
* @return 随机索引
* @since 5.2.1
*/
public static int[] randomInts(final int length) {
final int[] range = NumberUtil.range(length);
for (int i = 0; i < length; i++) {
final int random = randomInt(i, length);
ArrayUtil.swap(range, i, random);
}
return range;
}
// region ----- randomString
/**
* 获得一个随机的字符串只包含数字和大小写字母
@ -596,7 +694,7 @@ public class RandomUtil {
if (StrUtil.isEmpty(baseString)) {
return StrUtil.EMPTY;
}
if(length < 1){
if (length < 1) {
length = 1;
}
@ -608,6 +706,9 @@ public class RandomUtil {
}
return sb.toString();
}
// endregion
// region ---- randomChar
/**
* 随机数字数字为0~9单个数字
@ -639,6 +740,9 @@ public class RandomUtil {
public static char randomChar(final String baseString) {
return baseString.charAt(randomInt(baseString.length()));
}
// endregion
// region ----- weightRandom
/**
* 带有权重的随机生成器
@ -663,6 +767,9 @@ public class RandomUtil {
public static <T> WeightRandomSelector<T> weightRandom(final Iterable<WeightObj<T>> weightObjs) {
return new WeightRandomSelector<>(weightObjs);
}
// endregion
// region ----- randomDate
/**
* 以当天为基准随机产生一个日期
@ -693,5 +800,5 @@ public class RandomUtil {
return DateUtil.offset(baseDate, dateField, randomInt(min, max));
}
// endregion
}

View File

@ -1090,6 +1090,26 @@ public class CollUtilTest {
}
}
@ToString(callSuper = true)
@EqualsAndHashCode(callSuper = true)
@Data
static class Cat extends Animal {
public Cat(final String name, final Integer age) {
super(name, age);
}
}
@ToString(callSuper = true)
@EqualsAndHashCode(callSuper = true)
@Data
static class Pig extends Animal {
public Pig(final String name, final Integer age) {
super(name, age);
}
}
@Test
public void getFirstTest() {
Assertions.assertNull(CollUtil.getFirst(null));
@ -1162,6 +1182,29 @@ public class CollUtilTest {
Assertions.assertNull(CollUtil.max(null));
}
@Test
public void unionExtendTest() {
final List<Dog> dog = Arrays.asList(new Dog("dog1", 12), new Dog("dog2", 12));
final List<Cat> cat = Arrays.asList(new Cat("cat1", 12), new Cat("cat2", 12));
Assertions.assertEquals(CollUtil.union(dog, cat).size(), dog.size() + cat.size());
}
@Test
public void unionAllExtendTest() {
final List<Dog> dog = Arrays.asList(new Dog("dog1", 12), new Dog("dog2", 12));
final List<Cat> cat = Arrays.asList(new Cat("cat1", 12), new Cat("cat2", 12));
final List<Pig> pig = Arrays.asList(new Pig("pig1", 12), new Pig("pig2", 12));
Assertions.assertEquals(CollUtil.unionAll(dog, cat, pig).size(), dog.size() + cat.size() + pig.size());
}
@Test
public void unionDistinctExtendTest() {
final List<Dog> dog = Arrays.asList(new Dog("dog1", 12), new Dog("dog1", 12)); // same
final List<Cat> cat = Arrays.asList(new Cat("cat1", 12), new Cat("cat2", 12));
final List<Pig> pig = Arrays.asList(new Pig("pig1", 12), new Pig("pig2", 12));
Assertions.assertEquals(CollUtil.unionDistinct(dog, cat, pig).size(), 5);
}
@Test
public void flatListTest1() {

View File

@ -1088,4 +1088,11 @@ public class DateUtilTest {
Assertions.assertEquals(71, DateUtil.age(DateUtil.parse("1952-02-13"), DateUtil.parse("2023-02-14")));
Assertions.assertEquals(0, DateUtil.age(DateUtil.parse("2023-02-14"), DateUtil.parse("2023-02-14")));
}
@Test
void issueI7H34NTest() {
final DateTime parse = DateUtil.parse("2019-10-22T09:56:03.000123Z");
Assertions.assertNotNull(parse);
Assertions.assertEquals("2019-10-22 09:56:03", parse.toString());
}
}

View File

@ -93,4 +93,10 @@ public class PathUtilTest {
public void moveTest2(){
PathUtil.move(Paths.get("D:\\project\\test1.txt"), Paths.get("D:\\project\\test2.txt"), false);
}
@Test
public void issue3179Test() {
final String mimeType = PathUtil.getMimeType(Paths.get("xxxx.jpg"));
Assertions.assertEquals("image/jpeg", mimeType);
}
}

View File

@ -285,4 +285,26 @@ public class MapUtilTest {
final Map<String, Object> map = MapUtil.renameKey(v1, "name", "newName");
Assertions.assertEquals("张三", map.get("newName"));
}
@Test
public void removeNullValueTest() {
final Dict v1 = Dict.of().set("id", 12).set("name", null).set("age", null);
final Map<String, Object> map = MapUtil.removeNullValue(v1);
Assertions.assertEquals(1, map.size());
}
@Test
public void issue3162Test() {
final Map<String, Object> map = new HashMap<String, Object>() {
private static final long serialVersionUID = 1L;
{
put("a", "1");
put("b", "2");
put("c", "3");
}};
final Map<String, Object> filtered = MapUtil.filter(map, "a", "b");
Assertions.assertEquals(2, filtered.size());
Assertions.assertEquals("1", filtered.get("a"));
Assertions.assertEquals("2", filtered.get("b"));
}
}

View File

@ -229,4 +229,13 @@ public class Ipv4UtilTest {
final boolean match = ReUtil.isMatch(PatternPool.MAC_ADDRESS, macAddress);
Assertions.assertTrue(match);
}
@Test
public void matchesTest() {
final boolean matches1 = Ipv4Util.matches("127.*.*.1", "127.0.0.1");
Assertions.assertTrue(matches1);
final boolean matches2 = Ipv4Util.matches("192.168.*.1", "127.0.0.1");
Assertions.assertFalse(matches2);
}
}

View File

@ -0,0 +1,60 @@
/*
* Copyright (c) 2023 looly(loolly@aliyun.com)
* Hutool is licensed under Mulan PSL v2.
* You can use this software according to the terms and conditions of the Mulan PSL v2.
* You may obtain a copy of Mulan PSL v2 at:
* http://license.coscl.org.cn/MulanPSL2
* THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
* EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
* MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
* See the Mulan PSL v2 for more details.
*/
package org.dromara.hutool.core.reflect;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
import java.lang.reflect.Type;
public class IssueI7CRIWTest {
@Test
void getTypeArgumentsTest() {
// 无法从继承获取泛型则从接口获取
Type type = TypeUtil.getTypeArgument(C.class);
Assertions.assertEquals(type, String.class);
// 继承和第一个接口都非泛型接口则从找到的第一个泛型接口获取
type = TypeUtil.getTypeArgument(D.class);
Assertions.assertEquals(type, String.class);
}
static class A{
}
static class AT<T>{
}
interface Face1<T>{
}
interface Face2{
}
static class B extends A{
}
static class C extends A implements Face1<String>{
}
static class D extends A implements Face2, Face1<String>{
}
}

View File

@ -287,9 +287,33 @@ public class CharSequenceUtilTest {
@Test
void codeLengthTest() {
String a = "🍒🐽";
final String a = "🍒🐽";
final int i = StrUtil.codeLength(a);
Assertions.assertEquals(4, a.length());
Assertions.assertEquals(2, i);
}
@Test
public void limitByteLengthUtf8Test() {
final String str = "这是This一段中英文";
String ret = StrUtil.limitByteLengthUtf8(str, 12, true);
Assertions.assertEquals("这是Thi...", ret);
ret = StrUtil.limitByteLengthUtf8(str, 13, true);
Assertions.assertEquals("这是This...", ret);
ret = StrUtil.limitByteLengthUtf8(str, 14, true);
Assertions.assertEquals("这是This...", ret);
ret = StrUtil.limitByteLengthUtf8(str, 999, true);
Assertions.assertEquals(str, ret);
}
@Test
public void limitByteLengthTest() {
final String str = "This is English";
final String ret = StrUtil.limitByteLength(str, CharsetUtil.ISO_8859_1,10, 1, false);
Assertions.assertEquals("This is En", ret);
}
}

View File

@ -0,0 +1,31 @@
/*
* Copyright (c) 2023 looly(loolly@aliyun.com)
* Hutool is licensed under Mulan PSL v2.
* You can use this software according to the terms and conditions of the Mulan PSL v2.
* You may obtain a copy of Mulan PSL v2 at:
* http://license.coscl.org.cn/MulanPSL2
* THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
* EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
* MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
* See the Mulan PSL v2 for more details.
*/
package org.dromara.hutool.core.thread;
import org.dromara.hutool.core.lang.Console;
import java.util.concurrent.ExecutorService;
public class Issue3167Test {
public static void main(final String[] args) {
final ExecutorService executorService = ThreadUtil.newExecutor(2);
for (int i = 0; i < 1035; i++) {
final int finalI = i;
executorService.submit(() -> {
Console.log(Thread.currentThread().getName(), finalI);
ThreadUtil.sleep(5000);
});
}
}
}

View File

@ -0,0 +1,32 @@
/*
* Copyright (c) 2023 looly(loolly@aliyun.com)
* Hutool is licensed under Mulan PSL v2.
* You can use this software according to the terms and conditions of the Mulan PSL v2.
* You may obtain a copy of Mulan PSL v2 at:
* http://license.coscl.org.cn/MulanPSL2
* THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
* EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
* MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
* See the Mulan PSL v2 for more details.
*/
package org.dromara.hutool.db;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
public class IssueI7GUKOTest {
@Test
void entityTest() {
final Entity entity = Entity.of("data")
.set("sort", "test")
.set("pcode", "test")
.set("code", "test")
.set("define", "test")
.set("icon", "test")
.set("label", "test")
.set("stu", "test");
Assertions.assertEquals("[sort, pcode, code, define, icon, label, stu]", entity.keySet().toString());
}
}

View File

@ -22,7 +22,7 @@ public class MetaUtilTest {
@Test
public void getTablesTest() {
final List<String> tables = MetaUtil.getTables(ds);
Assertions.assertEquals("user", tables.get(0));
Assertions.assertTrue(tables.contains("user"));
}
@Test

View File

@ -124,7 +124,7 @@ public class JSONConverter implements Converter {
* <li>String: 转换为相应的对象"和'包围的字符串返回原字符串,""返回{@code null}</li>
* <li>ArrayIterableIterator转换为JSONArray</li>
* <li>Bean对象转为JSONObject</li>
* <li>Number返回原对象</li>
* <li>NumberBoolean返回原对象</li>
* <li>null返回{@code null}</li>
* </ul>
*
@ -138,7 +138,7 @@ public class JSONConverter implements Converter {
return null;
}
final JSON json;
if (obj instanceof JSON || obj instanceof Number) {
if (obj instanceof JSON || obj instanceof Number || obj instanceof Boolean) {
return obj;
} else if (obj instanceof CharSequence) {
final String jsonStr = StrUtil.trim((CharSequence) obj);

View File

@ -0,0 +1,35 @@
/*
* Copyright (c) 2023 looly(loolly@aliyun.com)
* Hutool is licensed under Mulan PSL v2.
* You can use this software according to the terms and conditions of the Mulan PSL v2.
* You may obtain a copy of Mulan PSL v2 at:
* http://license.coscl.org.cn/MulanPSL2
* THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
* EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
* MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
* See the Mulan PSL v2 for more details.
*/
package org.dromara.hutool.json;
import org.dromara.hutool.core.reflect.TypeReference;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
import java.util.Map;
/**
* https://gitee.com/dromara/hutool/issues/I7FQ29
*/
public class IssueI7FQ29Test {
@Test
void toMapTest() {
final String jsonStr = "{\"trans_no\": \"java.lang.String\"}";
final Map<String, Class<?>> map = JSONUtil.toBean(jsonStr, new TypeReference<Map<String, Class<?>>>() {
});
Assertions.assertNotNull(map);
Assertions.assertEquals(String.class, map.get("trans_no"));
}
}

View File

@ -0,0 +1,47 @@
/*
* Copyright (c) 2023 looly(loolly@aliyun.com)
* Hutool is licensed under Mulan PSL v2.
* You can use this software according to the terms and conditions of the Mulan PSL v2.
* You may obtain a copy of Mulan PSL v2 at:
* http://license.coscl.org.cn/MulanPSL2
* THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
* EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
* MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
* See the Mulan PSL v2 for more details.
*/
package org.dromara.hutool.json;
import org.dromara.hutool.core.lang.Console;
import org.dromara.hutool.core.lang.tuple.Pair;
import org.dromara.hutool.core.lang.tuple.Triple;
import org.dromara.hutool.core.lang.tuple.Tuple;
import org.dromara.hutool.core.reflect.TypeReference;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
public class IssueI7GPGXTest {
@Test
public void pairToBeanTest() {
final Pair<String, Boolean> hutoolPair = Pair.of("test1", true);
final String a = JSONUtil.toJsonStr(hutoolPair);
final Pair<String, Boolean> pair = JSONUtil.toBean(a, new TypeReference<Pair<String, Boolean>>() {});
Assertions.assertEquals(hutoolPair, pair);
}
@Test
void tripleToBeanTest() {
final Triple<String, Integer, Boolean> hutoolTriple = Triple.of("aaa", 123, true);
final String a = JSONUtil.toJsonStr(hutoolTriple);
final Triple<String, Integer, Boolean> pair = JSONUtil.toBean(a, new TypeReference<Triple<String, Integer, Boolean>>() {});
Assertions.assertEquals(hutoolTriple, pair);
}
@Test
void tupleToBeanTest() {
final Tuple hutoolTriple = Tuple.of("aaa", 123, true);
final String a = JSONUtil.toJsonStr(hutoolTriple);
final Tuple pair = JSONUtil.toBean(a, Tuple.class);
Assertions.assertEquals(hutoolTriple, pair);
}
}

View File

@ -2,6 +2,7 @@ package org.dromara.hutool.json;
import org.dromara.hutool.core.collection.ListUtil;
import org.dromara.hutool.core.date.DateUtil;
import org.dromara.hutool.core.lang.Console;
import org.dromara.hutool.core.map.MapUtil;
import org.dromara.hutool.core.math.NumberUtil;
import org.dromara.hutool.json.serialize.JSONStringer;
@ -334,4 +335,10 @@ public class JSONUtilTest {
private Byte[] d = new Byte[0];
private Byte[] e = new Byte[1];
}
@Test
void toJsonStrOfBooleanTest() {
final String jsonStr = JSONUtil.toJsonStr(true);
Assertions.assertEquals("true", jsonStr);
}
}

View File

@ -428,13 +428,20 @@ public class ExcelReader extends ExcelBase<ExcelReader> {
/**
* 获取Excel写出器<br>
* 在读取Excel并做一定编辑后获取写出器写出<br>
* 注意只读方式下此方法无效
* 在读取Excel并做一定编辑后获取写出器写出规则如下
* <ul>
* <li>1. 当从流中读取时转换为Writer直接使用Sheet对象此时修改不会影响源文件Writer中flush需要指定新的路径</li>
* <li>2. 当从文件读取时直接获取文件及sheet名称此时可以修改原文件</li>
* </ul>
*
* @return {@link ExcelWriter}
* @since 4.0.6
*/
public ExcelWriter getWriter() {
if (null == this.destFile) {
// 非读取文件形式直接获取sheet操作
return new ExcelWriter(this.sheet);
}
return ExcelUtil.getWriter(this.destFile, this.sheet.getSheetName());
}

View File

@ -125,7 +125,7 @@ public class SettingLoader {
if (line == null) {
break;
}
line = line.trim();
line = StrUtil.trim(line);
// 跳过注释行和空行
if (StrUtil.isBlank(line) || StrUtil.startWith(line, COMMENT_FLAG_PRE)) {
continue;
@ -133,7 +133,7 @@ public class SettingLoader {
// 记录分组名
if (StrUtil.isWrap(line, CharUtil.BRACKET_START, CharUtil.BRACKET_END)) {
group = line.substring(1, line.length() - 1).trim();
group = StrUtil.trim(line.substring(1, line.length() - 1));
continue;
}
@ -149,7 +149,7 @@ public class SettingLoader {
if (this.isUseVariable) {
value = replaceVar(group, value);
}
this.groupedMap.put(group, keyValue[0].trim(), value);
this.groupedMap.put(group, StrUtil.trim(keyValue[0]), value);
}
} finally {
IoUtil.closeQuietly(reader);

View File

@ -0,0 +1,28 @@
/*
* Copyright (c) 2023 looly(loolly@aliyun.com)
* Hutool is licensed under Mulan PSL v2.
* You can use this software according to the terms and conditions of the Mulan PSL v2.
* You may obtain a copy of Mulan PSL v2 at:
* http://license.coscl.org.cn/MulanPSL2
* THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
* EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
* MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
* See the Mulan PSL v2 for more details.
*/
package org.dromara.hutool.setting;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
public class IssueI7G34ETest {
@SuppressWarnings("MismatchedQueryAndUpdateOfCollection")
@Test
void readWithBomTest() {
final Setting setting = new Setting("test_with_bom.setting");
final String s = setting.getStrByGroup("key1", "line1");
Assertions.assertEquals("value1", s);
}
}

View File

@ -0,0 +1,3 @@
[line1]
key1 = value1
key2 = value2