diff --git a/hutool-core/src/main/java/org/dromara/hutool/core/collection/CollUtil.java b/hutool-core/src/main/java/org/dromara/hutool/core/collection/CollUtil.java index 64d04c0ba..03fca5bf4 100644 --- a/hutool-core/src/main/java/org/dromara/hutool/core/collection/CollUtil.java +++ b/hutool-core/src/main/java/org/dromara/hutool/core/collection/CollUtil.java @@ -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; @@ -401,7 +402,7 @@ public class CollUtil { * @return 单差集 */ public static Collection subtract(final Collection coll1, final Collection coll2) { - if(isEmpty(coll1) || isEmpty(coll2)){ + if (isEmpty(coll1) || isEmpty(coll2)) { return coll1; } @@ -530,7 +531,7 @@ public class CollUtil { } // Set直接判定 - if(coll1 instanceof Set){ + if (coll1 instanceof Set) { return coll1.containsAll(coll2); } @@ -1571,8 +1572,24 @@ public class CollUtil { * @param elementType 元素类型,为空时,使用Object类型来接纳所有类型 * @return 被加入集合 */ + public static Collection addAll(final Collection collection, final Object value, final Type elementType) { + return addAll(collection, value, elementType, null); + } + + /** + * 将指定对象全部加入到集合中
+ * 提供的对象如果为集合类型,会自动转换为目标元素类型
+ * 如果为String,支持类似于[1,2,3,4] 或者 1,2,3,4 这种格式 + * + * @param 元素类型 + * @param collection 被加入的集合 + * @param value 对象,可能为Iterator、Iterable、Enumeration、Array,或者与集合元素类型一致 + * @param elementType 元素类型,为空时,使用Object类型来接纳所有类型 + * @param converter 自定义元素类型转换器,{@code null}表示使用默认转换器 + * @return 被加入集合 + */ @SuppressWarnings({"unchecked", "rawtypes"}) - public static Collection addAll(final Collection collection, final Object value, Type elementType) { + public static Collection addAll(final Collection 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())); } @@ -2316,12 +2333,12 @@ public class CollUtil { * * * @param subCollection 第一个Iterable对象,即子集合。 - * @param collection 第二个Iterable对象,可以为任何实现了Iterable接口的集合。 + * @param collection 第二个Iterable对象,可以为任何实现了Iterable接口的集合。 * @return 如果subCollection是collection的子集合,则返回true;否则返回false。 * @since 6.0.0 */ public static boolean isSub(final Collection subCollection, final Collection collection) { - if(size(subCollection) > size(collection)){ + if (size(subCollection) > size(collection)) { return false; } return IterUtil.isSub(subCollection, collection); @@ -2354,13 +2371,13 @@ public class CollUtil { *
  • 如果不忽略顺序,两个{@link Iterable}所有具有相同下标的元素皆满足{@link Objects#equals(Object, Object)};
  • * * - * @param coll1 集合1 - * @param coll2 集合2 + * @param coll1 集合1 + * @param coll2 集合2 * @param ignoreOrder 是否忽略顺序 * @return 是否相同 */ public static boolean equals(final Collection coll1, final Collection coll2, final boolean ignoreOrder) { - if(size(coll1) != size(coll2)){ + if (size(coll1) != size(coll2)) { return false; } diff --git a/hutool-core/src/main/java/org/dromara/hutool/core/convert/CompositeConverter.java b/hutool-core/src/main/java/org/dromara/hutool/core/convert/CompositeConverter.java index f37a18ab0..270d39cfd 100644 --- a/hutool-core/src/main/java/org/dromara/hutool/core/convert/CompositeConverter.java +++ b/hutool-core/src/main/java/org/dromara/hutool/core/convert/CompositeConverter.java @@ -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)}则使用其转换器
    + * 注意:如果单例使用,此方法会影响全局 + * + * @param converter 转换器 + * @return this + */ + public CompositeConverter register(final MatcherConverter converter) { + registerConverter.register(converter); + return this; + } + + /** + * 登记自定义转换器,登记的目标类型必须一致
    + * 注意:如果单例使用,此方法会影响全局 + * + * @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 { } // 特殊类型转换,包括Collection、Map、强转、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; } diff --git a/hutool-core/src/main/java/org/dromara/hutool/core/convert/ConverterWithRoot.java b/hutool-core/src/main/java/org/dromara/hutool/core/convert/ConverterWithRoot.java new file mode 100644 index 000000000..cccb05dec --- /dev/null +++ b/hutool-core/src/main/java/org/dromara/hutool/core/convert/ConverterWithRoot.java @@ -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; + +/** + * 带根的转换器
    + * 在嵌套对象转换中,如果涉及子对象的转换,使用根转换器转换 + * + * @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; + } +} diff --git a/hutool-core/src/main/java/org/dromara/hutool/core/convert/RegisterConverter.java b/hutool-core/src/main/java/org/dromara/hutool/core/convert/RegisterConverter.java index 7a0b03aa3..e614606d2 100644 --- a/hutool-core/src/main/java/org/dromara/hutool/core/convert/RegisterConverter.java +++ b/hutool-core/src/main/java/org/dromara/hutool/core/convert/RegisterConverter.java @@ -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, Converter> defaultConverterMap; + private final Map, Converter> defaultConverterMap; /** * 构造 + * + * @param rootConverter 根转换器,用于子转换器转换 */ - public RegisterConverter() { - registerDefault(); + public RegisterConverter(final Converter rootConverter) { + super(rootConverter); + this.defaultConverterMap = initDefault(rootConverter); } @Override @@ -121,7 +105,7 @@ public class RegisterConverter implements Converter, Serializable { Converter converter; if (isCustomFirst) { converter = this.getCustomConverter(type, value); - if(null == converter){ + if (null == converter) { converter = this.getCustomConverter(type); } if (null == converter) { @@ -132,7 +116,7 @@ public class RegisterConverter implements Converter, Serializable { if (null == converter) { converter = this.getCustomConverter(type, value); } - if(null == converter){ + if (null == converter) { converter = this.getCustomConverter(type); } } @@ -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, Converter> defaultConverterMap = new SafeConcurrentHashMap<>(64); + private static Map, Converter> initDefault(final Converter rootConverter) { + final Map, 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 //AtomicXXXArray,since 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; } } diff --git a/hutool-core/src/main/java/org/dromara/hutool/core/convert/SpecialConverter.java b/hutool-core/src/main/java/org/dromara/hutool/core/convert/SpecialConverter.java index 91f242761..285cd8625 100644 --- a/hutool-core/src/main/java/org/dromara/hutool/core/convert/SpecialConverter.java +++ b/hutool-core/src/main/java/org/dromara/hutool/core/convert/SpecialConverter.java @@ -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; - } - /** * 类型转换器集合
    * 此集合初始化后不再加入新值,因此单例使用线程安全 @@ -63,42 +45,12 @@ public class SpecialConverter implements Converter, Serializable { /** * 构造 + * + * @param rootConverter 父转换器 */ - private SpecialConverter() { - final Set 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 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 initDefault(final Converter rootConverter) { + final Set 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; + } } diff --git a/hutool-core/src/main/java/org/dromara/hutool/core/convert/impl/AtomicReferenceConverter.java b/hutool-core/src/main/java/org/dromara/hutool/core/convert/impl/AtomicReferenceConverter.java index 4a588793a..7cfa2acac 100644 --- a/hutool-core/src/main/java/org/dromara/hutool/core/convert/impl/AtomicReferenceConverter.java +++ b/hutool-core/src/main/java/org/dromara/hutool/core/convert/impl/AtomicReferenceConverter.java @@ -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,16 +32,27 @@ 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) { //尝试将值转换为Reference泛型的类型 Object targetValue = null; final Type paramType = TypeUtil.getTypeArgument(AtomicReference.class); - if(!TypeUtil.isUnknown(paramType)){ - targetValue = CompositeConverter.getInstance().convert(paramType, value); + if (!TypeUtil.isUnknown(paramType)) { + targetValue = converter.convert(paramType, value); } - if(null == targetValue){ + if (null == targetValue) { targetValue = value; } diff --git a/hutool-core/src/main/java/org/dromara/hutool/core/convert/impl/EntryConverter.java b/hutool-core/src/main/java/org/dromara/hutool/core/convert/impl/EntryConverter.java index 6e78584f0..ac058a5a0 100644 --- a/hutool-core/src/main/java/org/dromara/hutool/core/convert/impl/EntryConverter.java +++ b/hutool-core/src/main/java/org/dromara/hutool/core/convert/impl/EntryConverter.java @@ -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) diff --git a/hutool-core/src/main/java/org/dromara/hutool/core/convert/impl/MapConverter.java b/hutool-core/src/main/java/org/dromara/hutool/core/convert/impl/MapConverter.java index dbf48afa2..a27811695 100644 --- a/hutool-core/src/main/java/org/dromara/hutool/core/convert/impl/MapConverter.java +++ b/hutool-core/src/main/java/org/dromara/hutool/core/convert/impl/MapConverter.java @@ -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) )); } } diff --git a/hutool-core/src/main/java/org/dromara/hutool/core/convert/impl/PairConverter.java b/hutool-core/src/main/java/org/dromara/hutool/core/convert/impl/PairConverter.java index b031607a4..1f00f3c9c 100755 --- a/hutool-core/src/main/java/org/dromara/hutool/core/convert/impl/PairConverter.java +++ b/hutool-core/src/main/java/org/dromara/hutool/core/convert/impl/PairConverter.java @@ -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) ); } } diff --git a/hutool-core/src/main/java/org/dromara/hutool/core/convert/impl/ReferenceConverter.java b/hutool-core/src/main/java/org/dromara/hutool/core/convert/impl/ReferenceConverter.java index fceaa8e6c..5a7d7cd9e 100644 --- a/hutool-core/src/main/java/org/dromara/hutool/core/convert/impl/ReferenceConverter.java +++ b/hutool-core/src/main/java/org/dromara/hutool/core/convert/impl/ReferenceConverter.java @@ -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; diff --git a/hutool-core/src/main/java/org/dromara/hutool/core/convert/impl/TripleConverter.java b/hutool-core/src/main/java/org/dromara/hutool/core/convert/impl/TripleConverter.java index 7da863c2a..7a3521d37 100755 --- a/hutool-core/src/main/java/org/dromara/hutool/core/convert/impl/TripleConverter.java +++ b/hutool-core/src/main/java/org/dromara/hutool/core/convert/impl/TripleConverter.java @@ -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 { @@ -69,9 +75,9 @@ public class TripleConverter implements Converter { public Triple convert(final Type leftType, final Type middleType, final Type rightType, final Object value) throws ConvertException { Map map = null; - if(value instanceof Map){ + if (value instanceof Map) { map = (Map) value; - }else if (BeanUtil.isReadableBean(value.getClass())) { + } else if (BeanUtil.isReadableBean(value.getClass())) { // 一次性只读场景,包装为Map效率更高 map = BeanUtil.toBeanMap(value); } @@ -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) ); } } diff --git a/hutool-core/src/main/java/org/dromara/hutool/core/lang/Assert.java b/hutool-core/src/main/java/org/dromara/hutool/core/lang/Assert.java index 694188cb9..2dfc4d3c4 100644 --- a/hutool-core/src/main/java/org/dromara/hutool/core/lang/Assert.java +++ b/hutool-core/src/main/java/org/dromara/hutool/core/lang/Assert.java @@ -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} 抛出给定的异常
    @@ -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} . - *
    -	 * Assert.notNull(clazz, "The class must not be null");
    -	 * 
    + * 断言对象是否不为{@code null} ,如果为{@code null} 抛出{@link IllegalArgumentException} + *
    {@code
    +	 *   Assert.notNull(clazz, "The class must not be null");
    +	 * }
    * * @param 被检查对象泛型类型 * @param object 被检查对象 @@ -227,14 +229,17 @@ public class Assert { * @throws IllegalArgumentException if the object is {@code null} */ public static 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} 异常 - *
    -	 * Assert.notNull(clazz);
    -	 * 
    + *
    {@code
    +	 *   Assert.notNull(clazz);
    +	 * }
    * * @param 被检查对象类型 * @param object 被检查对象 @@ -242,193 +247,14 @@ public class Assert { * @throws IllegalArgumentException if the object is {@code null} */ public static T notNull(final T object) throws IllegalArgumentException { - return notNull(object, "[Assertion failed] - this argument is required; it must not be null"); - } - - // ----------------------------------------------------------------------------------------------------------- Check empty - - /** - * 检查给定字符串是否为空,为空抛出自定义异常,并使用指定的函数获取错误信息返回。 - *
    -	 * Assert.notEmpty(name, ()->{
    -	 *      // to query relation message
    -	 *      return new IllegalArgumentException("relation message to return");
    -	 *  });
    -	 * 
    - * - * @param 异常类型 - * @param 字符串类型 - * @param text 被检查字符串 - * @param errorSupplier 错误抛出异常附带的消息生产接口 - * @return 非空字符串 - * @throws X 被检查字符串为空抛出此异常 - * @see StrUtil#isNotEmpty(CharSequence) - * @since 5.4.5 - */ - public static T notEmpty(final T text, final Supplier errorSupplier) throws X { - if (StrUtil.isEmpty(text)) { - throw errorSupplier.get(); + if (null == object) { + throw new IllegalArgumentException("[Assertion failed] - this argument is required; it must not be null"); } - return text; + return object; } + // endregion - /** - * 检查给定字符串是否为空,为空抛出 {@link IllegalArgumentException} - * - *
    -	 * Assert.notEmpty(name, "Name must not be empty");
    -	 * 
    - * - * @param 字符串类型 - * @param text 被检查字符串 - * @param errorMsgTemplate 错误消息模板,变量使用{}表示 - * @param params 参数 - * @return 非空字符串 - * @throws IllegalArgumentException 被检查字符串为空 - * @see StrUtil#isNotEmpty(CharSequence) - */ - public static T notEmpty(final T text, final String errorMsgTemplate, final Object... params) throws IllegalArgumentException { - return notEmpty(text, () -> new IllegalArgumentException(StrUtil.format(errorMsgTemplate, params))); - } - - /** - * 检查给定字符串是否为空,为空抛出 {@link IllegalArgumentException} - * - *
    -	 * Assert.notEmpty(name);
    -	 * 
    - * - * @param 字符串类型 - * @param text 被检查字符串 - * @return 被检查的字符串 - * @throws IllegalArgumentException 被检查字符串为空 - * @see StrUtil#isNotEmpty(CharSequence) - */ - public static 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、空串或只包含空白符),为空抛出自定义异常。 - * 并使用指定的函数获取错误信息返回 - *
    -	 * Assert.notBlank(name, ()->{
    -	 *      // to query relation message
    -	 *      return new IllegalArgumentException("relation message to return");
    -	 *  });
    -	 * 
    - * - * @param 异常类型 - * @param 字符串类型 - * @param text 被检查字符串 - * @param errorMsgSupplier 错误抛出异常附带的消息生产接口 - * @return 非空字符串 - * @throws X 被检查字符串为空白 - * @see StrUtil#isNotBlank(CharSequence) - */ - public static T notBlank(final T text, final Supplier errorMsgSupplier) throws X { - if (StrUtil.isBlank(text)) { - throw errorMsgSupplier.get(); - } - return text; - } - - /** - * 检查给定字符串是否为空白(null、空串或只包含空白符),为空抛出 {@link IllegalArgumentException} - * - *
    -	 * Assert.notBlank(name, "Name must not be blank");
    -	 * 
    - * - * @param 字符串类型 - * @param text 被检查字符串 - * @param errorMsgTemplate 错误消息模板,变量使用{}表示 - * @param params 参数 - * @return 非空字符串 - * @throws IllegalArgumentException 被检查字符串为空白 - * @see StrUtil#isNotBlank(CharSequence) - */ - public static 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} - * - *
    -	 * Assert.notBlank(name);
    -	 * 
    - * - * @param 字符串类型 - * @param text 被检查字符串 - * @return 非空字符串 - * @throws IllegalArgumentException 被检查字符串为空白 - * @see StrUtil#isNotBlank(CharSequence) - */ - public static 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"); - } - - /** - * 断言给定字符串是否不被另一个字符串包含(即是否为子串),并使用指定的函数获取错误信息返回
    - * 如果非子串,返回子串,如果是子串,则抛出{@link IllegalArgumentException}异常。 - *
    -	 * Assert.notContain(name, "rod", ()->{
    -	 *      // to query relation message
    -	 *      return new IllegalArgumentException("relation message to return ");
    -	 *  });
    -	 * 
    - * - * @param 字符串类型 - * @param 异常类型 - * @param textToSearch 被搜索的字符串 - * @param substring 被检查的子串 - * @param errorSupplier 错误抛出异常附带的消息生产接口 - * @return 被检查的子串 - * @throws X 非子串抛出异常 - * @see StrUtil#contains(CharSequence, CharSequence) - * @since 5.4.5 - */ - public static T notContain(final CharSequence textToSearch, final T substring, final Supplier errorSupplier) throws X { - if (StrUtil.contains(textToSearch, substring)) { - throw errorSupplier.get(); - } - return substring; - } - - /** - * 断言给定字符串是否不被另一个字符串包含(即是否为子串)
    - * 如果非子串,返回子串,如果是子串,则抛出{@link IllegalArgumentException}异常。 - *
    -	 * Assert.notContain(name, "rod", "Name must not contain 'rod'");
    -	 * 
    - * - * @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的子串。
    - * 如果非子串,返回子串,如果是子串,则抛出{@link IllegalArgumentException}异常。 - *
    -	 * Assert.notContain(name, "rod");
    -	 * 
    - * - * @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}将被认为不包含 - * 并使用指定的函数获取错误信息返回 - *
    -	 * Assert.noNullElements(array, ()->{
    -	 *      // to query relation message
    -	 *      return new IllegalArgumentException("relation message to return ");
    -	 *  });
    -	 * 
    - * - * @param 数组元素类型 - * @param 异常类型 - * @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[] noNullElements(final T[] array, final Supplier errorSupplier) throws X { - if (ArrayUtil.hasNull(array)) { - throw errorSupplier.get(); - } - return array; - } - - /** - * 断言给定数组是否不包含{@code null}元素,如果数组为空或 {@code null}将被认为不包含 - *
    -	 * Assert.noNullElements(array, "The array must not have null elements");
    -	 * 
    - * - * @param 数组元素类型 - * @param array 被检查的数组 - * @param errorMsgTemplate 异常时的消息模板 - * @param params 参数列表 - * @return 被检查的数组 - * @throws IllegalArgumentException if the object array contains a {@code null} element - */ - public static 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}将被认为不包含 - *
    -	 * Assert.noNullElements(array);
    -	 * 
    - * - * @param 数组元素类型 - * @param array 被检查的数组 - * @return 被检查的数组 - * @throws IllegalArgumentException if the object array contains a {@code null} element - */ - public static 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"); } + /** + * 检查给定字符串是否为空,为空抛出自定义异常,并使用指定的函数获取错误信息返回。 + *
    +	 * Assert.notEmpty(name, ()->{
    +	 *      // to query relation message
    +	 *      return new IllegalArgumentException("relation message to return");
    +	 *  });
    +	 * 
    + * + * @param 异常类型 + * @param 字符串类型 + * @param text 被检查字符串 + * @param errorSupplier 错误抛出异常附带的消息生产接口 + * @return 非空字符串 + * @throws X 被检查字符串为空抛出此异常 + * @see StrUtil#isNotEmpty(CharSequence) + * @since 5.4.5 + */ + public static T notEmpty(final T text, final Supplier errorSupplier) throws X { + if (StrUtil.isEmpty(text)) { + throw errorSupplier.get(); + } + return text; + } + + /** + * 检查给定字符串是否为空,为空抛出 {@link IllegalArgumentException} + * + *
    +	 * Assert.notEmpty(name, "Name must not be empty");
    +	 * 
    + * + * @param 字符串类型 + * @param text 被检查字符串 + * @param errorMsgTemplate 错误消息模板,变量使用{}表示 + * @param params 参数 + * @return 非空字符串 + * @throws IllegalArgumentException 被检查字符串为空 + * @see StrUtil#isNotEmpty(CharSequence) + */ + public static T notEmpty(final T text, final String errorMsgTemplate, final Object... params) throws IllegalArgumentException { + return notEmpty(text, () -> new IllegalArgumentException(StrUtil.format(errorMsgTemplate, params))); + } + + /** + * 检查给定字符串是否为空,为空抛出 {@link IllegalArgumentException} + * + *
    +	 * Assert.notEmpty(name);
    +	 * 
    + * + * @param 字符串类型 + * @param text 被检查字符串 + * @return 被检查的字符串 + * @throws IllegalArgumentException 被检查字符串为空 + * @see StrUtil#isNotEmpty(CharSequence) + */ + public static 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、空串或只包含空白符),为空抛出自定义异常。 + * 并使用指定的函数获取错误信息返回 + *
    +	 * Assert.notBlank(name, ()->{
    +	 *      // to query relation message
    +	 *      return new IllegalArgumentException("relation message to return");
    +	 *  });
    +	 * 
    + * + * @param 异常类型 + * @param 字符串类型 + * @param text 被检查字符串 + * @param errorMsgSupplier 错误抛出异常附带的消息生产接口 + * @return 非空字符串 + * @throws X 被检查字符串为空白 + * @see StrUtil#isNotBlank(CharSequence) + */ + public static T notBlank(final T text, final Supplier errorMsgSupplier) throws X { + if (StrUtil.isBlank(text)) { + throw errorMsgSupplier.get(); + } + return text; + } + + /** + * 检查给定字符串是否为空白(null、空串或只包含空白符),为空抛出 {@link IllegalArgumentException} + * + *
    +	 * Assert.notBlank(name, "Name must not be blank");
    +	 * 
    + * + * @param 字符串类型 + * @param text 被检查字符串 + * @param errorMsgTemplate 错误消息模板,变量使用{}表示 + * @param params 参数 + * @return 非空字符串 + * @throws IllegalArgumentException 被检查字符串为空白 + * @see StrUtil#isNotBlank(CharSequence) + */ + public static 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} + * + *
    +	 * Assert.notBlank(name);
    +	 * 
    + * + * @param 字符串类型 + * @param text 被检查字符串 + * @return 非空字符串 + * @throws IllegalArgumentException 被检查字符串为空白 + * @see StrUtil#isNotBlank(CharSequence) + */ + public static 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 + /** + * 断言给定字符串是否不被另一个字符串包含(即是否为子串),并使用指定的函数获取错误信息返回
    + * 如果非子串,返回子串,如果是子串,则抛出{@link IllegalArgumentException}异常。 + *
    +	 * Assert.notContain(name, "rod", ()->{
    +	 *      // to query relation message
    +	 *      return new IllegalArgumentException("relation message to return ");
    +	 *  });
    +	 * 
    + * + * @param 字符串类型 + * @param 异常类型 + * @param textToSearch 被搜索的字符串 + * @param substring 被检查的子串 + * @param errorSupplier 错误抛出异常附带的消息生产接口 + * @return 被检查的子串 + * @throws X 非子串抛出异常 + * @see StrUtil#contains(CharSequence, CharSequence) + * @since 5.4.5 + */ + public static T notContain(final CharSequence textToSearch, final T substring, final Supplier errorSupplier) throws X { + if (StrUtil.contains(textToSearch, substring)) { + throw errorSupplier.get(); + } + return substring; + } + + /** + * 断言给定字符串是否不被另一个字符串包含(即是否为子串)
    + * 如果非子串,返回子串,如果是子串,则抛出{@link IllegalArgumentException}异常。 + *
    +	 * Assert.notContain(name, "rod", "Name must not contain 'rod'");
    +	 * 
    + * + * @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的子串。
    + * 如果非子串,返回子串,如果是子串,则抛出{@link IllegalArgumentException}异常。 + *
    +	 * Assert.notContain(name, "rod");
    +	 * 
    + * + * @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}将被认为不包含 + * 并使用指定的函数获取错误信息返回 + *
    +	 * Assert.noNullElements(array, ()->{
    +	 *      // to query relation message
    +	 *      return new IllegalArgumentException("relation message to return ");
    +	 *  });
    +	 * 
    + * + * @param 数组元素类型 + * @param 异常类型 + * @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[] noNullElements(final T[] array, final Supplier errorSupplier) throws X { + if (ArrayUtil.hasNull(array)) { + throw errorSupplier.get(); + } + return array; + } + + /** + * 断言给定数组是否不包含{@code null}元素,如果数组为空或 {@code null}将被认为不包含 + *
    +	 * Assert.noNullElements(array, "The array must not have null elements");
    +	 * 
    + * + * @param 数组元素类型 + * @param array 被检查的数组 + * @param errorMsgTemplate 异常时的消息模板 + * @param params 参数列表 + * @return 被检查的数组 + * @throws IllegalArgumentException if the object array contains a {@code null} element + */ + public static 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}将被认为不包含 + *
    +	 * Assert.noNullElements(array);
    +	 * 
    + * + * @param 数组元素类型 + * @param array 被检查的数组 + * @return 被检查的数组 + * @throws IllegalArgumentException if the object array contains a {@code null} element + */ + public static 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 + /** * 断言给定对象是否是给定类的实例 *
    @@ -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 异常
     	 * 
    @@ -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
     
    diff --git a/hutool-core/src/test/java/org/dromara/hutool/core/convert/CompositeConverterTest.java b/hutool-core/src/test/java/org/dromara/hutool/core/convert/CompositeConverterTest.java
    index 2fa46ceb4..f0f5fe23d 100644
    --- a/hutool-core/src/test/java/org/dromara/hutool/core/convert/CompositeConverterTest.java
    +++ b/hutool-core/src/test/java/org/dromara/hutool/core/convert/CompositeConverterTest.java
    @@ -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;
    diff --git a/hutool-http/src/main/java/org/dromara/hutool/http/client/engine/jdk/JdkHttpConnection.java b/hutool-http/src/main/java/org/dromara/hutool/http/client/engine/jdk/JdkHttpConnection.java
    index 41b5139d1..57fd62d59 100644
    --- a/hutool-http/src/main/java/org/dromara/hutool/http/client/engine/jdk/JdkHttpConnection.java
    +++ b/hutool-http/src/main/java/org/dromara/hutool/http/client/engine/jdk/JdkHttpConnection.java
    @@ -74,8 +74,6 @@ public class JdkHttpConnection implements HeaderOperation, Cl
     		return new JdkHttpConnection(url, proxy);
     	}
     
    -	// region --------------------------------------------------------------- Constructor
    -
     	/**
     	 * 构造HttpConnection
     	 *
    @@ -92,9 +90,7 @@ public class JdkHttpConnection implements HeaderOperation, 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, Cl
     		return this;
     	}
     
    -	// endregion --------------------------------------------------------------- Getters And Setters
    +	// endregion ----- Getters And Setters
     
    -	// region ---------------------------------------------------------------- Headers
    +	// region ------ Headers
     
     	/**
     	 * 设置请求头
    @@ -327,7 +323,7 @@ public class JdkHttpConnection implements HeaderOperation, Cl return this.conn.getHeaderFields(); } - // endregion---------------------------------------------------------------- Headers + // endregion------ Headers /** * 连接 diff --git a/hutool-json/src/main/java/org/dromara/hutool/json/convert/JSONConverter.java b/hutool-json/src/main/java/org/dromara/hutool/json/convert/JSONConverter.java index 69840953c..8f942c582 100644 --- a/hutool-json/src/main/java/org/dromara/hutool/json/convert/JSONConverter.java +++ b/hutool-json/src/main/java/org/dromara/hutool/json/convert/JSONConverter.java @@ -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; } @@ -150,7 +152,7 @@ public class JSONConverter implements Converter, Serializable { obj = ((Opt) obj).getOrNull(); } - if(obj instanceof JSON){ + if (obj instanceof JSON) { return (JSON) obj; } @@ -242,33 +244,41 @@ public class JSONConverter implements Converter, Serializable { final Object value; // JSON原始类型 - if(json instanceof JSONPrimitive){ + if (json instanceof JSONPrimitive) { value = ((JSONPrimitive) json).getValue(); } else { value = json; } - // 标准转换器 - final Converter converter = RegisterConverter.getInstance().getConverter(targetType, value, true); - if (null != converter) { - return (T) converter.convert(targetType, value); - } + final JSONConfig config = ObjUtil.defaultIfNull(json.config(), JSONConfig::of); + final boolean ignoreError = config.isIgnoreError(); + try { + // 标准转换器 + final Converter converter = registerConverter.getConverter(targetType, value, true); + if (null != converter) { + return (T) converter.convert(targetType, value); + } - // 特殊类型转换,包括Collection、Map、强转、Array等 - final T result = (T) SpecialConverter.getInstance().convert(targetType, rawType, value); - if (null != result) { - return result; + // 特殊类型转换,包括Collection、Map、强转、Array等 + 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; } diff --git a/hutool-json/src/test/java/org/dromara/hutool/json/Issue1200Test.java b/hutool-json/src/test/java/org/dromara/hutool/json/Issue1200Test.java index 2b5c64247..99b624cc6 100644 --- a/hutool-json/src/test/java/org/dromara/hutool/json/Issue1200Test.java +++ b/hutool-json/src/test/java/org/dromara/hutool/json/Issue1200Test.java @@ -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。 - *

    - * 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); + public void toBeanTest() { + 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>> items; + + @Data + public static class ItemsBean { + public ResultBean.ItemsBean.DetailBean detail; + + @Data + public static class DetailBean { + public String visitorStatus; + } + } } } diff --git a/hutool-json/src/test/java/org/dromara/hutool/json/JSONArrayTest.java b/hutool-json/src/test/java/org/dromara/hutool/json/JSONArrayTest.java index 90b41c40f..d648eda4b 100644 --- a/hutool-json/src/test/java/org/dromara/hutool/json/JSONArrayTest.java +++ b/hutool-json/src/test/java/org/dromara/hutool/json/JSONArrayTest.java @@ -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); diff --git a/hutool-json/src/test/java/org/dromara/hutool/json/test/bean/ResultBean.java b/hutool-json/src/test/java/org/dromara/hutool/json/test/bean/ResultBean.java deleted file mode 100644 index e366f1479..000000000 --- a/hutool-json/src/test/java/org/dromara/hutool/json/test/bean/ResultBean.java +++ /dev/null @@ -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>> items; - - @Data - public static class ItemsBean { - public DetailBean detail; - - @Data - public static class DetailBean { - public String visitorStatus; - } - } -}