mirror of
https://gitee.com/dromara/hutool.git
synced 2025-04-24 18:04:54 +08:00
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:
commit
98a6a3686d
@ -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();
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
/**
|
||||
|
97
hutool-core/src/main/java/org/dromara/hutool/core/compress/ZipReplacer.java
Executable file
97
hutool-core/src/main/java/org/dromara/hutool/core/compress/ZipReplacer.java
Executable 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);
|
||||
}
|
||||
}
|
@ -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;
|
||||
}
|
||||
|
@ -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
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
|
||||
/**
|
||||
|
@ -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) {
|
||||
|
@ -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)
|
||||
);
|
||||
}
|
||||
}
|
@ -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)
|
||||
);
|
||||
}
|
||||
}
|
@ -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);
|
||||
}
|
||||
}
|
@ -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
|
||||
|
||||
/**
|
||||
* 通过生日计算星座
|
||||
|
@ -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, '+')) {
|
||||
|
@ -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、无法识别类型默认按照扩展名识别
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
|
||||
/**
|
||||
* 构造
|
||||
*
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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) {
|
||||
|
@ -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>
|
||||
|
@ -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
|
||||
|
||||
/**
|
||||
|
@ -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]);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
|
@ -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();
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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() {
|
||||
|
@ -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());
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
@ -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"));
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
@ -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>{
|
||||
|
||||
}
|
||||
}
|
@ -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);
|
||||
|
||||
}
|
||||
}
|
||||
|
31
hutool-core/src/test/java/org/dromara/hutool/core/thread/Issue3167Test.java
Executable file
31
hutool-core/src/test/java/org/dromara/hutool/core/thread/Issue3167Test.java
Executable 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);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
32
hutool-db/src/test/java/org/dromara/hutool/db/IssueI7GUKOTest.java
Executable file
32
hutool-db/src/test/java/org/dromara/hutool/db/IssueI7GUKOTest.java
Executable 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());
|
||||
}
|
||||
}
|
@ -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
|
||||
|
@ -124,7 +124,7 @@ public class JSONConverter implements Converter {
|
||||
* <li>String: 转换为相应的对象,"和'包围的字符串返回原字符串,""返回{@code null}</li>
|
||||
* <li>Array、Iterable、Iterator:转换为JSONArray</li>
|
||||
* <li>Bean对象:转为JSONObject</li>
|
||||
* <li>Number:返回原对象</li>
|
||||
* <li>Number、Boolean:返回原对象</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);
|
||||
|
35
hutool-json/src/test/java/org/dromara/hutool/json/IssueI7FQ29Test.java
Executable file
35
hutool-json/src/test/java/org/dromara/hutool/json/IssueI7FQ29Test.java
Executable 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"));
|
||||
}
|
||||
}
|
47
hutool-json/src/test/java/org/dromara/hutool/json/IssueI7GPGXTest.java
Executable file
47
hutool-json/src/test/java/org/dromara/hutool/json/IssueI7GPGXTest.java
Executable 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);
|
||||
}
|
||||
}
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
@ -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());
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
|
28
hutool-setting/src/test/java/org/dromara/hutool/setting/IssueI7G34ETest.java
Executable file
28
hutool-setting/src/test/java/org/dromara/hutool/setting/IssueI7G34ETest.java
Executable 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);
|
||||
}
|
||||
}
|
3
hutool-setting/src/test/resources/test_with_bom.setting
Executable file
3
hutool-setting/src/test/resources/test_with_bom.setting
Executable file
@ -0,0 +1,3 @@
|
||||
[line1]
|
||||
key1 = value1
|
||||
key2 = value2
|
Loading…
Reference in New Issue
Block a user