This commit is contained in:
Looly 2024-09-13 22:02:25 +08:00
parent 47fe46c495
commit ee13716d42
18 changed files with 646 additions and 522 deletions

View File

@ -30,6 +30,7 @@ import org.dromara.hutool.core.comparator.PinyinComparator;
import org.dromara.hutool.core.comparator.PropertyComparator;
import org.dromara.hutool.core.convert.CompositeConverter;
import org.dromara.hutool.core.convert.ConvertUtil;
import org.dromara.hutool.core.convert.Converter;
import org.dromara.hutool.core.exception.ExceptionUtil;
import org.dromara.hutool.core.func.SerBiConsumer;
import org.dromara.hutool.core.func.SerConsumer3;
@ -1571,8 +1572,24 @@ public class CollUtil {
* @param elementType 元素类型为空时使用Object类型来接纳所有类型
* @return 被加入集合
*/
public static <T> Collection<T> addAll(final Collection<T> collection, final Object value, final Type elementType) {
return addAll(collection, value, elementType, null);
}
/**
* 将指定对象全部加入到集合中<br>
* 提供的对象如果为集合类型会自动转换为目标元素类型<br>
* 如果为String支持类似于[1,2,3,4] 或者 1,2,3,4 这种格式
*
* @param <T> 元素类型
* @param collection 被加入的集合
* @param value 对象可能为IteratorIterableEnumerationArray或者与集合元素类型一致
* @param elementType 元素类型为空时使用Object类型来接纳所有类型
* @param converter 自定义元素类型转换器{@code null}表示使用默认转换器
* @return 被加入集合
*/
@SuppressWarnings({"unchecked", "rawtypes"})
public static <T> Collection<T> addAll(final Collection<T> collection, final Object value, Type elementType) {
public static <T> Collection<T> addAll(final Collection<T> collection, final Object value, Type elementType, final Converter converter) {
if (null == collection || null == value) {
return collection;
}
@ -1595,7 +1612,7 @@ public class CollUtil {
iter = IterUtil.getIter(value);
}
final CompositeConverter convert = CompositeConverter.getInstance();
final Converter convert = ObjUtil.defaultIfNull(converter, CompositeConverter::getInstance);
while (iter.hasNext()) {
collection.add((T) convert.convert(elementType, iter.next()));
}

View File

@ -23,6 +23,7 @@ import org.dromara.hutool.core.reflect.TypeReference;
import org.dromara.hutool.core.reflect.TypeUtil;
import org.dromara.hutool.core.util.ObjUtil;
import java.io.Serializable;
import java.lang.reflect.Type;
import java.util.Optional;
@ -36,7 +37,7 @@ import java.util.Optional;
*
* @author Looly
*/
public class CompositeConverter extends RegisterConverter {
public class CompositeConverter implements Converter, Serializable {
private static final long serialVersionUID = 1L;
/**
@ -46,7 +47,12 @@ public class CompositeConverter extends RegisterConverter {
/**
* 静态初始化器由JVM来保证线程安全
*/
private static final CompositeConverter INSTANCE = new CompositeConverter();
private static final CompositeConverter INSTANCE;
static {
INSTANCE = new CompositeConverter();
INSTANCE.registerConverter = new RegisterConverter(INSTANCE);
INSTANCE.specialConverter = new SpecialConverter(INSTANCE);
}
}
/**
@ -58,11 +64,38 @@ public class CompositeConverter extends RegisterConverter {
return SingletonHolder.INSTANCE;
}
private RegisterConverter registerConverter;
private SpecialConverter specialConverter;
/**
* 构造
*/
public CompositeConverter() {
super();
private CompositeConverter() {
}
/**
* 登记自定义转换器符合{@link MatcherConverter#match(Type, Class, Object)}则使用其转换器<br>
* 注意如果单例使用此方法会影响全局
*
* @param converter 转换器
* @return this
*/
public CompositeConverter register(final MatcherConverter converter) {
registerConverter.register(converter);
return this;
}
/**
* 登记自定义转换器登记的目标类型必须一致<br>
* 注意如果单例使用此方法会影响全局
*
* @param type 转换的目标类型
* @param converter 转换器
* @return this
*/
public CompositeConverter register(final Type type, final Converter converter) {
registerConverter.register(type, converter);
return this;
}
/**
@ -142,7 +175,7 @@ public class CompositeConverter extends RegisterConverter {
}
// 标准转换器
final Converter converter = getConverter(type, value, isCustomFirst);
final Converter converter = registerConverter.getConverter(type, value, isCustomFirst);
if (null != converter) {
return converter.convert(type, value, defaultValue);
}
@ -157,7 +190,7 @@ public class CompositeConverter extends RegisterConverter {
}
// 特殊类型转换包括CollectionMap强转Array等
final T result = (T) SpecialConverter.getInstance().convert(type, rawType, value);
final T result = (T) specialConverter.convert(type, rawType, value);
if (null != result) {
return result;
}

View File

@ -0,0 +1,49 @@
/*
* Copyright (c) 2024 Hutool Team and hutool.cn
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.dromara.hutool.core.convert;
import org.dromara.hutool.core.lang.Assert;
/**
* 带根的转换器<br>
* 在嵌套对象转换中如果涉及子对象的转换使用根转换器转换
*
* @author Looly
* @since 6.0.0
*/
public abstract class ConverterWithRoot implements Converter {
protected final Converter rootConverter;
/**
* 构造
*
* @param rootConverter 根转换器
*/
public ConverterWithRoot(final Converter rootConverter) {
this.rootConverter = Assert.notNull(rootConverter);
}
/**
* 获取根转换器用于子转换器转换
*
* @return 根转换器
*/
public Converter getRootConverter() {
return rootConverter;
}
}

View File

@ -54,28 +54,9 @@ import java.util.concurrent.atomic.AtomicReference;
* @author looly
* @since 6.0.0
*/
public class RegisterConverter implements Converter, Serializable {
public class RegisterConverter extends ConverterWithRoot implements Serializable {
private static final long serialVersionUID = 1L;
/**
* 类级的内部类也就是静态的成员式内部类该内部类的实例与外部类的实例 没有绑定关系而且只有被调用到才会装载从而实现了延迟加载
*/
private static class SingletonHolder {
/**
* 静态初始化器由JVM来保证线程安全
*/
private static final RegisterConverter INSTANCE = new RegisterConverter();
}
/**
* 获得单例的 RegisterConverter
*
* @return RegisterConverter
*/
public static RegisterConverter getInstance() {
return RegisterConverter.SingletonHolder.INSTANCE;
}
/**
* 用户自定义类型转换器存储自定义匹配规则的一类对象的转换器
*/
@ -88,13 +69,16 @@ public class RegisterConverter implements Converter, Serializable {
/**
* 默认类型转换器
*/
private Map<Class<?>, Converter> defaultConverterMap;
private final Map<Class<?>, Converter> defaultConverterMap;
/**
* 构造
*
* @param rootConverter 根转换器用于子转换器转换
*/
public RegisterConverter() {
registerDefault();
public RegisterConverter(final Converter rootConverter) {
super(rootConverter);
this.defaultConverterMap = initDefault(rootConverter);
}
@Override
@ -179,7 +163,7 @@ public class RegisterConverter implements Converter, Serializable {
*
* @param type 转换的目标类型
* @param converter 转换器
* @return ConverterRegistry
* @return this
*/
public RegisterConverter register(final Type type, final Converter converter) {
if (null == customConverterMap) {
@ -197,7 +181,7 @@ public class RegisterConverter implements Converter, Serializable {
* 登记自定义转换器符合{@link MatcherConverter#match(Type, Class, Object)}则使用其转换器
*
* @param converter 转换器
* @return ConverterRegistry
* @return this
*/
public RegisterConverter register(final MatcherConverter converter) {
if (null == this.converterSet) {
@ -212,65 +196,68 @@ public class RegisterConverter implements Converter, Serializable {
}
/**
* 注册默认转换器
* 初始化默认转换器
*
* @return 默认转换器
*/
private void registerDefault() {
final Map<Class<?>, Converter> defaultConverterMap = new SafeConcurrentHashMap<>(64);
private static Map<Class<?>, Converter> initDefault(final Converter rootConverter) {
final Map<Class<?>, Converter> converterMap = new SafeConcurrentHashMap<>(64);
// 包装类转换器
defaultConverterMap.put(Character.class, CharacterConverter.INSTANCE);
defaultConverterMap.put(Boolean.class, BooleanConverter.INSTANCE);
defaultConverterMap.put(AtomicBoolean.class, AtomicBooleanConverter.INSTANCE);// since 3.0.8
converterMap.put(Character.class, CharacterConverter.INSTANCE);
converterMap.put(Boolean.class, BooleanConverter.INSTANCE);
converterMap.put(AtomicBoolean.class, AtomicBooleanConverter.INSTANCE);// since 3.0.8
final StringConverter stringConverter = new StringConverter();
defaultConverterMap.put(CharSequence.class, stringConverter);
defaultConverterMap.put(String.class, stringConverter);
converterMap.put(CharSequence.class, stringConverter);
converterMap.put(String.class, stringConverter);
// URI and URL
defaultConverterMap.put(URI.class, new URIConverter());
defaultConverterMap.put(URL.class, new URLConverter());
converterMap.put(URI.class, new URIConverter());
converterMap.put(URL.class, new URLConverter());
// 日期时间
defaultConverterMap.put(Calendar.class, new CalendarConverter());
defaultConverterMap.put(XMLGregorianCalendar.class, new XMLGregorianCalendarConverter());
converterMap.put(Calendar.class, new CalendarConverter());
converterMap.put(XMLGregorianCalendar.class, new XMLGregorianCalendarConverter());
// 日期时间 JDK8+(since 5.0.0)
defaultConverterMap.put(TemporalAccessor.class, TemporalAccessorConverter.INSTANCE);
defaultConverterMap.put(Instant.class, TemporalAccessorConverter.INSTANCE);
defaultConverterMap.put(LocalDateTime.class, TemporalAccessorConverter.INSTANCE);
defaultConverterMap.put(LocalDate.class, TemporalAccessorConverter.INSTANCE);
defaultConverterMap.put(LocalTime.class, TemporalAccessorConverter.INSTANCE);
defaultConverterMap.put(ZonedDateTime.class, TemporalAccessorConverter.INSTANCE);
defaultConverterMap.put(OffsetDateTime.class, TemporalAccessorConverter.INSTANCE);
defaultConverterMap.put(OffsetTime.class, TemporalAccessorConverter.INSTANCE);
defaultConverterMap.put(DayOfWeek.class, TemporalAccessorConverter.INSTANCE);
defaultConverterMap.put(Month.class, TemporalAccessorConverter.INSTANCE);
defaultConverterMap.put(MonthDay.class, TemporalAccessorConverter.INSTANCE);
converterMap.put(TemporalAccessor.class, TemporalAccessorConverter.INSTANCE);
converterMap.put(Instant.class, TemporalAccessorConverter.INSTANCE);
converterMap.put(LocalDateTime.class, TemporalAccessorConverter.INSTANCE);
converterMap.put(LocalDate.class, TemporalAccessorConverter.INSTANCE);
converterMap.put(LocalTime.class, TemporalAccessorConverter.INSTANCE);
converterMap.put(ZonedDateTime.class, TemporalAccessorConverter.INSTANCE);
converterMap.put(OffsetDateTime.class, TemporalAccessorConverter.INSTANCE);
converterMap.put(OffsetTime.class, TemporalAccessorConverter.INSTANCE);
converterMap.put(DayOfWeek.class, TemporalAccessorConverter.INSTANCE);
converterMap.put(Month.class, TemporalAccessorConverter.INSTANCE);
converterMap.put(MonthDay.class, TemporalAccessorConverter.INSTANCE);
defaultConverterMap.put(Period.class, new PeriodConverter());
defaultConverterMap.put(Duration.class, new DurationConverter());
converterMap.put(Period.class, new PeriodConverter());
converterMap.put(Duration.class, new DurationConverter());
// Reference
defaultConverterMap.put(WeakReference.class, ReferenceConverter.INSTANCE);// since 3.0.8
defaultConverterMap.put(SoftReference.class, ReferenceConverter.INSTANCE);// since 3.0.8
defaultConverterMap.put(AtomicReference.class, new AtomicReferenceConverter());// since 3.0.8
final ReferenceConverter referenceConverter = new ReferenceConverter(rootConverter);
converterMap.put(WeakReference.class, referenceConverter);// since 3.0.8
converterMap.put(SoftReference.class, referenceConverter);// since 3.0.8
converterMap.put(AtomicReference.class, new AtomicReferenceConverter(rootConverter));// since 3.0.8
//AtomicXXXArraysince 5.4.5
defaultConverterMap.put(AtomicIntegerArray.class, new AtomicIntegerArrayConverter());
defaultConverterMap.put(AtomicLongArray.class, new AtomicLongArrayConverter());
converterMap.put(AtomicIntegerArray.class, new AtomicIntegerArrayConverter());
converterMap.put(AtomicLongArray.class, new AtomicLongArrayConverter());
// 其它类型
defaultConverterMap.put(Locale.class, new LocaleConverter());
defaultConverterMap.put(Charset.class, new CharsetConverter());
defaultConverterMap.put(Path.class, new PathConverter());
defaultConverterMap.put(Currency.class, new CurrencyConverter());// since 3.0.8
defaultConverterMap.put(UUID.class, new UUIDConverter());// since 4.0.10
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
converterMap.put(Locale.class, new LocaleConverter());
converterMap.put(Charset.class, new CharsetConverter());
converterMap.put(Path.class, new PathConverter());
converterMap.put(Currency.class, new CurrencyConverter());// since 3.0.8
converterMap.put(UUID.class, new UUIDConverter());// since 4.0.10
converterMap.put(StackTraceElement.class, new StackTraceElementConverter());// since 4.5.2
converterMap.put(Optional.class, new OptionalConverter());// since 5.0.0
converterMap.put(Opt.class, new OptConverter());// since 5.7.16
converterMap.put(Pair.class, new PairConverter(rootConverter));// since 6.0.0
converterMap.put(Triple.class, new TripleConverter(rootConverter));// since 6.0.0
converterMap.put(Tuple.class, TupleConverter.INSTANCE);// since 6.0.0
this.defaultConverterMap = defaultConverterMap;
return converterMap;
}
}

View File

@ -17,6 +17,7 @@
package org.dromara.hutool.core.convert;
import org.dromara.hutool.core.convert.impl.*;
import org.dromara.hutool.core.lang.Assert;
import org.dromara.hutool.core.reflect.TypeUtil;
import org.dromara.hutool.core.stream.StreamUtil;
@ -33,28 +34,9 @@ import java.util.Set;
* @author Looly
* @since 6.0.0
*/
public class SpecialConverter implements Converter, Serializable {
public class SpecialConverter extends ConverterWithRoot implements Serializable {
private static final long serialVersionUID = 1L;
/**
* 类级的内部类也就是静态的成员式内部类该内部类的实例与外部类的实例 没有绑定关系而且只有被调用到才会装载从而实现了延迟加载
*/
private static class SingletonHolder {
/**
* 静态初始化器由JVM来保证线程安全
*/
private static final SpecialConverter INSTANCE = new SpecialConverter();
}
/**
* 获得单例的 CustomConverter
*
* @return CustomConverter
*/
public static SpecialConverter getInstance() {
return SpecialConverter.SingletonHolder.INSTANCE;
}
/**
* 类型转换器集合<br>
* 此集合初始化后不再加入新值因此单例使用线程安全
@ -63,42 +45,12 @@ public class SpecialConverter implements Converter, Serializable {
/**
* 构造
*
* @param rootConverter 父转换器
*/
private SpecialConverter() {
final Set<MatcherConverter> converterSet = new LinkedHashSet<>(64);
// 集合转换含有泛型参数不可以默认强转
converterSet.add(CollectionConverter.INSTANCE);
// Map类型含有泛型参数不可以默认强转
converterSet.add(MapConverter.INSTANCE);
// issue#I6SZYB Entry类含有泛型参数不可以默认强转
converterSet.add(EntryConverter.INSTANCE);
// 默认强转
converterSet.add(CastConverter.INSTANCE);
// 日期java.sql中的日期以及自定义日期统一处理
converterSet.add(DateConverter.INSTANCE);
// 原始类型转换
converterSet.add(PrimitiveConverter.INSTANCE);
// 数字类型转换
converterSet.add(NumberConverter.INSTANCE);
// 枚举转换
converterSet.add(EnumConverter.INSTANCE);
// 数组转换
converterSet.add(ArrayConverter.INSTANCE);
// Record
converterSet.add(RecordConverter.INSTANCE);
// Kotlin Bean
converterSet.add(KBeanConverter.INSTANCE);
// issue#I7FQ29 Class
converterSet.add(ClassConverter.INSTANCE);
// // 空值转空Bean
converterSet.add(EmptyBeanConverter.INSTANCE);
// 日期相关
converterSet.add(TimeZoneConverter.INSTANCE);
converterSet.add(ZoneIdConverter.INSTANCE);
this.converterSet = converterSet;
public SpecialConverter(final Converter rootConverter) {
super(rootConverter);
this.converterSet = initDefault(Assert.notNull(rootConverter));
}
@Override
@ -141,4 +93,47 @@ public class SpecialConverter implements Converter, Serializable {
private static Converter getConverterFromSet(final Set<? extends MatcherConverter> converterSet, final Type type, final Class<?> rawType, final Object value) {
return StreamUtil.of(converterSet).filter((predicate) -> predicate.match(type, rawType, value)).findFirst().orElse(null);
}
/**
* 初始化默认转换器
*
* @param rootConverter 根转换器用于递归子对象转换
* @return 转换器集合
*/
private static Set<MatcherConverter> initDefault(final Converter rootConverter) {
final Set<MatcherConverter> converterSet = new LinkedHashSet<>(64);
// 集合转换含有泛型参数不可以默认强转
converterSet.add(CollectionConverter.INSTANCE);
// Map类型含有泛型参数不可以默认强转
converterSet.add(new MapConverter(rootConverter));
// issue#I6SZYB Entry类含有泛型参数不可以默认强转
converterSet.add(new EntryConverter(rootConverter));
// 默认强转
converterSet.add(CastConverter.INSTANCE);
// 日期java.sql中的日期以及自定义日期统一处理
converterSet.add(DateConverter.INSTANCE);
// 原始类型转换
converterSet.add(PrimitiveConverter.INSTANCE);
// 数字类型转换
converterSet.add(NumberConverter.INSTANCE);
// 枚举转换
converterSet.add(EnumConverter.INSTANCE);
// 数组转换
converterSet.add(ArrayConverter.INSTANCE);
// Record
converterSet.add(RecordConverter.INSTANCE);
// Kotlin Bean
converterSet.add(KBeanConverter.INSTANCE);
// issue#I7FQ29 Class
converterSet.add(ClassConverter.INSTANCE);
// // 空值转空Bean
converterSet.add(EmptyBeanConverter.INSTANCE);
// 日期相关
converterSet.add(TimeZoneConverter.INSTANCE);
converterSet.add(ZoneIdConverter.INSTANCE);
return converterSet;
}
}

View File

@ -17,7 +17,7 @@
package org.dromara.hutool.core.convert.impl;
import org.dromara.hutool.core.convert.AbstractConverter;
import org.dromara.hutool.core.convert.CompositeConverter;
import org.dromara.hutool.core.convert.Converter;
import org.dromara.hutool.core.reflect.TypeUtil;
import java.lang.reflect.Type;
@ -32,6 +32,17 @@ import java.util.concurrent.atomic.AtomicReference;
public class AtomicReferenceConverter extends AbstractConverter {
private static final long serialVersionUID = 1L;
private final Converter converter;
/**
* 构造
*
* @param converter 用于转换AtomicReference包装的对象类型
*/
public AtomicReferenceConverter(final Converter converter) {
this.converter = converter;
}
@Override
protected AtomicReference<?> convertInternal(final Class<?> targetClass, final Object value) {
@ -39,7 +50,7 @@ public class AtomicReferenceConverter extends AbstractConverter {
Object targetValue = null;
final Type paramType = TypeUtil.getTypeArgument(AtomicReference.class);
if (!TypeUtil.isUnknown(paramType)) {
targetValue = CompositeConverter.getInstance().convert(paramType, value);
targetValue = converter.convert(paramType, value);
}
if (null == targetValue) {
targetValue = value;

View File

@ -17,8 +17,8 @@
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.convert.MatcherConverter;
import org.dromara.hutool.core.lang.tuple.Pair;
import org.dromara.hutool.core.map.MapUtil;
@ -46,10 +46,16 @@ import java.util.Map;
public class EntryConverter implements MatcherConverter, Serializable {
private static final long serialVersionUID = 1L;
private final Converter convert;
/**
* 单例
* 构造
*
* @param converter 转换器用于将Entry中key和value转换为指定类型的对象
*/
public static final EntryConverter INSTANCE = new EntryConverter();
public EntryConverter(final Converter converter) {
this.convert = converter;
}
@Override
public boolean match(final Type targetType, final Class<?> rawType, final Object value) {
@ -132,7 +138,7 @@ public class EntryConverter implements MatcherConverter, Serializable {
* @return Entry
*/
@SuppressWarnings("rawtypes")
private static Map.Entry<?, ?> mapToEntry(final Type targetType, final Type keyType, final Type valueType, final Map map) {
private Map.Entry<?, ?> mapToEntry(final Type targetType, final Type keyType, final Type valueType, final Map map) {
final Object key;
final Object value;
@ -146,7 +152,6 @@ public class EntryConverter implements MatcherConverter, Serializable {
value = map.get("value");
}
final CompositeConverter convert = CompositeConverter.getInstance();
return (Map.Entry<?, ?>) ConstructorUtil.newInstance(TypeUtil.getClass(targetType),
TypeUtil.isUnknown(keyType) ? key : convert.convert(keyType, key),
TypeUtil.isUnknown(valueType) ? value : convert.convert(valueType, value)

View File

@ -17,9 +17,7 @@
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.MatcherConverter;
import org.dromara.hutool.core.convert.*;
import org.dromara.hutool.core.map.MapUtil;
import org.dromara.hutool.core.reflect.TypeReference;
import org.dromara.hutool.core.reflect.TypeUtil;
@ -40,13 +38,22 @@ import java.util.Objects;
* @author Looly
* @since 3.0.8
*/
public class MapConverter implements MatcherConverter, Serializable {
public class MapConverter extends ConverterWithRoot implements MatcherConverter, Serializable {
private static final long serialVersionUID = 1L;
/**
* 单例
*/
public static MapConverter INSTANCE = new MapConverter();
public static final MapConverter INSTANCE = new MapConverter(CompositeConverter.getInstance());
/**
* 构造
*
* @param rootConverter 根转换器用于转换Map中键值对中的值{@code null}
*/
public MapConverter(final Converter rootConverter) {
super(rootConverter);
}
@Override
public boolean match(final Type targetType, final Class<?> rawType, final Object value) {
@ -111,10 +118,9 @@ public class MapConverter implements MatcherConverter, Serializable {
*/
@SuppressWarnings({"rawtypes", "unchecked"})
private void convertMapToMap(final Type keyType, final Type valueType, final Map<?, ?> srcMap, final Map targetMap) {
final CompositeConverter convert = CompositeConverter.getInstance();
srcMap.forEach((key, value) -> targetMap.put(
TypeUtil.isUnknown(keyType) ? key : convert.convert(keyType, key),
TypeUtil.isUnknown(valueType) ? value : convert.convert(valueType, value)
TypeUtil.isUnknown(keyType) ? key : rootConverter.convert(keyType, key),
TypeUtil.isUnknown(valueType) ? value : rootConverter.convert(valueType, value)
));
}
}

View File

@ -17,9 +17,9 @@
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.convert.ConverterWithRoot;
import org.dromara.hutool.core.lang.tuple.Pair;
import org.dromara.hutool.core.map.MapUtil;
import org.dromara.hutool.core.reflect.TypeReference;
@ -27,6 +27,7 @@ import org.dromara.hutool.core.reflect.TypeUtil;
import org.dromara.hutool.core.text.CharUtil;
import org.dromara.hutool.core.text.StrUtil;
import java.io.Serializable;
import java.lang.reflect.Type;
import java.util.Map;
@ -41,12 +42,17 @@ import java.util.Map;
*
* @author looly
*/
public class PairConverter implements Converter {
public class PairConverter extends ConverterWithRoot implements Serializable {
private static final long serialVersionUID = 1L;
/**
* 单例
* 构造
*
* @param rootConverter 根转换器用于转换Pair中的值
*/
public static final PairConverter INSTANCE = new PairConverter();
public PairConverter(final Converter rootConverter) {
super(rootConverter);
}
@Override
public Object convert(Type targetType, final Object value) throws ConvertException {
@ -122,7 +128,7 @@ public class PairConverter implements Converter {
* @return Pair
*/
@SuppressWarnings("rawtypes")
private static Pair<?, ?> mapToPair(final Type keyType, final Type valueType, final Map map) {
private Pair<?, ?> mapToPair(final Type keyType, final Type valueType, final Map map) {
final Object left;
final Object right;
@ -136,10 +142,9 @@ public class PairConverter implements Converter {
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)
TypeUtil.isUnknown(keyType) ? left : rootConverter.convert(keyType, left),
TypeUtil.isUnknown(valueType) ? right : rootConverter.convert(valueType, right)
);
}
}

View File

@ -17,9 +17,10 @@
package org.dromara.hutool.core.convert.impl;
import org.dromara.hutool.core.convert.AbstractConverter;
import org.dromara.hutool.core.convert.CompositeConverter;
import org.dromara.hutool.core.text.StrUtil;
import org.dromara.hutool.core.convert.Converter;
import org.dromara.hutool.core.lang.Assert;
import org.dromara.hutool.core.reflect.TypeUtil;
import org.dromara.hutool.core.text.StrUtil;
import java.lang.ref.Reference;
import java.lang.ref.SoftReference;
@ -36,10 +37,16 @@ import java.lang.reflect.Type;
public class ReferenceConverter extends AbstractConverter {
private static final long serialVersionUID = 1L;
private final Converter rootConverter;
/**
* 单例
* 构造
*
* @param rootConverter 根转换器用于转换Reference泛型的类型
*/
public static ReferenceConverter INSTANCE = new ReferenceConverter();
public ReferenceConverter(final Converter rootConverter) {
this.rootConverter = Assert.notNull(rootConverter);
}
@SuppressWarnings("unchecked")
@Override
@ -49,7 +56,7 @@ public class ReferenceConverter extends AbstractConverter {
Object targetValue = null;
final Type paramType = TypeUtil.getTypeArgument(targetClass);
if(!TypeUtil.isUnknown(paramType)){
targetValue = CompositeConverter.getInstance().convert(paramType, value);
targetValue = rootConverter.convert(paramType, value);
}
if(null == targetValue){
targetValue = value;

View File

@ -17,13 +17,14 @@
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.convert.ConverterWithRoot;
import org.dromara.hutool.core.lang.tuple.Triple;
import org.dromara.hutool.core.reflect.TypeReference;
import org.dromara.hutool.core.reflect.TypeUtil;
import java.io.Serializable;
import java.lang.reflect.Type;
import java.util.Map;
@ -36,12 +37,17 @@ import java.util.Map;
* @author looly
* @since 6.0.0
*/
public class TripleConverter implements Converter {
public class TripleConverter extends ConverterWithRoot implements Serializable {
private static final long serialVersionUID = 1L;
/**
* 单例
* 构造
*
* @param rootConverter 根转换器用于转换无法被识别的对象
*/
public static final TripleConverter INSTANCE = new TripleConverter();
public TripleConverter(final Converter rootConverter) {
super(rootConverter);
}
@Override
public Object convert(Type targetType, final Object value) throws ConvertException {
@ -92,17 +98,16 @@ public class TripleConverter implements Converter {
* @return Entry
*/
@SuppressWarnings("rawtypes")
private static Triple<?, ?, ?> mapToTriple(final Type leftType, final Type middleType, final Type rightType, final Map map) {
private 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)
TypeUtil.isUnknown(leftType) ? left : rootConverter.convert(leftType, left),
TypeUtil.isUnknown(middleType) ? middle : rootConverter.convert(middleType, middle),
TypeUtil.isUnknown(rightType) ? right : rootConverter.convert(rightType, right)
);
}
}

View File

@ -16,10 +16,10 @@
package org.dromara.hutool.core.lang;
import org.dromara.hutool.core.array.ArrayUtil;
import org.dromara.hutool.core.collection.CollUtil;
import org.dromara.hutool.core.map.MapUtil;
import org.dromara.hutool.core.text.StrUtil;
import org.dromara.hutool.core.array.ArrayUtil;
import org.dromara.hutool.core.util.ObjUtil;
import java.util.Map;
@ -35,6 +35,7 @@ public class Assert {
private static final String TEMPLATE_VALUE_MUST_BE_BETWEEN_AND = "The value must be between {} and {}.";
// region ----- isTrue and isFalse
/**
* 断言是否为真如果为 {@code false} 抛出给定的异常<br>
@ -135,6 +136,9 @@ public class Assert {
public static void isFalse(final boolean expression) throws IllegalArgumentException {
isFalse(expression, "[Assertion failed] - this expression must be false");
}
// endregion
// region ----- isNull or notNull
/**
* 断言对象是否为{@code null} 如果不为{@code null} 抛出指定类型异常
@ -186,8 +190,6 @@ public class Assert {
isNull(object, "[Assertion failed] - the object argument must be null");
}
// ----------------------------------------------------------------------------------------------------------- Check not null
/**
* 断言对象是否不为{@code null} 如果为{@code null} 抛出指定类型异常
* 并使用指定的函数获取错误信息返回
@ -214,10 +216,10 @@ public class Assert {
}
/**
* 断言对象是否不为{@code null} 如果为{@code null} 抛出{@link IllegalArgumentException} 异常 Assert that an object is not {@code null} .
* <pre class="code">
* 断言对象是否不为{@code null} 如果为{@code null} 抛出{@link IllegalArgumentException}
* <pre>{@code
* Assert.notNull(clazz, "The class must not be null");
* </pre>
* }</pre>
*
* @param <T> 被检查对象泛型类型
* @param object 被检查对象
@ -227,14 +229,17 @@ public class Assert {
* @throws IllegalArgumentException if the object is {@code null}
*/
public static <T> T notNull(final T object, final String errorMsgTemplate, final Object... params) throws IllegalArgumentException {
return notNull(object, () -> new IllegalArgumentException(StrUtil.format(errorMsgTemplate, params)));
if (null == object) {
throw new IllegalArgumentException(StrUtil.format(errorMsgTemplate, params));
}
return object;
}
/**
* 断言对象是否不为{@code null} 如果为{@code null} 抛出{@link IllegalArgumentException} 异常
* <pre class="code">
* <pre>{@code
* Assert.notNull(clazz);
* </pre>
* }</pre>
*
* @param <T> 被检查对象类型
* @param object 被检查对象
@ -242,193 +247,14 @@ public class Assert {
* @throws IllegalArgumentException if the object is {@code null}
*/
public static <T> T notNull(final T object) throws IllegalArgumentException {
return notNull(object, "[Assertion failed] - this argument is required; it must not be null");
if (null == object) {
throw new IllegalArgumentException("[Assertion failed] - this argument is required; it must not be null");
}
return object;
}
// endregion
// ----------------------------------------------------------------------------------------------------------- Check empty
/**
* 检查给定字符串是否为空为空抛出自定义异常并使用指定的函数获取错误信息返回
* <pre class="code">
* Assert.notEmpty(name, ()-&gt;{
* // to query relation message
* return new IllegalArgumentException("relation message to return");
* });
* </pre>
*
* @param <X> 异常类型
* @param <T> 字符串类型
* @param text 被检查字符串
* @param errorSupplier 错误抛出异常附带的消息生产接口
* @return 非空字符串
* @throws X 被检查字符串为空抛出此异常
* @see StrUtil#isNotEmpty(CharSequence)
* @since 5.4.5
*/
public static <T extends CharSequence, X extends Throwable> T notEmpty(final T text, final Supplier<X> errorSupplier) throws X {
if (StrUtil.isEmpty(text)) {
throw errorSupplier.get();
}
return text;
}
/**
* 检查给定字符串是否为空为空抛出 {@link IllegalArgumentException}
*
* <pre class="code">
* Assert.notEmpty(name, "Name must not be empty");
* </pre>
*
* @param <T> 字符串类型
* @param text 被检查字符串
* @param errorMsgTemplate 错误消息模板变量使用{}表示
* @param params 参数
* @return 非空字符串
* @throws IllegalArgumentException 被检查字符串为空
* @see StrUtil#isNotEmpty(CharSequence)
*/
public static <T extends CharSequence> T notEmpty(final T text, final String errorMsgTemplate, final Object... params) throws IllegalArgumentException {
return notEmpty(text, () -> new IllegalArgumentException(StrUtil.format(errorMsgTemplate, params)));
}
/**
* 检查给定字符串是否为空为空抛出 {@link IllegalArgumentException}
*
* <pre class="code">
* Assert.notEmpty(name);
* </pre>
*
* @param <T> 字符串类型
* @param text 被检查字符串
* @return 被检查的字符串
* @throws IllegalArgumentException 被检查字符串为空
* @see StrUtil#isNotEmpty(CharSequence)
*/
public static <T extends CharSequence> T notEmpty(final T text) throws IllegalArgumentException {
return notEmpty(text, "[Assertion failed] - this String argument must have length; it must not be null or empty");
}
/**
* 检查给定字符串是否为空白null空串或只包含空白符为空抛出自定义异常
* 并使用指定的函数获取错误信息返回
* <pre class="code">
* Assert.notBlank(name, ()-&gt;{
* // to query relation message
* return new IllegalArgumentException("relation message to return");
* });
* </pre>
*
* @param <X> 异常类型
* @param <T> 字符串类型
* @param text 被检查字符串
* @param errorMsgSupplier 错误抛出异常附带的消息生产接口
* @return 非空字符串
* @throws X 被检查字符串为空白
* @see StrUtil#isNotBlank(CharSequence)
*/
public static <T extends CharSequence, X extends Throwable> T notBlank(final T text, final Supplier<X> errorMsgSupplier) throws X {
if (StrUtil.isBlank(text)) {
throw errorMsgSupplier.get();
}
return text;
}
/**
* 检查给定字符串是否为空白null空串或只包含空白符为空抛出 {@link IllegalArgumentException}
*
* <pre class="code">
* Assert.notBlank(name, "Name must not be blank");
* </pre>
*
* @param <T> 字符串类型
* @param text 被检查字符串
* @param errorMsgTemplate 错误消息模板变量使用{}表示
* @param params 参数
* @return 非空字符串
* @throws IllegalArgumentException 被检查字符串为空白
* @see StrUtil#isNotBlank(CharSequence)
*/
public static <T extends CharSequence> T notBlank(final T text, final String errorMsgTemplate, final Object... params) throws IllegalArgumentException {
return notBlank(text, () -> new IllegalArgumentException(StrUtil.format(errorMsgTemplate, params)));
}
/**
* 检查给定字符串是否为空白null空串或只包含空白符为空抛出 {@link IllegalArgumentException}
*
* <pre class="code">
* Assert.notBlank(name);
* </pre>
*
* @param <T> 字符串类型
* @param text 被检查字符串
* @return 非空字符串
* @throws IllegalArgumentException 被检查字符串为空白
* @see StrUtil#isNotBlank(CharSequence)
*/
public static <T extends CharSequence> T notBlank(final T text) throws IllegalArgumentException {
return notBlank(text, "[Assertion failed] - this String argument must have text; it must not be null, empty, or blank");
}
/**
* 断言给定字符串是否不被另一个字符串包含即是否为子串并使用指定的函数获取错误信息返回<br>
* 如果非子串返回子串如果是子串则抛出{@link IllegalArgumentException}异常
* <pre class="code">
* Assert.notContain(name, "rod", ()-&gt;{
* // to query relation message
* return new IllegalArgumentException("relation message to return ");
* });
* </pre>
*
* @param <T> 字符串类型
* @param <X> 异常类型
* @param textToSearch 被搜索的字符串
* @param substring 被检查的子串
* @param errorSupplier 错误抛出异常附带的消息生产接口
* @return 被检查的子串
* @throws X 非子串抛出异常
* @see StrUtil#contains(CharSequence, CharSequence)
* @since 5.4.5
*/
public static <T extends CharSequence, X extends Throwable> T notContain(final CharSequence textToSearch, final T substring, final Supplier<X> errorSupplier) throws X {
if (StrUtil.contains(textToSearch, substring)) {
throw errorSupplier.get();
}
return substring;
}
/**
* 断言给定字符串是否不被另一个字符串包含即是否为子串<br>
* 如果非子串返回子串如果是子串则抛出{@link IllegalArgumentException}异常
* <pre class="code">
* Assert.notContain(name, "rod", "Name must not contain 'rod'");
* </pre>
*
* @param textToSearch 被搜索的字符串
* @param subString 被检查的子串
* @param errorMsgTemplate 异常时的消息模板
* @param params 参数列表
* @return 被检查的子串
* @throws IllegalArgumentException 非子串抛出异常
*/
public static String notContain(final String textToSearch, final String subString, final String errorMsgTemplate, final Object... params) throws IllegalArgumentException {
return notContain(textToSearch, subString, () -> new IllegalArgumentException(StrUtil.format(errorMsgTemplate, params)));
}
/**
* 断言给定字符串是否不被另一个字符串包含即是否为子串即subString是否不是textToSearch的子串<br>
* 如果非子串返回子串如果是子串则抛出{@link IllegalArgumentException}异常
* <pre class="code">
* Assert.notContain(name, "rod");
* </pre>
*
* @param textToSearch 被搜索的字符串
* @param subString 被检查的子串
* @return 被检查的子串
* @throws IllegalArgumentException 非子串抛出异常
*/
public static String notContain(final String textToSearch, final String subString) throws IllegalArgumentException {
return notContain(textToSearch, subString, "[Assertion failed] - this String argument must not contain the substring [{}]", subString);
}
// region ----- notEmpty and notBlank
/**
* 断言给定数组是否包含元素数组必须不为 {@code null} 且至少包含一个元素
@ -488,64 +314,6 @@ public class Assert {
return notEmpty(array, "[Assertion failed] - this array must not be empty: it must contain at least 1 element");
}
/**
* 断言给定数组是否不包含{@code null}元素如果数组为空或 {@code null}将被认为不包含
* 并使用指定的函数获取错误信息返回
* <pre class="code">
* Assert.noNullElements(array, ()-&gt;{
* // to query relation message
* return new IllegalArgumentException("relation message to return ");
* });
* </pre>
*
* @param <T> 数组元素类型
* @param <X> 异常类型
* @param array 被检查的数组
* @param errorSupplier 错误抛出异常附带的消息生产接口
* @return 被检查的数组
* @throws X if the object array contains a {@code null} element
* @see ArrayUtil#hasNull(Object[])
* @since 5.4.5
*/
public static <T, X extends Throwable> T[] noNullElements(final T[] array, final Supplier<X> errorSupplier) throws X {
if (ArrayUtil.hasNull(array)) {
throw errorSupplier.get();
}
return array;
}
/**
* 断言给定数组是否不包含{@code null}元素如果数组为空或 {@code null}将被认为不包含
* <pre class="code">
* Assert.noNullElements(array, "The array must not have null elements");
* </pre>
*
* @param <T> 数组元素类型
* @param array 被检查的数组
* @param errorMsgTemplate 异常时的消息模板
* @param params 参数列表
* @return 被检查的数组
* @throws IllegalArgumentException if the object array contains a {@code null} element
*/
public static <T> T[] noNullElements(final T[] array, final String errorMsgTemplate, final Object... params) throws IllegalArgumentException {
return noNullElements(array, () -> new IllegalArgumentException(StrUtil.format(errorMsgTemplate, params)));
}
/**
* 断言给定数组是否不包含{@code null}元素如果数组为空或 {@code null}将被认为不包含
* <pre class="code">
* Assert.noNullElements(array);
* </pre>
*
* @param <T> 数组元素类型
* @param array 被检查的数组
* @return 被检查的数组
* @throws IllegalArgumentException if the object array contains a {@code null} element
*/
public static <T> T[] noNullElements(final T[] array) throws IllegalArgumentException {
return noNullElements(array, "[Assertion failed] - this array must not contain any null elements");
}
/**
* 断言给定集合非空
* 并使用指定的函数获取错误信息返回
@ -671,6 +439,255 @@ public class Assert {
return notEmpty(map, "[Assertion failed] - this map must not be empty; it must contain at least one entry");
}
/**
* 检查给定字符串是否为空为空抛出自定义异常并使用指定的函数获取错误信息返回
* <pre class="code">
* Assert.notEmpty(name, ()-&gt;{
* // to query relation message
* return new IllegalArgumentException("relation message to return");
* });
* </pre>
*
* @param <X> 异常类型
* @param <T> 字符串类型
* @param text 被检查字符串
* @param errorSupplier 错误抛出异常附带的消息生产接口
* @return 非空字符串
* @throws X 被检查字符串为空抛出此异常
* @see StrUtil#isNotEmpty(CharSequence)
* @since 5.4.5
*/
public static <T extends CharSequence, X extends Throwable> T notEmpty(final T text, final Supplier<X> errorSupplier) throws X {
if (StrUtil.isEmpty(text)) {
throw errorSupplier.get();
}
return text;
}
/**
* 检查给定字符串是否为空为空抛出 {@link IllegalArgumentException}
*
* <pre class="code">
* Assert.notEmpty(name, "Name must not be empty");
* </pre>
*
* @param <T> 字符串类型
* @param text 被检查字符串
* @param errorMsgTemplate 错误消息模板变量使用{}表示
* @param params 参数
* @return 非空字符串
* @throws IllegalArgumentException 被检查字符串为空
* @see StrUtil#isNotEmpty(CharSequence)
*/
public static <T extends CharSequence> T notEmpty(final T text, final String errorMsgTemplate, final Object... params) throws IllegalArgumentException {
return notEmpty(text, () -> new IllegalArgumentException(StrUtil.format(errorMsgTemplate, params)));
}
/**
* 检查给定字符串是否为空为空抛出 {@link IllegalArgumentException}
*
* <pre class="code">
* Assert.notEmpty(name);
* </pre>
*
* @param <T> 字符串类型
* @param text 被检查字符串
* @return 被检查的字符串
* @throws IllegalArgumentException 被检查字符串为空
* @see StrUtil#isNotEmpty(CharSequence)
*/
public static <T extends CharSequence> T notEmpty(final T text) throws IllegalArgumentException {
return notEmpty(text, "[Assertion failed] - this String argument must have length; it must not be null or empty");
}
/**
* 检查给定字符串是否为空白null空串或只包含空白符为空抛出自定义异常
* 并使用指定的函数获取错误信息返回
* <pre class="code">
* Assert.notBlank(name, ()-&gt;{
* // to query relation message
* return new IllegalArgumentException("relation message to return");
* });
* </pre>
*
* @param <X> 异常类型
* @param <T> 字符串类型
* @param text 被检查字符串
* @param errorMsgSupplier 错误抛出异常附带的消息生产接口
* @return 非空字符串
* @throws X 被检查字符串为空白
* @see StrUtil#isNotBlank(CharSequence)
*/
public static <T extends CharSequence, X extends Throwable> T notBlank(final T text, final Supplier<X> errorMsgSupplier) throws X {
if (StrUtil.isBlank(text)) {
throw errorMsgSupplier.get();
}
return text;
}
/**
* 检查给定字符串是否为空白null空串或只包含空白符为空抛出 {@link IllegalArgumentException}
*
* <pre class="code">
* Assert.notBlank(name, "Name must not be blank");
* </pre>
*
* @param <T> 字符串类型
* @param text 被检查字符串
* @param errorMsgTemplate 错误消息模板变量使用{}表示
* @param params 参数
* @return 非空字符串
* @throws IllegalArgumentException 被检查字符串为空白
* @see StrUtil#isNotBlank(CharSequence)
*/
public static <T extends CharSequence> T notBlank(final T text, final String errorMsgTemplate, final Object... params) throws IllegalArgumentException {
return notBlank(text, () -> new IllegalArgumentException(StrUtil.format(errorMsgTemplate, params)));
}
/**
* 检查给定字符串是否为空白null空串或只包含空白符为空抛出 {@link IllegalArgumentException}
*
* <pre class="code">
* Assert.notBlank(name);
* </pre>
*
* @param <T> 字符串类型
* @param text 被检查字符串
* @return 非空字符串
* @throws IllegalArgumentException 被检查字符串为空白
* @see StrUtil#isNotBlank(CharSequence)
*/
public static <T extends CharSequence> T notBlank(final T text) throws IllegalArgumentException {
return notBlank(text, "[Assertion failed] - this String argument must have text; it must not be null, empty, or blank");
}
// endregion
// region ----- notContain
/**
* 断言给定字符串是否不被另一个字符串包含即是否为子串并使用指定的函数获取错误信息返回<br>
* 如果非子串返回子串如果是子串则抛出{@link IllegalArgumentException}异常
* <pre class="code">
* Assert.notContain(name, "rod", ()-&gt;{
* // to query relation message
* return new IllegalArgumentException("relation message to return ");
* });
* </pre>
*
* @param <T> 字符串类型
* @param <X> 异常类型
* @param textToSearch 被搜索的字符串
* @param substring 被检查的子串
* @param errorSupplier 错误抛出异常附带的消息生产接口
* @return 被检查的子串
* @throws X 非子串抛出异常
* @see StrUtil#contains(CharSequence, CharSequence)
* @since 5.4.5
*/
public static <T extends CharSequence, X extends Throwable> T notContain(final CharSequence textToSearch, final T substring, final Supplier<X> errorSupplier) throws X {
if (StrUtil.contains(textToSearch, substring)) {
throw errorSupplier.get();
}
return substring;
}
/**
* 断言给定字符串是否不被另一个字符串包含即是否为子串<br>
* 如果非子串返回子串如果是子串则抛出{@link IllegalArgumentException}异常
* <pre class="code">
* Assert.notContain(name, "rod", "Name must not contain 'rod'");
* </pre>
*
* @param textToSearch 被搜索的字符串
* @param subString 被检查的子串
* @param errorMsgTemplate 异常时的消息模板
* @param params 参数列表
* @return 被检查的子串
* @throws IllegalArgumentException 非子串抛出异常
*/
public static String notContain(final String textToSearch, final String subString, final String errorMsgTemplate, final Object... params) throws IllegalArgumentException {
return notContain(textToSearch, subString, () -> new IllegalArgumentException(StrUtil.format(errorMsgTemplate, params)));
}
/**
* 断言给定字符串是否不被另一个字符串包含即是否为子串即subString是否不是textToSearch的子串<br>
* 如果非子串返回子串如果是子串则抛出{@link IllegalArgumentException}异常
* <pre class="code">
* Assert.notContain(name, "rod");
* </pre>
*
* @param textToSearch 被搜索的字符串
* @param subString 被检查的子串
* @return 被检查的子串
* @throws IllegalArgumentException 非子串抛出异常
*/
public static String notContain(final String textToSearch, final String subString) throws IllegalArgumentException {
return notContain(textToSearch, subString, "[Assertion failed] - this String argument must not contain the substring [{}]", subString);
}
// endregion
// region ----- noNullElements
/**
* 断言给定数组是否不包含{@code null}元素如果数组为空或 {@code null}将被认为不包含
* 并使用指定的函数获取错误信息返回
* <pre class="code">
* Assert.noNullElements(array, ()-&gt;{
* // to query relation message
* return new IllegalArgumentException("relation message to return ");
* });
* </pre>
*
* @param <T> 数组元素类型
* @param <X> 异常类型
* @param array 被检查的数组
* @param errorSupplier 错误抛出异常附带的消息生产接口
* @return 被检查的数组
* @throws X if the object array contains a {@code null} element
* @see ArrayUtil#hasNull(Object[])
* @since 5.4.5
*/
public static <T, X extends Throwable> T[] noNullElements(final T[] array, final Supplier<X> errorSupplier) throws X {
if (ArrayUtil.hasNull(array)) {
throw errorSupplier.get();
}
return array;
}
/**
* 断言给定数组是否不包含{@code null}元素如果数组为空或 {@code null}将被认为不包含
* <pre class="code">
* Assert.noNullElements(array, "The array must not have null elements");
* </pre>
*
* @param <T> 数组元素类型
* @param array 被检查的数组
* @param errorMsgTemplate 异常时的消息模板
* @param params 参数列表
* @return 被检查的数组
* @throws IllegalArgumentException if the object array contains a {@code null} element
*/
public static <T> T[] noNullElements(final T[] array, final String errorMsgTemplate, final Object... params) throws IllegalArgumentException {
return noNullElements(array, () -> new IllegalArgumentException(StrUtil.format(errorMsgTemplate, params)));
}
/**
* 断言给定数组是否不包含{@code null}元素如果数组为空或 {@code null}将被认为不包含
* <pre class="code">
* Assert.noNullElements(array);
* </pre>
*
* @param <T> 数组元素类型
* @param array 被检查的数组
* @return 被检查的数组
* @throws IllegalArgumentException if the object array contains a {@code null} element
*/
public static <T> T[] noNullElements(final T[] array) throws IllegalArgumentException {
return noNullElements(array, "[Assertion failed] - this array must not contain any null elements");
}
// endregion
// region ----- isInstanceOf and isAssignable
/**
* 断言给定对象是否是给定类的实例
* <pre class="code">
@ -743,6 +760,9 @@ public class Assert {
throw new IllegalArgumentException(StrUtil.format(errorMsgTemplate, params));
}
}
// endregion
// region ----- state
/**
* 检查boolean表达式当检查结果为false时抛出 {@code IllegalStateException}
@ -793,7 +813,9 @@ public class Assert {
public static void state(final boolean expression) throws IllegalStateException {
state(expression, "[Assertion failed] - this state invariant must be true");
}
// endregion
// region ----- checkIndex
/**
* 检查下标数组集合字符串是否符合要求下标必须满足
*
@ -834,6 +856,9 @@ public class Assert {
}
return index;
}
// endregion
// region ----- checkBetween
/**
* 检查值是否在指定范围内
@ -1001,6 +1026,10 @@ public class Assert {
return value;
}
// endregion
// region ----- equals and notEquals
/**
* 断言两个对象是否不相等,如果两个对象相等 抛出IllegalArgumentException 异常
* <pre class="code">
@ -1045,7 +1074,6 @@ public class Assert {
throw errorSupplier.get();
}
}
// ----------------------------------------------------------------------------------------------------------- Check not equals
/**
* 断言两个对象是否相等,如果两个对象不相等 抛出IllegalArgumentException 异常
@ -1091,8 +1119,7 @@ public class Assert {
throw errorSupplier.get();
}
}
// ----------------------------------------------------------------------------------------------------------- Check is equals
// endregion
// -------------------------------------------------------------------------------------------------------------------------------------------- Private method start

View File

@ -40,12 +40,6 @@ public class CompositeConverterTest {
@Data
public static class EmptyBean {}
@Test
public void getConverterTest() {
final Converter converter = CompositeConverter.getInstance().getConverter(CharSequence.class, null, false);
assertNotNull(converter);
}
@Test
public void customTest() {
final int a = 454553;

View File

@ -74,8 +74,6 @@ public class JdkHttpConnection implements HeaderOperation<JdkHttpConnection>, Cl
return new JdkHttpConnection(url, proxy);
}
// region --------------------------------------------------------------- Constructor
/**
* 构造HttpConnection
*
@ -92,9 +90,7 @@ public class JdkHttpConnection implements HeaderOperation<JdkHttpConnection>, Cl
this.conn.setDoInput(true);
}
// endregion --------------------------------------------------------------- Constructor
// region --------------------------------------------------------------- Getters And Setters
// region ----- Getters And Setters
/**
* 获取请求方法,GET/POST
@ -279,9 +275,9 @@ public class JdkHttpConnection implements HeaderOperation<JdkHttpConnection>, Cl
return this;
}
// endregion --------------------------------------------------------------- Getters And Setters
// endregion ----- Getters And Setters
// region ---------------------------------------------------------------- Headers
// region ------ Headers
/**
* 设置请求头<br>
@ -327,7 +323,7 @@ public class JdkHttpConnection implements HeaderOperation<JdkHttpConnection>, Cl
return this.conn.getHeaderFields();
}
// endregion---------------------------------------------------------------- Headers
// endregion------ Headers
/**
* 连接

View File

@ -29,6 +29,7 @@ import org.dromara.hutool.core.reflect.TypeReference;
import org.dromara.hutool.core.reflect.TypeUtil;
import org.dromara.hutool.core.reflect.kotlin.KClassUtil;
import org.dromara.hutool.core.text.StrUtil;
import org.dromara.hutool.core.util.ObjUtil;
import org.dromara.hutool.json.*;
import org.dromara.hutool.json.reader.JSONParser;
import org.dromara.hutool.json.reader.JSONTokener;
@ -59,13 +60,6 @@ public class JSONConverter implements Converter, Serializable {
*/
public static final JSONConverter INSTANCE = new JSONConverter(null);
static {
final RegisterConverter converter = RegisterConverter.getInstance();
converter.register(JSONObject.class, INSTANCE);
converter.register(JSONArray.class, INSTANCE);
converter.register(JSONPrimitive.class, INSTANCE);
}
/**
* 创建JSON转换器
*
@ -73,17 +67,25 @@ public class JSONConverter implements Converter, Serializable {
* @return JSONConverter
*/
public static JSONConverter of(final JSONConfig config) {
return new JSONConverter(config);
final JSONConverter jsonConverter = new JSONConverter(config);
jsonConverter.registerConverter = new RegisterConverter(jsonConverter)
.register(JSONObject.class, INSTANCE)
.register(JSONArray.class, INSTANCE)
.register(JSONPrimitive.class, INSTANCE);
jsonConverter.specialConverter = new SpecialConverter(jsonConverter);
return jsonConverter;
}
private final JSONConfig config;
private RegisterConverter registerConverter;
private SpecialConverter specialConverter;
/**
* 构造
*
* @param config JSON配置
*/
public JSONConverter(final JSONConfig config) {
private JSONConverter(final JSONConfig config) {
this.config = config;
}
@ -248,27 +250,35 @@ public class JSONConverter implements Converter, Serializable {
value = json;
}
final JSONConfig config = ObjUtil.defaultIfNull(json.config(), JSONConfig::of);
final boolean ignoreError = config.isIgnoreError();
try {
// 标准转换器
final Converter converter = RegisterConverter.getInstance().getConverter(targetType, value, true);
final Converter converter = registerConverter.getConverter(targetType, value, true);
if (null != converter) {
return (T) converter.convert(targetType, value);
}
// 特殊类型转换包括CollectionMap强转Array等
final T result = (T) SpecialConverter.getInstance().convert(targetType, rawType, value);
final T result = (T) specialConverter.convert(targetType, rawType, value);
if (null != result) {
return result;
}
} catch (final ConvertException e) {
if (ignoreError) {
return null;
}
}
// 尝试转Bean
if (BeanUtil.isWritableBean(rawType)) {
return BeanCopier.of(value,
ConstructorUtil.newInstanceIfPossible(rawType), targetType,
InternalJSONUtil.toCopyOptions(json.config())).copy();
InternalJSONUtil.toCopyOptions(config)).copy();
}
// 跳过异常时返回null
if (json.config().isIgnoreError()) {
if (ignoreError) {
return null;
}

View File

@ -16,28 +16,41 @@
package org.dromara.hutool.json;
import lombok.Data;
import org.dromara.hutool.core.io.resource.ResourceUtil;
import org.dromara.hutool.core.lang.Console;
import org.dromara.hutool.json.test.bean.ResultBean;
import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
import java.util.List;
/**
* 测试在bean转换时使用BeanConverter默认忽略转换失败的字段
* 现阶段Converter的问题在于无法更细粒度的控制转换失败的范围例如Bean的一个字段为List
* list任意一个item转换失败都会导致这个list为null
* <p>
* TODO 需要在Converter中添加ConvertOption用于更细粒度的控制转换规则
* 转换失败则在设置setIgnoreError(true)不报错
*/
public class Issue1200Test {
@Test
@Disabled
public void toBeanTest() {
final JSONObject jsonObject = JSONUtil.parseObj(ResourceUtil.readUtf8Str("issue1200.json"));
Console.log(jsonObject);
final JSONObject jsonObject = JSONUtil.parseObj(
ResourceUtil.readUtf8Str("issue1200.json"),
JSONConfig.of().setIgnoreError(true));
final ResultBean resultBean = jsonObject.toBean(ResultBean.class);
Console.log(resultBean);
Assertions.assertNull(resultBean.getItems().get(0).get(0));
}
@Data
static
class ResultBean {
public List<List<List<ResultBean.ItemsBean>>> items;
@Data
public static class ItemsBean {
public ResultBean.ItemsBean.DetailBean detail;
@Data
public static class DetailBean {
public String visitorStatus;
}
}
}
}

View File

@ -193,7 +193,7 @@ public class JSONArrayTest {
@Test
public void toListWithErrorTest() {
Assertions.assertThrows(ConvertException.class, ()->{
Assertions.assertThrows(JSONException.class, ()->{
final String json = "[['aaa',{'akey':'avalue','bkey':'bvalue'}]]";
final JSONArray ja = JSONUtil.parseArray(json);

View File

@ -1,36 +0,0 @@
/*
* Copyright (c) 2024 Hutool Team and hutool.cn
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.dromara.hutool.json.test.bean;
import lombok.Data;
import java.util.List;
@Data
public class ResultBean {
public List<List<List<ItemsBean>>> items;
@Data
public static class ItemsBean {
public DetailBean detail;
@Data
public static class DetailBean {
public String visitorStatus;
}
}
}