From 6c001e868bf8150e6ac864677e8d39863e7c79b7 Mon Sep 17 00:00:00 2001 From: huangchengxing <841396397@qq.com> Date: Thu, 25 Aug 2022 12:13:00 +0800 Subject: [PATCH 01/13] =?UTF-8?q?=E4=BC=98=E5=8C=96=E6=94=AF=E6=8C=81?= =?UTF-8?q?=E4=B8=80=E9=94=AE=E5=A4=9A=E5=80=BC=E7=9A=84Map=E9=9B=86?= =?UTF-8?q?=E5=90=88=E6=89=A9=E5=B1=95=EF=BC=8C=E4=B8=BA=E5=85=B6=E6=B7=BB?= =?UTF-8?q?=E5=8A=A0=E7=BB=9F=E4=B8=80=E6=8E=A5=E5=8F=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/cn/hutool/core/map/MapWrapper.java | 15 +- .../core/map/multi/AbsCollValueMap.java | 168 +++++++----- .../core/map/multi/CollectionValueMap.java | 100 +++---- .../hutool/core/map/multi/ListValueMap.java | 69 ++--- .../hutool/core/map/multi/MultiValueMap.java | 250 ++++++++++++++++++ .../cn/hutool/core/map/multi/SetValueMap.java | 70 ++--- .../core/net/multipart/MultipartFormData.java | 23 +- .../core/map/CollectionValueMapTest.java | 160 +++++++++++ .../cn/hutool/core/map/ListValueMapTest.java | 160 +++++++++++ .../cn/hutool/core/map/SetValueMapTest.java | 162 ++++++++++++ 10 files changed, 941 insertions(+), 236 deletions(-) create mode 100644 hutool-core/src/main/java/cn/hutool/core/map/multi/MultiValueMap.java create mode 100644 hutool-core/src/test/java/cn/hutool/core/map/CollectionValueMapTest.java create mode 100644 hutool-core/src/test/java/cn/hutool/core/map/ListValueMapTest.java create mode 100644 hutool-core/src/test/java/cn/hutool/core/map/SetValueMapTest.java diff --git a/hutool-core/src/main/java/cn/hutool/core/map/MapWrapper.java b/hutool-core/src/main/java/cn/hutool/core/map/MapWrapper.java index 95e4bb197..e1102da9d 100755 --- a/hutool-core/src/main/java/cn/hutool/core/map/MapWrapper.java +++ b/hutool-core/src/main/java/cn/hutool/core/map/MapWrapper.java @@ -1,16 +1,13 @@ package cn.hutool.core.map; +import cn.hutool.core.lang.Assert; import cn.hutool.core.util.ObjUtil; import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.io.Serializable; -import java.util.Collection; -import java.util.Iterator; -import java.util.Map; -import java.util.Objects; -import java.util.Set; +import java.util.*; import java.util.function.BiConsumer; import java.util.function.BiFunction; import java.util.function.Function; @@ -35,7 +32,9 @@ public class MapWrapper implements Map, Iterable>, S * 默认初始大小 */ protected static final int DEFAULT_INITIAL_CAPACITY = 1 << 4; // aka 16 - + /** + * 原始集合 + */ private Map raw; /** @@ -52,9 +51,11 @@ public class MapWrapper implements Map, Iterable>, S /** * 构造 * - * @param raw 被包装的Map + * @param raw 被包装的Map,不允许为{@code null} + * @throws NullPointerException 当被包装的集合为{@code null}时抛出 */ public MapWrapper(final Map raw) { + Assert.notNull(raw, "raw must not null"); this.raw = raw; } diff --git a/hutool-core/src/main/java/cn/hutool/core/map/multi/AbsCollValueMap.java b/hutool-core/src/main/java/cn/hutool/core/map/multi/AbsCollValueMap.java index 7ccc48292..9c0e960d1 100644 --- a/hutool-core/src/main/java/cn/hutool/core/map/multi/AbsCollValueMap.java +++ b/hutool-core/src/main/java/cn/hutool/core/map/multi/AbsCollValueMap.java @@ -1,22 +1,29 @@ package cn.hutool.core.map.multi; import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.lang.Opt; import cn.hutool.core.map.MapWrapper; +import cn.hutool.core.util.ObjUtil; import java.util.Collection; import java.util.HashMap; import java.util.Map; +import java.util.function.BiFunction; +import java.util.function.BiPredicate; +import java.util.function.Supplier; +import java.util.stream.Collectors; /** - * 值作为集合的Map实现,通过调用putValue可以在相同key时加入多个值,多个值用集合表示 + * {@link MultiValueMap}的基本实现 * * @param 键类型 * @param 值类型 - * @param 集合类型 * @author looly * @since 5.7.4 + * @see SetValueMap + * @see ListValueMap */ -public abstract class AbsCollValueMap> extends MapWrapper { +public abstract class AbsCollValueMap extends MapWrapper> implements MultiValueMap { private static final long serialVersionUID = 1L; /** @@ -27,94 +34,132 @@ public abstract class AbsCollValueMap> extends Map // ------------------------------------------------------------------------- Constructor start /** - * 构造 + * 使用{@code mapFactory}创建的集合构造一个多值映射Map集合 + * + * @param mapFactory 生成集合的工厂方法 */ - public AbsCollValueMap() { - this(DEFAULT_INITIAL_CAPACITY); + protected AbsCollValueMap(Supplier>> mapFactory) { + super(mapFactory); } /** - * 构造 + * 基于{@link HashMap}构造一个多值映射集合 * - * @param initialCapacity 初始大小 + * @param map 提供初始数据的集合 */ - public AbsCollValueMap(final int initialCapacity) { - this(initialCapacity, DEFAULT_LOAD_FACTOR); + protected AbsCollValueMap(Map> map) { + super(new HashMap<>(map)); } /** - * 构造 - * - * @param m Map + * 基于{@link HashMap}构造一个多值映射集合 */ - public AbsCollValueMap(final Map m) { - this(DEFAULT_LOAD_FACTOR, m); + protected AbsCollValueMap() { + super(new HashMap<>(16)); } - /** - * 构造 - * - * @param loadFactor 加载因子 - * @param m Map - */ - public AbsCollValueMap(final float loadFactor, final Map m) { - this(m.size(), loadFactor); - this.putAll(m); - } - - /** - * 构造 - * - * @param initialCapacity 初始大小 - * @param loadFactor 加载因子 - */ - public AbsCollValueMap(final int initialCapacity, final float loadFactor) { - super(new HashMap<>(initialCapacity, loadFactor)); - } // ------------------------------------------------------------------------- Constructor end + /** - * 放入所有value + * 将集合中的全部元素对追加到指定键对应的值集合中,效果等同于: + *
{@code
+	 * coll.forEach(t -> map.putValue(key, t))
+	 * }
* - * @param m valueMap - * @since 5.7.4 + * @param key 键 + * @param coll 待添加的值集合 + * @return 是否成功添加 */ - public void putAllValues(final Map> m) { - if(null != m){ - m.forEach((key, valueColl) -> { - if(null != valueColl){ - valueColl.forEach((value) -> putValue(key, value)); - } - }); + @Override + public boolean putAllValues(K key, Collection coll) { + if (ObjUtil.isNull(coll)) { + return false; } + return super.computeIfAbsent(key, k -> createCollection()) + .addAll(coll); } /** - * 放入Value
- * 如果键对应值列表有值,加入,否则创建一个新列表后加入 + * 向指定键对应的值集合追加值,效果等同于: + *
{@code
+	 * map.computeIfAbsent(key, k -> new Collection()).add(value)
+	 * }
* * @param key 键 * @param value 值 + * @return 是否成功添加 */ - public void putValue(final K key, final V value) { - C collection = this.get(key); - if (null == collection) { - collection = createCollection(); - this.put(key, collection); - } - collection.add(value); + @Override + public boolean putValue(K key, V value) { + return super.computeIfAbsent(key, k -> createCollection()) + .add(value); } /** - * 获取值 + * 将值从指定键下的值集合中删除 * * @param key 键 - * @param index 第几个值的索引,越界返回null - * @return 值或null + * @param value 值 + * @return 是否成功删除 */ - public V get(final K key, final int index) { - final Collection collection = get(key); - return CollUtil.get(collection, index); + @Override + public boolean removeValue(K key, V value) { + return Opt.ofNullable(super.get(key)) + .map(t -> t.remove(value)) + .orElse(false); + } + + /** + * 将一批值从指定键下的值集合中删除 + * + * @param key 键 + * @param values 值 + * @return 是否成功删除 + */ + @Override + public boolean removeAllValues(K key, Collection values) { + if (CollUtil.isEmpty(values)) { + return false; + } + Collection coll = get(key); + return ObjUtil.isNotNull(coll) && coll.removeAll(values); + } + + /** + * 根据条件过滤所有值集合中的值,并以新值生成新的值集合,新集合中的值集合类型与当前实例的默认值集合类型保持一致 + * + * @param filter 判断方法 + * @return 当前实例 + */ + @Override + public MultiValueMap filterAllValues(BiPredicate filter) { + entrySet().forEach(e -> { + K k = e.getKey(); + Collection coll = e.getValue().stream() + .filter(v -> filter.test(k, v)) + .collect(Collectors.toCollection(this::createCollection)); + e.setValue(coll); + }); + return this; + } + + /** + * 根据条件替换所有值集合中的值,并以新值生成新的值集合,新集合中的值集合类型与当前实例的默认值集合类型保持一致 + * + * @param operate 替换方法 + * @return 当前实例 + */ + @Override + public MultiValueMap replaceAllValues(BiFunction operate) { + entrySet().forEach(e -> { + K k = e.getKey(); + Collection coll = e.getValue().stream() + .map(v -> operate.apply(k, v)) + .collect(Collectors.toCollection(this::createCollection)); + e.setValue(coll); + }); + return this; } /** @@ -123,5 +168,6 @@ public abstract class AbsCollValueMap> extends Map * * @return {@link Collection} */ - protected abstract C createCollection(); + protected abstract Collection createCollection(); + } diff --git a/hutool-core/src/main/java/cn/hutool/core/map/multi/CollectionValueMap.java b/hutool-core/src/main/java/cn/hutool/core/map/multi/CollectionValueMap.java index 3f9dd8bcf..e798c7602 100644 --- a/hutool-core/src/main/java/cn/hutool/core/map/multi/CollectionValueMap.java +++ b/hutool-core/src/main/java/cn/hutool/core/map/multi/CollectionValueMap.java @@ -6,97 +6,67 @@ import java.util.ArrayList; import java.util.Collection; import java.util.HashMap; import java.util.Map; +import java.util.function.Supplier; /** - * 值作为集合的Map实现,通过调用putValue可以在相同key时加入多个值,多个值用集合表示
- * 此类可以通过传入函数自定义集合类型的创建规则 + *

{@link MultiValueMap}的通用实现,可视为值为{@link Collection}集合的{@link Map}集合。
+ * 构建时指定一个工厂方法用于生成原始的{@link Map}集合,然后再指定一个工厂方法用于生成自定义类型的值集合。
+ * 当调用{@link MultiValueMap}中格式为“putXXX”的方法时,将会为key创建值集合,并将key相同的值追加到集合中 * * @param 键类型 * @param 值类型 * @author looly * @since 4.3.3 */ -public class CollectionValueMap extends AbsCollValueMap> { +public class CollectionValueMap extends AbsCollValueMap { + private static final long serialVersionUID = 9012989578038102983L; - private final Func0> collectionCreateFunc; + private final Func0> collFactory; // ------------------------------------------------------------------------- Constructor start /** - * 构造 + * 创建一个多值映射集合,基于{@code mapFactory}与{@code collFactory}实现 + * + * @param mapFactory 生成集合的工厂方法 + * @param collFactory 生成值集合的工厂方法 + */ + public CollectionValueMap(Supplier>> mapFactory, Func0> collFactory) { + super(mapFactory); + this.collFactory = collFactory; + } + + /** + * 创建一个多值映射集合,默认基于{@link HashMap}与{@code collFactory}生成的集合实现 + * + * @param collFactory 生成值集合的工厂方法 + */ + public CollectionValueMap(Func0> collFactory) { + this.collFactory = collFactory; + } + + /** + * 创建一个多值映射集合,默认基于{@link HashMap}与{@link ArrayList}实现 */ public CollectionValueMap() { - this(DEFAULT_INITIAL_CAPACITY); + this.collFactory = ArrayList::new; } /** - * 构造 + * 创建一个多值映射集合,默认基于{@link HashMap}与{@link ArrayList}实现 * - * @param initialCapacity 初始大小 + * @param map 提供数据的原始集合 */ - public CollectionValueMap(final int initialCapacity) { - this(initialCapacity, DEFAULT_LOAD_FACTOR); + public CollectionValueMap(Map> map) { + super(map); + this.collFactory = ArrayList::new; } - /** - * 构造 - * - * @param m Map - */ - public CollectionValueMap(final Map> m) { - this(DEFAULT_LOAD_FACTOR, m); - } - - /** - * 构造 - * - * @param loadFactor 加载因子 - * @param m Map - */ - public CollectionValueMap(final float loadFactor, final Map> m) { - this(loadFactor, m, ArrayList::new); - } - - /** - * 构造 - * - * @param initialCapacity 初始大小 - * @param loadFactor 加载因子 - */ - public CollectionValueMap(final int initialCapacity, final float loadFactor) { - this(initialCapacity, loadFactor, ArrayList::new); - } - - /** - * 构造 - * - * @param loadFactor 加载因子 - * @param m Map - * @param collectionCreateFunc Map中值的集合创建函数 - * @since 5.7.4 - */ - public CollectionValueMap(final float loadFactor, final Map> m, final Func0> collectionCreateFunc) { - this(m.size(), loadFactor, collectionCreateFunc); - this.putAll(m); - } - - /** - * 构造 - * - * @param initialCapacity 初始大小 - * @param loadFactor 加载因子 - * @param collectionCreateFunc Map中值的集合创建函数 - * @since 5.7.4 - */ - public CollectionValueMap(final int initialCapacity, final float loadFactor, final Func0> collectionCreateFunc) { - super(new HashMap<>(initialCapacity, loadFactor)); - this.collectionCreateFunc = collectionCreateFunc; - } // ------------------------------------------------------------------------- Constructor end @Override protected Collection createCollection() { - return collectionCreateFunc.callWithRuntimeException(); + return collFactory.callWithRuntimeException(); } } diff --git a/hutool-core/src/main/java/cn/hutool/core/map/multi/ListValueMap.java b/hutool-core/src/main/java/cn/hutool/core/map/multi/ListValueMap.java index ab3b29c33..90f79cc01 100644 --- a/hutool-core/src/main/java/cn/hutool/core/map/multi/ListValueMap.java +++ b/hutool-core/src/main/java/cn/hutool/core/map/multi/ListValueMap.java @@ -1,10 +1,7 @@ package cn.hutool.core.map.multi; -import java.util.ArrayList; -import java.util.Collection; -import java.util.HashMap; -import java.util.List; -import java.util.Map; +import java.util.*; +import java.util.function.Supplier; /** * 值作为集合List的Map实现,通过调用putValue可以在相同key时加入多个值,多个值用集合表示 @@ -15,55 +12,35 @@ import java.util.Map; * @param 值类型 * @since 4.3.3 */ -public class ListValueMap extends AbsCollValueMap> { +public class ListValueMap extends AbsCollValueMap { private static final long serialVersionUID = 6044017508487827899L; // ------------------------------------------------------------------------- Constructor start + /** - * 构造 + * 基于{@code mapFactory}创建一个值为{@link List}的多值映射集合 + * + * @param mapFactory 创建集合的工厂反方 + */ + public ListValueMap(Supplier>> mapFactory) { + super(mapFactory); + } + + /** + * 基于{@link HashMap}创建一个值为{@link List}的多值映射集合 + * + * @param map 提供数据的原始集合 + */ + public ListValueMap(Map> map) { + super(map); + } + + /** + * 基于{@link HashMap}创建一个值为{@link List}的多值映射集合 */ public ListValueMap() { - this(DEFAULT_INITIAL_CAPACITY); } - /** - * 构造 - * - * @param initialCapacity 初始大小 - */ - public ListValueMap(final int initialCapacity) { - this(initialCapacity, DEFAULT_LOAD_FACTOR); - } - - /** - * 构造 - * - * @param m Map - */ - public ListValueMap(final Map> m) { - this(DEFAULT_LOAD_FACTOR, m); - } - - /** - * 构造 - * - * @param loadFactor 加载因子 - * @param m Map - */ - public ListValueMap(final float loadFactor, final Map> m) { - this(m.size(), loadFactor); - this.putAllValues(m); - } - - /** - * 构造 - * - * @param initialCapacity 初始大小 - * @param loadFactor 加载因子 - */ - public ListValueMap(final int initialCapacity, final float loadFactor) { - super(new HashMap<>(initialCapacity, loadFactor)); - } // ------------------------------------------------------------------------- Constructor end @Override diff --git a/hutool-core/src/main/java/cn/hutool/core/map/multi/MultiValueMap.java b/hutool-core/src/main/java/cn/hutool/core/map/multi/MultiValueMap.java new file mode 100644 index 000000000..684d83a54 --- /dev/null +++ b/hutool-core/src/main/java/cn/hutool/core/map/multi/MultiValueMap.java @@ -0,0 +1,250 @@ +package cn.hutool.core.map.multi; + +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.util.ArrayUtil; + +import java.util.*; +import java.util.function.*; +import java.util.stream.Collectors; + +/** + *

一个键对应多个值的集合{@link Map}实现,提供针对键对应的值集合中的元素而非值集合本身的一些快捷操作, + * 本身可作为一个值为{@link Collection}类型的{@link Map}使用。
+ * + *

值集合类型

+ *

值集合的类型由接口的实现类自行维护,当通过{@link MultiValueMap}定义的方法进行增删改操作时, + * 实现类应保证通过通过实例方法获得的集合类型都一致。但是若用户直接通过{@link Map}定义的方法进行增删改操作时, + * 实例无法保证通过实例方法获得的集合类型都一致。
+ * 因此,若无必要则更推荐通过{@link MultiValueMap}定义的方法进行操作。 + * + *

对值集合的修改

+ *

当通过实例方法获得值集合时,若该集合允许修改,则对值集合的修改将会影响到其所属的{@link MultiValueMap}实例,反之亦然。 + * 因此当同时遍历当前实例或者值集合时,若存在写操作,则需要注意可能引发的{@link ConcurrentModificationException}。 + * + * @author huangchengxing + * @since 6.0.0 + * @see AbsCollValueMap + * @see CollectionValueMap + * @see ListValueMap + * @see SetValueMap + */ +public interface MultiValueMap extends Map> { + + // =================== override =================== + + /** + * 更新键对应的值集合
+ * 注意:该操作将移除键对应的旧值集合,若仅需向值集合追加应值,则应使用{@link #putAllValues(Object, Collection)} + * + * @param key 键 + * @param value 键对应的新值集合 + * @return 旧值集合 + */ + @Override + Collection put(K key, Collection value); + + /** + * 更新全部键的值集合
+ * 注意:该操作将移除键对应的旧值集合,若仅需向值集合追加应值,则应使用{@link #putAllValues(Object, Collection)} + * + * @param map 需要更新的键值对集合 + */ + @Override + void putAll(Map> map); + + // =================== write operate =================== + + /** + * 将集合中的全部键值对追加到当前实例中,效果等同于: + *

{@code
+	 * for (Entry> entry : m.entrySet()) {
+	 * 	K key = entry.getKey();
+	 * 	Collection coll = entry.getValues();
+	 * 	for (V val : coll) {
+	 * 		map.putValue(key, val)
+	 * 	}
+	 * }
+	 * }
+ * + * @param m 待添加的集合 + */ + default void putAllValues(final Map> m) { + if (CollUtil.isNotEmpty(m)) { + m.forEach(this::putAllValues); + } + } + + /** + * 将集合中的全部元素对追加到指定键对应的值集合中,效果等同于: + *
{@code
+	 * 	for (V val : coll) {
+	 * 		map.putValue(key, val)
+	 * 	}
+	 * }
+ * + * @param key 键 + * @param coll 待添加的值集合 + * @return 是否成功添加 + */ + boolean putAllValues(K key, final Collection coll); + + /** + * 将数组中的全部元素追加到指定的值集合中,效果等同于: + *
{@code
+	 * 	for (V val : values) {
+	 * 		map.putValue(key, val)
+	 * 	}
+	 * }
+ * + * @param key 键 + * @param values 待添加的值 + * @return boolean + */ + default boolean putValues(final K key, final V... values) { + return ArrayUtil.isNotEmpty(values) && putAllValues(key, Arrays.asList(values)); + } + + /** + * 向指定键对应的值集合追加值,效果等同于: + *
{@code
+	 * Collection coll = map.get(key);
+	 * if(null == coll) {
+	 * 	coll.add(value);
+	 * 	map.put(coll);
+	 * } else {
+	 * 	coll.add(value);
+	 * }
+	 * }
+ * + * @param key 键 + * @param value 值 + * @return 是否成功添加 + */ + boolean putValue(final K key, final V value); + + /** + * 将值从指定键下的值集合中删除 + * + * @param key 键 + * @param value 值 + * @return 是否成功删除 + */ + boolean removeValue(final K key, final V value); + + /** + * 将一批值从指定键下的值集合中删除 + * + * @param key 键 + * @param values 值数组 + * @return 是否成功删除 + */ + default boolean removeValues(final K key, final V... values) { + return ArrayUtil.isNotEmpty(values) && removeAllValues(key, Arrays.asList(values)); + } + + /** + * 将一批值从指定键下的值集合中删除 + * + * @param key 键 + * @param values 值集合 + * @return 是否成功删除 + */ + boolean removeAllValues(final K key, final Collection values); + + /** + * 根据条件过滤所有值集合中的值,并以新值生成新的值集合,新集合中的值集合类型与当前实例的默认值集合类型保持一致 + * + * @param filter 判断方法 + * @return 当前实例 + */ + default MultiValueMap filterAllValues(Predicate filter) { + return filterAllValues((k, v) -> filter.test(v)); + } + + /** + * 根据条件过滤所有值集合中的值,并以新值生成新的值集合,新集合中的值集合类型与当前实例的默认值集合类型保持一致 + * + * @param filter 判断方法 + * @return 当前实例 + */ + MultiValueMap filterAllValues(BiPredicate filter); + + /** + * 根据条件替换所有值集合中的值,并以新值生成新的值集合,新集合中的值集合类型与当前实例的默认值集合类型保持一致 + * + * @param operate 替换方法 + * @return 当前实例 + */ + default MultiValueMap replaceAllValues(UnaryOperator operate) { + return replaceAllValues((k, v) -> operate.apply(v)); + } + + /** + * 根据条件替换所有值集合中的值,并以新值生成新的值集合,新集合中的值集合类型与当前实例的默认值集合类型保持一致 + * + * @param operate 替换方法 + * @return 当前实例 + */ + MultiValueMap replaceAllValues(BiFunction operate); + + // =================== read operate =================== + + /** + * 获取键对应的值,若值不存在,则返回{@link Collections#emptyList()}。效果等同于: + *
{@code
+	 * map.getOrDefault(key, Collections.emptyList())
+	 * }
+ * + * @param key 键 + * @return 值集合 + */ + default Collection getValues(final K key) { + return getOrDefault(key, Collections.emptyList()); + } + + /** + * 获取键对应值的数量,若键对应的值不存在,则返回{@code 0} + * + * @param key 键 + * @return 值的数量 + */ + default int size(final K key) { + return getValues(key).size(); + } + + /** + * 遍历所有键值对,效果等同于: + *
{@code
+	 * for (Entry> entry : entrySet()) {
+	 * 	K key = entry.getKey();
+	 * 	Collection coll = entry.getValues();
+	 * 	for (V val : coll) {
+	 * 		consumer.accept(key, val);
+	 * 	}
+	 * }
+	 * }
+ * + * @param consumer 操作 + */ + default void allForEach(BiConsumer consumer) { + forEach((k, coll) -> coll.forEach(v -> consumer.accept(k, v))); + } + + /** + * 获取所有的值,效果等同于: + *
{@code
+	 * List results = new ArrayList<>();
+	 * for (Collection coll : values()) {
+	 * 	results.addAll(coll);
+	 * }
+	 * }
+ * + * @return 值 + */ + default Collection allValues() { + return values().stream() + .flatMap(Collection::stream) + .collect(Collectors.toList()); + } + +} diff --git a/hutool-core/src/main/java/cn/hutool/core/map/multi/SetValueMap.java b/hutool-core/src/main/java/cn/hutool/core/map/multi/SetValueMap.java index 05a4fdcac..7b76611c7 100644 --- a/hutool-core/src/main/java/cn/hutool/core/map/multi/SetValueMap.java +++ b/hutool-core/src/main/java/cn/hutool/core/map/multi/SetValueMap.java @@ -1,10 +1,7 @@ package cn.hutool.core.map.multi; -import java.util.Collection; -import java.util.HashMap; -import java.util.LinkedHashSet; -import java.util.Map; -import java.util.Set; +import java.util.*; +import java.util.function.Supplier; /** * 值作为集合Set(LinkedHashSet)的Map实现,通过调用putValue可以在相同key时加入多个值,多个值用集合表示 @@ -15,59 +12,40 @@ import java.util.Set; * @param 值类型 * @since 4.3.3 */ -public class SetValueMap extends AbsCollValueMap> { +public class SetValueMap extends AbsCollValueMap { private static final long serialVersionUID = 6044017508487827899L; // ------------------------------------------------------------------------- Constructor start + /** - * 构造 + * 基于{@code mapFactory}创建一个值为{@link Set}的多值映射集合 + * + * @param mapFactory 创建集合的工厂反方 + */ + public SetValueMap(Supplier>> mapFactory) { + super(mapFactory); + } + + /** + * 基于{@link HashMap}创建一个值为{@link Set}的多值映射集合 + * + * @param map 提供数据的原始集合 + */ + public SetValueMap(Map> map) { + super(map); + } + + /** + * 基于{@link HashMap}创建一个值为{@link Set}的多值映射集合 */ public SetValueMap() { - this(DEFAULT_INITIAL_CAPACITY); } - /** - * 构造 - * - * @param initialCapacity 初始大小 - */ - public SetValueMap(final int initialCapacity) { - this(initialCapacity, DEFAULT_LOAD_FACTOR); - } - - /** - * 构造 - * - * @param m Map - */ - public SetValueMap(final Map> m) { - this(DEFAULT_LOAD_FACTOR, m); - } - - /** - * 构造 - * - * @param loadFactor 加载因子 - * @param m Map - */ - public SetValueMap(final float loadFactor, final Map> m) { - this(m.size(), loadFactor); - this.putAllValues(m); - } - - /** - * 构造 - * - * @param initialCapacity 初始大小 - * @param loadFactor 加载因子 - */ - public SetValueMap(final int initialCapacity, final float loadFactor) { - super(new HashMap<>(initialCapacity, loadFactor)); - } // ------------------------------------------------------------------------- Constructor end @Override protected Set createCollection() { return new LinkedHashSet<>(DEFAULT_COLLECTION_INITIAL_CAPACITY); } + } diff --git a/hutool-core/src/main/java/cn/hutool/core/net/multipart/MultipartFormData.java b/hutool-core/src/main/java/cn/hutool/core/net/multipart/MultipartFormData.java index 34ff7521d..dfb173352 100644 --- a/hutool-core/src/main/java/cn/hutool/core/net/multipart/MultipartFormData.java +++ b/hutool-core/src/main/java/cn/hutool/core/net/multipart/MultipartFormData.java @@ -3,11 +3,12 @@ package cn.hutool.core.net.multipart; import cn.hutool.core.collection.CollUtil; import cn.hutool.core.convert.Convert; import cn.hutool.core.map.multi.ListValueMap; +import cn.hutool.core.map.multi.MultiValueMap; import java.io.IOException; import java.io.InputStream; import java.nio.charset.Charset; -import java.util.List; +import java.util.Collection; import java.util.Map; import java.util.Set; @@ -20,9 +21,9 @@ import java.util.Set; public class MultipartFormData { /** 请求参数 */ - private final ListValueMap requestParameters = new ListValueMap<>(); + private final MultiValueMap requestParameters = new ListValueMap<>(); /** 请求文件 */ - private final ListValueMap requestFiles = new ListValueMap<>(); + private final MultiValueMap requestFiles = new ListValueMap<>(); /** 上传选项 */ private final UploadSetting setting; @@ -101,9 +102,9 @@ public class MultipartFormData { * @return null未找到,否则返回值 */ public String getParam(final String paramName) { - final List values = getListParam(paramName); + final Collection values = getListParam(paramName); if (CollUtil.isNotEmpty(values)) { - return values.get(0); + return CollUtil.get(values, 0); } return null; } @@ -122,7 +123,7 @@ public class MultipartFormData { * @return 数组表单值 */ public String[] getArrayParam(final String paramName) { - final List listParam = getListParam(paramName); + final Collection listParam = getListParam(paramName); if(null != listParam){ return listParam.toArray(new String[0]); } @@ -136,7 +137,7 @@ public class MultipartFormData { * @return 数组表单值 * @since 5.3.0 */ - public List getListParam(final String paramName) { + public Collection getListParam(final String paramName) { return requestParameters.get(paramName); } @@ -154,7 +155,7 @@ public class MultipartFormData { * * @return 所有属性的集合 */ - public ListValueMap getParamListMap() { + public MultiValueMap getParamListMap() { return this.requestParameters; } @@ -181,7 +182,7 @@ public class MultipartFormData { * @return 上传的文件列表 */ public UploadFile[] getFiles(final String paramName) { - final List fileList = getFileList(paramName); + final Collection fileList = getFileList(paramName); if(null != fileList){ return fileList.toArray(new UploadFile[0]); } @@ -196,7 +197,7 @@ public class MultipartFormData { * @return 上传的文件列表 * @since 5.3.0 */ - public List getFileList(final String paramName) { + public Collection getFileList(final String paramName) { return requestFiles.get(paramName); } @@ -223,7 +224,7 @@ public class MultipartFormData { * * @return 文件映射 */ - public ListValueMap getFileListValueMap() { + public MultiValueMap getFileListValueMap() { return this.requestFiles; } diff --git a/hutool-core/src/test/java/cn/hutool/core/map/CollectionValueMapTest.java b/hutool-core/src/test/java/cn/hutool/core/map/CollectionValueMapTest.java new file mode 100644 index 000000000..e624bcc6b --- /dev/null +++ b/hutool-core/src/test/java/cn/hutool/core/map/CollectionValueMapTest.java @@ -0,0 +1,160 @@ +package cn.hutool.core.map; + +import cn.hutool.core.map.multi.CollectionValueMap; +import cn.hutool.core.map.multi.MultiValueMap; +import cn.hutool.core.text.StrUtil; +import org.junit.Assert; +import org.junit.Test; + +import java.util.*; + +public class CollectionValueMapTest { + + @Test + public void putTest() { + MultiValueMap map = new CollectionValueMap<>(); + Assert.assertNull(map.put(1, Arrays.asList("a", "b"))); + Collection collection = map.put(1, Arrays.asList("c", "d")); + Assert.assertEquals(Arrays.asList("a", "b"), collection); + } + + @Test + public void putAllTest() { + MultiValueMap map = new CollectionValueMap<>(); + Map> source = new HashMap<>(); + source.put(1, Arrays.asList("a", "b", "c")); + map.putAll(source); + Assert.assertEquals(1, map.size()); + Assert.assertEquals(Arrays.asList("a", "b", "c"), map.get(1)); + } + + @Test + public void putValueTest() { + MultiValueMap map = new CollectionValueMap<>(); + Assert.assertTrue(map.putValue(1, "a")); + Assert.assertTrue(map.putValue(1, "b")); + Assert.assertTrue(map.putValue(1, "c")); + Assert.assertEquals(1, map.size()); + Assert.assertEquals(Arrays.asList("a", "b", "c"), map.get(1)); + } + + @Test + public void putAllValueTest() { + MultiValueMap map = new CollectionValueMap<>(); + Assert.assertTrue(map.putAllValues(1, Arrays.asList("a", "b", "c"))); + Assert.assertEquals(1, map.size()); + Assert.assertEquals(Arrays.asList("a", "b", "c"), map.get(1)); + + Map> source = new HashMap<>(); + Assert.assertTrue(map.putValue(1, "e")); + Assert.assertTrue(map.putValue(1, "f")); + map.putAllValues(source); + Assert.assertEquals(Arrays.asList("a", "b", "c", "e", "f"), map.get(1)); + } + + @Test + public void putValuesTest() { + MultiValueMap map = new CollectionValueMap<>(); + Assert.assertTrue(map.putValues(1, "a", "b", "c")); + Assert.assertEquals(Arrays.asList("a", "b", "c"), map.get(1)); + } + + @Test + public void testFilterAllValues() { + MultiValueMap map = new CollectionValueMap<>(); + Assert.assertTrue(map.putValues(1, "a", "b", "c")); + Assert.assertTrue(map.putValues(2, "a", "b", "c")); + + Assert.assertEquals(map, map.filterAllValues((k, v) -> StrUtil.equals(v, "a"))); + Assert.assertEquals(Collections.singletonList("a"), map.getValues(1)); + Assert.assertEquals(Collections.singletonList("a"), map.getValues(2)); + + Assert.assertEquals(map, map.filterAllValues(v -> !StrUtil.equals(v, "a"))); + Assert.assertEquals(Collections.emptyList(), map.getValues(1)); + Assert.assertEquals(Collections.emptyList(), map.getValues(2)); + } + + @Test + public void testReplaceAllValues() { + MultiValueMap map = new CollectionValueMap<>(); + Assert.assertTrue(map.putValues(1, "a", "b", "c")); + Assert.assertTrue(map.putValues(2, "a", "b", "c")); + + Assert.assertEquals(map, map.replaceAllValues((k, v) -> v + "2")); + Assert.assertEquals(Arrays.asList("a2", "b2", "c2"), map.getValues(1)); + Assert.assertEquals(Arrays.asList("a2", "b2", "c2"), map.getValues(2)); + + Assert.assertEquals(map, map.replaceAllValues(v -> v + "3")); + Assert.assertEquals(Arrays.asList("a23", "b23", "c23"), map.getValues(1)); + Assert.assertEquals(Arrays.asList("a23", "b23", "c23"), map.getValues(2)); + } + + @Test + public void removeValueTest() { + MultiValueMap map = new CollectionValueMap<>(); + map.putValues(1, "a", "b", "c"); + Assert.assertFalse(map.removeValue(1, "d")); + Assert.assertTrue(map.removeValue(1, "c")); + Assert.assertEquals(Arrays.asList("a", "b"), map.get(1)); + } + + @Test + public void removeAllValuesTest() { + MultiValueMap map = new CollectionValueMap<>(); + map.putValues(1, "a", "b", "c"); + Assert.assertFalse(map.removeAllValues(1, Arrays.asList("e", "f"))); + Assert.assertTrue(map.removeAllValues(1, Arrays.asList("b", "c"))); + Assert.assertEquals(Collections.singletonList("a"), map.get(1)); + } + + @Test + public void removeValuesTest() { + MultiValueMap map = new CollectionValueMap<>(); + map.putValues(1, "a", "b", "c"); + Assert.assertFalse(map.removeValues(1, "e", "f")); + Assert.assertTrue(map.removeValues(1, "b", "c")); + Assert.assertEquals(Collections.singletonList("a"), map.get(1)); + } + + @Test + public void getValuesTest() { + MultiValueMap map = new CollectionValueMap<>(); + map.putValues(1, "a", "b", "c"); + Assert.assertEquals(Collections.emptyList(), map.getValues(2)); + Assert.assertEquals(Arrays.asList("a", "b", "c"), map.getValues(1)); + } + + @Test + public void sizeTest() { + MultiValueMap map = new CollectionValueMap<>(); + map.putValues(1, "a", "b", "c"); + Assert.assertEquals(0, map.size(2)); + Assert.assertEquals(3, map.size(1)); + } + + @Test + public void allForEachTest() { + MultiValueMap map = new CollectionValueMap<>(); + map.putValues(1, "a", "b", "c"); + List keys = new ArrayList<>(); + List values = new ArrayList<>(); + map.allForEach((k, v) -> { + keys.add(k); + values.add(v); + }); + Assert.assertEquals(Arrays.asList(1, 1, 1), keys); + Assert.assertEquals(Arrays.asList("a", "b", "c"), values); + } + + @Test + public void allValuesTest() { + MultiValueMap map = new CollectionValueMap<>(new LinkedHashMap<>()); + map.putAllValues(1, Arrays.asList("a", "b", "c")); + map.putAllValues(2, Arrays.asList("d", "e")); + Assert.assertEquals( + Arrays.asList("a", "b", "c", "d", "e"), + map.allValues() + ); + } + +} diff --git a/hutool-core/src/test/java/cn/hutool/core/map/ListValueMapTest.java b/hutool-core/src/test/java/cn/hutool/core/map/ListValueMapTest.java new file mode 100644 index 000000000..b607127c5 --- /dev/null +++ b/hutool-core/src/test/java/cn/hutool/core/map/ListValueMapTest.java @@ -0,0 +1,160 @@ +package cn.hutool.core.map; + +import cn.hutool.core.map.multi.ListValueMap; +import cn.hutool.core.map.multi.MultiValueMap; +import cn.hutool.core.text.StrUtil; +import org.junit.Assert; +import org.junit.Test; + +import java.util.*; + +public class ListValueMapTest { + + @Test + public void putTest() { + MultiValueMap map = new ListValueMap<>(); + Assert.assertNull(map.put(1, Arrays.asList("a", "b"))); + Collection collection = map.put(1, Arrays.asList("c", "d")); + Assert.assertEquals(Arrays.asList("a", "b"), collection); + } + + @Test + public void putAllTest() { + MultiValueMap map = new ListValueMap<>(); + Map> source = new HashMap<>(); + source.put(1, Arrays.asList("a", "b", "c")); + map.putAll(source); + Assert.assertEquals(1, map.size()); + Assert.assertEquals(Arrays.asList("a", "b", "c"), map.get(1)); + } + + @Test + public void putValueTest() { + MultiValueMap map = new ListValueMap<>(); + Assert.assertTrue(map.putValue(1, "a")); + Assert.assertTrue(map.putValue(1, "b")); + Assert.assertTrue(map.putValue(1, "c")); + Assert.assertEquals(1, map.size()); + Assert.assertEquals(Arrays.asList("a", "b", "c"), map.get(1)); + } + + @Test + public void putAllValueTest() { + MultiValueMap map = new ListValueMap<>(); + Assert.assertTrue(map.putAllValues(1, Arrays.asList("a", "b", "c"))); + Assert.assertEquals(1, map.size()); + Assert.assertEquals(Arrays.asList("a", "b", "c"), map.get(1)); + + Map> source = new HashMap<>(); + Assert.assertTrue(map.putValue(1, "e")); + Assert.assertTrue(map.putValue(1, "f")); + map.putAllValues(source); + Assert.assertEquals(Arrays.asList("a", "b", "c", "e", "f"), map.get(1)); + } + + @Test + public void putValuesTest() { + MultiValueMap map = new ListValueMap<>(); + Assert.assertTrue(map.putValues(1, "a", "b", "c")); + Assert.assertEquals(Arrays.asList("a", "b", "c"), map.get(1)); + } + + @Test + public void removeValueTest() { + MultiValueMap map = new ListValueMap<>(); + map.putValues(1, "a", "b", "c"); + Assert.assertFalse(map.removeValue(1, "d")); + Assert.assertTrue(map.removeValue(1, "c")); + Assert.assertEquals(Arrays.asList("a", "b"), map.get(1)); + } + + @Test + public void removeAllValuesTest() { + MultiValueMap map = new ListValueMap<>(); + map.putValues(1, "a", "b", "c"); + Assert.assertFalse(map.removeAllValues(1, Arrays.asList("e", "f"))); + Assert.assertTrue(map.removeAllValues(1, Arrays.asList("b", "c"))); + Assert.assertEquals(Collections.singletonList("a"), map.get(1)); + } + + @Test + public void removeValuesTest() { + MultiValueMap map = new ListValueMap<>(); + map.putValues(1, "a", "b", "c"); + Assert.assertFalse(map.removeValues(1, "e", "f")); + Assert.assertTrue(map.removeValues(1, "b", "c")); + Assert.assertEquals(Collections.singletonList("a"), map.get(1)); + } + + @Test + public void testFilterAllValues() { + MultiValueMap map = new ListValueMap<>(); + Assert.assertTrue(map.putValues(1, "a", "b", "c")); + Assert.assertTrue(map.putValues(2, "a", "b", "c")); + + Assert.assertEquals(map, map.filterAllValues((k, v) -> StrUtil.equals(v, "a"))); + Assert.assertEquals(Collections.singletonList("a"), map.getValues(1)); + Assert.assertEquals(Collections.singletonList("a"), map.getValues(2)); + + Assert.assertEquals(map, map.filterAllValues(v -> !StrUtil.equals(v, "a"))); + Assert.assertEquals(Collections.emptyList(), map.getValues(1)); + Assert.assertEquals(Collections.emptyList(), map.getValues(2)); + } + + @Test + public void testReplaceAllValues() { + MultiValueMap map = new ListValueMap<>(); + Assert.assertTrue(map.putValues(1, "a", "b", "c")); + Assert.assertTrue(map.putValues(2, "a", "b", "c")); + + Assert.assertEquals(map, map.replaceAllValues((k, v) -> v + "2")); + Assert.assertEquals(Arrays.asList("a2", "b2", "c2"), map.getValues(1)); + Assert.assertEquals(Arrays.asList("a2", "b2", "c2"), map.getValues(2)); + + Assert.assertEquals(map, map.replaceAllValues(v -> v + "3")); + Assert.assertEquals(Arrays.asList("a23", "b23", "c23"), map.getValues(1)); + Assert.assertEquals(Arrays.asList("a23", "b23", "c23"), map.getValues(2)); + } + + @Test + public void getValuesTest() { + MultiValueMap map = new ListValueMap<>(); + map.putValues(1, "a", "b", "c"); + Assert.assertEquals(Collections.emptyList(), map.getValues(2)); + Assert.assertEquals(Arrays.asList("a", "b", "c"), map.getValues(1)); + } + + @Test + public void sizeTest() { + MultiValueMap map = new ListValueMap<>(); + map.putValues(1, "a", "b", "c"); + Assert.assertEquals(0, map.size(2)); + Assert.assertEquals(3, map.size(1)); + } + + @Test + public void allForEachTest() { + MultiValueMap map = new ListValueMap<>(); + map.putValues(1, "a", "b", "c"); + List keys = new ArrayList<>(); + List values = new ArrayList<>(); + map.allForEach((k, v) -> { + keys.add(k); + values.add(v); + }); + Assert.assertEquals(Arrays.asList(1, 1, 1), keys); + Assert.assertEquals(Arrays.asList("a", "b", "c"), values); + } + + @Test + public void allValuesTest() { + MultiValueMap map = new ListValueMap<>(); + map.putAllValues(1, Arrays.asList("a", "b", "c")); + map.putAllValues(2, Arrays.asList("d", "e")); + Assert.assertEquals( + Arrays.asList("a", "b", "c", "d", "e"), + map.allValues() + ); + } + +} diff --git a/hutool-core/src/test/java/cn/hutool/core/map/SetValueMapTest.java b/hutool-core/src/test/java/cn/hutool/core/map/SetValueMapTest.java new file mode 100644 index 000000000..e41e5f872 --- /dev/null +++ b/hutool-core/src/test/java/cn/hutool/core/map/SetValueMapTest.java @@ -0,0 +1,162 @@ +package cn.hutool.core.map; + +import cn.hutool.core.map.multi.MultiValueMap; +import cn.hutool.core.map.multi.SetValueMap; +import cn.hutool.core.text.StrUtil; +import org.junit.Assert; +import org.junit.Test; + +import java.util.*; + +public class SetValueMapTest { + + @Test + public void putTest() { + MultiValueMap map = new SetValueMap<>(); + Assert.assertNull(map.put(1, Arrays.asList("a", "b"))); + Collection collection = map.put(1, Arrays.asList("c", "d")); + Assert.assertEquals(Arrays.asList("a", "b"), collection); + } + + @Test + public void putAllTest() { + MultiValueMap map = new SetValueMap<>(); + Map> source = new HashMap<>(); + source.put(1, Arrays.asList("a", "b", "c")); + map.putAll(source); + Assert.assertEquals(1, map.size()); + Assert.assertEquals(Arrays.asList("a", "b", "c"), map.get(1)); + } + + @Test + public void putValueTest() { + MultiValueMap map = new SetValueMap<>(); + Assert.assertTrue(map.putValue(1, "a")); + Assert.assertTrue(map.putValue(1, "b")); + Assert.assertTrue(map.putValue(1, "c")); + Assert.assertEquals(1, map.size()); + Assert.assertEquals(new HashSet<>(Arrays.asList("a", "b", "c")), map.get(1)); + } + + @Test + public void putAllValueTest() { + MultiValueMap map = new SetValueMap<>(); + Assert.assertTrue(map.putAllValues(1, Arrays.asList("a", "b", "c"))); + Assert.assertEquals(1, map.size()); + Assert.assertEquals(new HashSet<>(Arrays.asList("a", "b", "c")), map.get(1)); + + Map> source = new HashMap<>(); + Assert.assertTrue(map.putValue(1, "e")); + Assert.assertFalse(map.putValue(1, "e")); + Assert.assertTrue(map.putValue(1, "f")); + Assert.assertFalse(map.putValue(1, "f")); + map.putAllValues(source); + Assert.assertEquals(new HashSet<>(Arrays.asList("a", "b", "c", "e", "f")), map.get(1)); + } + + @Test + public void putValuesTest() { + MultiValueMap map = new SetValueMap<>(); + Assert.assertTrue(map.putValues(1, "a", "b", "c")); + Assert.assertEquals(new HashSet<>(Arrays.asList("a", "b", "c")), map.get(1)); + } + + @Test + public void removeValueTest() { + MultiValueMap map = new SetValueMap<>(); + map.putValues(1, "a", "b", "c"); + Assert.assertFalse(map.removeValue(1, "d")); + Assert.assertTrue(map.removeValue(1, "c")); + Assert.assertEquals(new HashSet<>(Arrays.asList("a", "b")), map.get(1)); + } + + @Test + public void removeAllValuesTest() { + MultiValueMap map = new SetValueMap<>(); + map.putValues(1, "a", "b", "c"); + Assert.assertFalse(map.removeAllValues(1, Arrays.asList("e", "f"))); + Assert.assertTrue(map.removeAllValues(1, Arrays.asList("b", "c"))); + Assert.assertEquals(Collections.singleton("a"), map.get(1)); + } + + @Test + public void removeValuesTest() { + MultiValueMap map = new SetValueMap<>(); + map.putValues(1, "a", "b", "c"); + Assert.assertFalse(map.removeValues(1, "e", "f")); + Assert.assertTrue(map.removeValues(1, "b", "c")); + Assert.assertEquals(Collections.singleton("a"), map.get(1)); + } + + @Test + public void testFilterAllValues() { + MultiValueMap map = new SetValueMap<>(); + Assert.assertTrue(map.putValues(1, "a", "b", "c")); + Assert.assertTrue(map.putValues(2, "a", "b", "c")); + + Assert.assertEquals(map, map.filterAllValues((k, v) -> StrUtil.equals(v, "a"))); + Assert.assertEquals(Collections.singleton("a"), map.getValues(1)); + Assert.assertEquals(Collections.singleton("a"), map.getValues(2)); + + Assert.assertEquals(map, map.filterAllValues(v -> !StrUtil.equals(v, "a"))); + Assert.assertEquals(Collections.emptySet(), map.getValues(1)); + Assert.assertEquals(Collections.emptySet(), map.getValues(2)); + } + + @Test + public void testReplaceAllValues() { + MultiValueMap map = new SetValueMap<>(); + Assert.assertTrue(map.putValues(1, "a", "b", "c")); + Assert.assertTrue(map.putValues(2, "a", "b", "c")); + + Assert.assertEquals(map, map.replaceAllValues((k, v) -> v + "2")); + Assert.assertEquals(new HashSet<>(Arrays.asList("a2", "b2", "c2")), map.getValues(1)); + Assert.assertEquals(new HashSet<>(Arrays.asList("a2", "b2", "c2")), map.getValues(2)); + + Assert.assertEquals(map, map.replaceAllValues(v -> v + "3")); + Assert.assertEquals(new HashSet<>(Arrays.asList("a23", "b23", "c23")), map.getValues(1)); + Assert.assertEquals(new HashSet<>(Arrays.asList("a23", "b23", "c23")), map.getValues(2)); + } + + @Test + public void getValuesTest() { + MultiValueMap map = new SetValueMap<>(); + map.putValues(1, "a", "b", "c"); + Assert.assertEquals(Collections.emptyList(), map.getValues(2)); + Assert.assertEquals(new HashSet<>(Arrays.asList("a", "b", "c")), map.getValues(1)); + } + + @Test + public void sizeTest() { + MultiValueMap map = new SetValueMap<>(); + map.putValues(1, "a", "b", "c"); + Assert.assertEquals(0, map.size(2)); + Assert.assertEquals(3, map.size(1)); + } + + @Test + public void allForEachTest() { + MultiValueMap map = new SetValueMap<>(); + map.putValues(1, "a", "b", "c"); + List keys = new ArrayList<>(); + List values = new ArrayList<>(); + map.allForEach((k, v) -> { + keys.add(k); + values.add(v); + }); + Assert.assertEquals(Arrays.asList(1, 1, 1), keys); + Assert.assertEquals(Arrays.asList("a", "b", "c"), values); + } + + @Test + public void allValuesTest() { + MultiValueMap map = new SetValueMap<>(); + map.putAllValues(1, Arrays.asList("a", "b", "c")); + map.putAllValues(2, Arrays.asList("d", "e")); + Assert.assertEquals( + Arrays.asList("a", "b", "c", "d", "e"), + map.allValues() + ); + } + +} From f7ebc9f0fade8a5cab2e5c6e3fc9d6e14f311c48 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=B4=BE=E6=AD=A3=E5=85=A8?= Date: Tue, 30 Aug 2022 12:06:57 +0800 Subject: [PATCH 02/13] =?UTF-8?q?Zodiac=20test=E5=AE=8C=E5=96=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../test/java/cn/hutool/core/date/ZodiacTest.java | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/hutool-core/src/test/java/cn/hutool/core/date/ZodiacTest.java b/hutool-core/src/test/java/cn/hutool/core/date/ZodiacTest.java index dac1329c3..bbe1eac5c 100644 --- a/hutool-core/src/test/java/cn/hutool/core/date/ZodiacTest.java +++ b/hutool-core/src/test/java/cn/hutool/core/date/ZodiacTest.java @@ -3,6 +3,9 @@ package cn.hutool.core.date; import org.junit.Assert; import org.junit.Test; +import java.util.Calendar; +import java.util.Date; + public class ZodiacTest { @Test @@ -10,6 +13,11 @@ public class ZodiacTest { Assert.assertEquals("摩羯座", Zodiac.getZodiac(Month.JANUARY, 19)); Assert.assertEquals("水瓶座", Zodiac.getZodiac(Month.JANUARY, 20)); Assert.assertEquals("巨蟹座", Zodiac.getZodiac(6, 17)); + Calendar calendar = Calendar.getInstance(); + calendar.set(2022, Calendar.JULY, 17); + Assert.assertEquals("巨蟹座", Zodiac.getZodiac(calendar.getTime())); + Assert.assertEquals("巨蟹座", Zodiac.getZodiac(calendar)); + Assert.assertNull(Zodiac.getZodiac((Calendar) null)); } @Test @@ -17,5 +25,11 @@ public class ZodiacTest { Assert.assertEquals("狗", Zodiac.getChineseZodiac(1994)); Assert.assertEquals("狗", Zodiac.getChineseZodiac(2018)); Assert.assertEquals("猪", Zodiac.getChineseZodiac(2019)); + Calendar calendar = Calendar.getInstance(); + calendar.set(2022, Calendar.JULY, 17); + Assert.assertEquals("虎", Zodiac.getChineseZodiac(calendar.getTime())); + Assert.assertEquals("虎", Zodiac.getChineseZodiac(calendar)); + Assert.assertNull(Zodiac.getChineseZodiac(1899)); + Assert.assertNull(Zodiac.getChineseZodiac((Calendar) null)); } } From a09dd3966b918453264a44bb7fc36fc779d2820b Mon Sep 17 00:00:00 2001 From: Husky <2466896229@qq.com> Date: Tue, 30 Aug 2022 14:43:39 +0800 Subject: [PATCH 03/13] =?UTF-8?q?#I5OSFC=20=E4=BF=AE=E5=A4=8D=EF=BC=8C?= =?UTF-8?q?=E9=94=99=E8=AF=AF=E5=BC=82=E5=B8=B8=E4=BF=A1=E6=81=AF=E4=BF=AE?= =?UTF-8?q?=E6=94=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../poi/excel/reader/MapSheetReader.java | 4 +++- .../cn/hutool/poi/excel/ExcelReadTest.java | 17 +++++++++++++++++ hutool-poi/src/test/resources/read.xlsx | Bin 0 -> 9728 bytes 3 files changed, 20 insertions(+), 1 deletion(-) create mode 100644 hutool-poi/src/test/resources/read.xlsx diff --git a/hutool-poi/src/main/java/cn/hutool/poi/excel/reader/MapSheetReader.java b/hutool-poi/src/main/java/cn/hutool/poi/excel/reader/MapSheetReader.java index 9a0f65d9f..ec8a2fc00 100644 --- a/hutool-poi/src/main/java/cn/hutool/poi/excel/reader/MapSheetReader.java +++ b/hutool-poi/src/main/java/cn/hutool/poi/excel/reader/MapSheetReader.java @@ -44,7 +44,9 @@ public class MapSheetReader extends AbstractSheetReader if (headerRowIndex < firstRowNum) { throw new IndexOutOfBoundsException(StrUtil.format("Header row index {} is lower than first row index {}.", headerRowIndex, firstRowNum)); } else if (headerRowIndex > lastRowNum) { - throw new IndexOutOfBoundsException(StrUtil.format("Header row index {} is greater than last row index {}.", headerRowIndex, firstRowNum)); + throw new IndexOutOfBoundsException(StrUtil.format("Header row index {} is greater than last row index {}.", headerRowIndex, lastRowNum)); + } else if (startRowIndex > lastRowNum) { + throw new IndexOutOfBoundsException(StrUtil.format("startRowIndex row index {} is greater than last row index {}.", startRowIndex, lastRowNum)); } final int startRowIndex = Math.max(this.startRowIndex, firstRowNum);// 读取起始行(包含) final int endRowIndex = Math.min(this.endRowIndex, lastRowNum);// 读取结束行(包含) diff --git a/hutool-poi/src/test/java/cn/hutool/poi/excel/ExcelReadTest.java b/hutool-poi/src/test/java/cn/hutool/poi/excel/ExcelReadTest.java index b50218aac..f88e2e236 100644 --- a/hutool-poi/src/test/java/cn/hutool/poi/excel/ExcelReadTest.java +++ b/hutool-poi/src/test/java/cn/hutool/poi/excel/ExcelReadTest.java @@ -251,4 +251,21 @@ public class ExcelReadTest { final ExcelReader reader = ExcelUtil.getReader(ResourceUtil.getStream("read_row_npe.xlsx")); reader.readColumn(0, 1); } + + @Test + public void readIssueTest() { + //https://gitee.com/dromara/hutool/issues/I5OSFC + final ExcelReader reader = ExcelUtil.getReader(ResourceUtil.getStream("read.xlsx")); + final List> read = reader.read(1,2,2); + for (Map map : read) { + Console.log(map); + } + //超出lastIndex 抛出相应提示:startRowIndex row index 4 is greater than last row index 2. + //而非:Illegal Capacity: -1 + try { + final List> readGreaterIndex = reader.read(1,4,4); + } catch (Exception e) { + Console.log(e.toString()); + } + } } diff --git a/hutool-poi/src/test/resources/read.xlsx b/hutool-poi/src/test/resources/read.xlsx new file mode 100644 index 0000000000000000000000000000000000000000..10eda0923ba8bce554ad6ca3a6a29e319f454f35 GIT binary patch literal 9728 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 P8t5et>T@#D Date: Sun, 28 Aug 2022 15:06:36 +0800 Subject: [PATCH 04/13] =?UTF-8?q?=E5=AE=8C=E5=96=84annotation=E5=8C=85?= =?UTF-8?q?=E7=9B=B8=E5=85=B3=E6=B5=8B=E8=AF=95=E7=94=A8=E4=BE=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../core/annotation/AnnotationForTest.java | 29 ---- .../core/annotation/AnnotationUtilTest.java | 141 ++++++++++++++---- .../CombinationAnnotationElementTest.java | 60 ++++++++ .../annotation/RepeatAnnotationForTest.java | 16 -- 4 files changed, 174 insertions(+), 72 deletions(-) delete mode 100755 hutool-core/src/test/java/cn/hutool/core/annotation/AnnotationForTest.java create mode 100644 hutool-core/src/test/java/cn/hutool/core/annotation/CombinationAnnotationElementTest.java delete mode 100755 hutool-core/src/test/java/cn/hutool/core/annotation/RepeatAnnotationForTest.java diff --git a/hutool-core/src/test/java/cn/hutool/core/annotation/AnnotationForTest.java b/hutool-core/src/test/java/cn/hutool/core/annotation/AnnotationForTest.java deleted file mode 100755 index f109e836d..000000000 --- a/hutool-core/src/test/java/cn/hutool/core/annotation/AnnotationForTest.java +++ /dev/null @@ -1,29 +0,0 @@ -package cn.hutool.core.annotation; - -import java.lang.annotation.ElementType; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.lang.annotation.Target; - -/** - * 用于单元测试的注解类
- * 注解类相关说明见:https://www.cnblogs.com/xdp-gacl/p/3622275.html - * - * @author looly - */ -// Retention注解决定MyAnnotation注解的生命周期 -@Retention(RetentionPolicy.RUNTIME) -// Target注解决定MyAnnotation注解可以加在哪些成分上,如加在类身上,或者属性身上,或者方法身上等成分 -@Target({ ElementType.METHOD, ElementType.TYPE }) -public @interface AnnotationForTest { - - /** - * 注解的默认属性值 - * - * @return 属性值 - */ - String value() default ""; - - @Alias("value") - String retry() default ""; -} diff --git a/hutool-core/src/test/java/cn/hutool/core/annotation/AnnotationUtilTest.java b/hutool-core/src/test/java/cn/hutool/core/annotation/AnnotationUtilTest.java index 3032ed5ca..994d3e2d1 100755 --- a/hutool-core/src/test/java/cn/hutool/core/annotation/AnnotationUtilTest.java +++ b/hutool-core/src/test/java/cn/hutool/core/annotation/AnnotationUtilTest.java @@ -1,49 +1,136 @@ package cn.hutool.core.annotation; +import cn.hutool.core.util.ObjUtil; import org.junit.Assert; import org.junit.Test; -import java.lang.annotation.Annotation; +import java.lang.annotation.*; +import java.util.Map; +/** + * test for {@link AnnotationUtil} + */ public class AnnotationUtilTest { @Test - public void getCombinationAnnotationsTest(){ - final Annotation[] annotations = AnnotationUtil.getAnnotations(ClassWithAnnotation.class, true); - Assert.assertNotNull(annotations); + public void testToCombination() { + CombinationAnnotationElement element = AnnotationUtil.toCombination(ClassForTest.class); + Assert.assertEquals(2, element.getAnnotations().length); + } + + @Test + public void testGetAnnotations() { + Annotation[] annotations = AnnotationUtil.getAnnotations(ClassForTest.class, true); Assert.assertEquals(2, annotations.length); - } - - @Test - public void getCombinationAnnotationsWithClassTest(){ - final AnnotationForTest[] annotations = AnnotationUtil.getCombinationAnnotations(ClassWithAnnotation.class, AnnotationForTest.class); - Assert.assertNotNull(annotations); + annotations = AnnotationUtil.getAnnotations(ClassForTest.class, false); Assert.assertEquals(1, annotations.length); - Assert.assertEquals("测试", annotations[0].value()); } @Test - public void getAnnotationValueTest() { - final Object value = AnnotationUtil.getAnnotationValue(ClassWithAnnotation.class, AnnotationForTest.class); - Assert.assertEquals("测试", value); - + public void testGetCombinationAnnotations() { + MetaAnnotationForTest[] annotations = AnnotationUtil.getCombinationAnnotations(ClassForTest.class, MetaAnnotationForTest.class); + Assert.assertEquals(1, annotations.length); } @Test - public void getAnnotationSyncAlias() { - // 直接获取 - Assert.assertEquals("", ClassWithAnnotation.class.getAnnotation(AnnotationForTest.class).retry()); + public void testAnnotations() { + MetaAnnotationForTest[] annotations1 = AnnotationUtil.getAnnotations(ClassForTest.class, false, MetaAnnotationForTest.class); + Assert.assertEquals(0, annotations1.length); + annotations1 = AnnotationUtil.getAnnotations(ClassForTest.class, true, MetaAnnotationForTest.class); + Assert.assertEquals(1, annotations1.length); - // 加别名适配 - final AnnotationForTest annotation = AnnotationUtil.getAnnotationAlias(ClassWithAnnotation.class, AnnotationForTest.class); - Assert.assertEquals("测试", annotation.retry()); + Annotation[] annotations2 = AnnotationUtil.getAnnotations( + ClassForTest.class, false, t -> ObjUtil.equals(t.annotationType(), MetaAnnotationForTest.class) + ); + Assert.assertEquals(0, annotations2.length); + annotations2 = AnnotationUtil.getAnnotations( + ClassForTest.class, true, t -> ObjUtil.equals(t.annotationType(), MetaAnnotationForTest.class) + ); + Assert.assertEquals(1, annotations2.length); } - @AnnotationForTest("测试") - @RepeatAnnotationForTest - static class ClassWithAnnotation{ - public void test(){ - - } + @Test + public void testGetAnnotation() { + MetaAnnotationForTest annotation = AnnotationUtil.getAnnotation(ClassForTest.class, MetaAnnotationForTest.class); + Assert.assertNotNull(annotation); } + + @Test + public void testHasAnnotation() { + Assert.assertTrue(AnnotationUtil.hasAnnotation(ClassForTest.class, MetaAnnotationForTest.class)); + } + + @Test + public void testGetAnnotationValue() { + AnnotationForTest annotation = ClassForTest.class.getAnnotation(AnnotationForTest.class); + Assert.assertEquals(annotation.value(), AnnotationUtil.getAnnotationValue(ClassForTest.class, AnnotationForTest.class)); + Assert.assertEquals(annotation.value(), AnnotationUtil.getAnnotationValue(ClassForTest.class, AnnotationForTest.class, "value")); + Assert.assertNull(AnnotationUtil.getAnnotationValue(ClassForTest.class, AnnotationForTest.class, "property")); + } + + @Test + public void testGetAnnotationValueMap() { + AnnotationForTest annotation = ClassForTest.class.getAnnotation(AnnotationForTest.class); + Map valueMap = AnnotationUtil.getAnnotationValueMap(ClassForTest.class, AnnotationForTest.class); + Assert.assertNotNull(valueMap); + Assert.assertEquals(1, valueMap.size()); + Assert.assertEquals(annotation.value(), valueMap.get("value")); + } + + @Test + public void testGetRetentionPolicy() { + RetentionPolicy policy = AnnotationForTest.class.getAnnotation(Retention.class).value(); + Assert.assertEquals(policy, AnnotationUtil.getRetentionPolicy(AnnotationForTest.class)); + } + + @Test + public void testGetTargetType() { + ElementType[] types = AnnotationForTest.class.getAnnotation(Target.class).value(); + Assert.assertArrayEquals(types, AnnotationUtil.getTargetType(AnnotationForTest.class)); + } + + @Test + public void testIsDocumented() { + Assert.assertFalse(AnnotationUtil.isDocumented(AnnotationForTest.class)); + } + + @Test + public void testIsInherited() { + Assert.assertFalse(AnnotationUtil.isInherited(AnnotationForTest.class)); + } + + @Test + public void testSetValue() { + AnnotationForTest annotation = ClassForTest.class.getAnnotation(AnnotationForTest.class); + String newValue = "is a new value"; + Assert.assertNotEquals(newValue, annotation.value()); + AnnotationUtil.setValue(annotation, "value", newValue); + Assert.assertEquals(newValue, annotation.value()); + } + + @Test + public void testGetAnnotationAlias() { + MetaAnnotationForTest annotation = AnnotationUtil.getAnnotationAlias(AnnotationForTest.class, MetaAnnotationForTest.class); + Assert.assertEquals(annotation.value(), annotation.alias()); + Assert.assertEquals(MetaAnnotationForTest.class, annotation.annotationType()); + } + + @Target(ElementType.TYPE_USE) + @Retention(RetentionPolicy.RUNTIME) + private @interface MetaAnnotationForTest{ + @Alias(value = "alias") + String value() default ""; + String alias() default ""; + } + + @MetaAnnotationForTest("foo") + @Target(ElementType.TYPE_USE) + @Retention(RetentionPolicy.RUNTIME) + private @interface AnnotationForTest{ + String value() default ""; + } + + @AnnotationForTest("foo") + private static class ClassForTest{} + } diff --git a/hutool-core/src/test/java/cn/hutool/core/annotation/CombinationAnnotationElementTest.java b/hutool-core/src/test/java/cn/hutool/core/annotation/CombinationAnnotationElementTest.java new file mode 100644 index 000000000..cb81ef2cb --- /dev/null +++ b/hutool-core/src/test/java/cn/hutool/core/annotation/CombinationAnnotationElementTest.java @@ -0,0 +1,60 @@ +package cn.hutool.core.annotation; + +import org.junit.Assert; +import org.junit.Test; + +import java.lang.annotation.*; + +/** + * test for {@link CombinationAnnotationElement} + */ +public class CombinationAnnotationElementTest { + + @Test + public void testOf() { + CombinationAnnotationElement element = CombinationAnnotationElement.of(ClassForTest.class, a -> true); + Assert.assertNotNull(element); + } + + @Test + public void testIsAnnotationPresent() { + CombinationAnnotationElement element = CombinationAnnotationElement.of(ClassForTest.class, a -> true); + Assert.assertTrue(element.isAnnotationPresent(MetaAnnotationForTest.class)); + } + + @Test + public void testGetAnnotation() { + AnnotationForTest annotation1 = ClassForTest.class.getAnnotation(AnnotationForTest.class); + MetaAnnotationForTest annotation2 = AnnotationForTest.class.getAnnotation(MetaAnnotationForTest.class); + CombinationAnnotationElement element = CombinationAnnotationElement.of(ClassForTest.class, a -> true); + Assert.assertEquals(annotation1, element.getAnnotation(AnnotationForTest.class)); + Assert.assertEquals(annotation2, element.getAnnotation(MetaAnnotationForTest.class)); + } + + @Test + public void testGetAnnotations() { + CombinationAnnotationElement element = CombinationAnnotationElement.of(ClassForTest.class, a -> true); + Annotation[] annotations = element.getAnnotations(); + Assert.assertEquals(2, annotations.length); + } + + @Test + public void testGetDeclaredAnnotations() { + CombinationAnnotationElement element = CombinationAnnotationElement.of(ClassForTest.class, a -> true); + Annotation[] annotations = element.getDeclaredAnnotations(); + Assert.assertEquals(2, annotations.length); + } + + @Target(ElementType.TYPE_USE) + @Retention(RetentionPolicy.RUNTIME) + private @interface MetaAnnotationForTest{ } + + @MetaAnnotationForTest + @Target(ElementType.TYPE_USE) + @Retention(RetentionPolicy.RUNTIME) + private @interface AnnotationForTest{ } + + @AnnotationForTest + private static class ClassForTest{} + +} diff --git a/hutool-core/src/test/java/cn/hutool/core/annotation/RepeatAnnotationForTest.java b/hutool-core/src/test/java/cn/hutool/core/annotation/RepeatAnnotationForTest.java deleted file mode 100755 index cc71c68da..000000000 --- a/hutool-core/src/test/java/cn/hutool/core/annotation/RepeatAnnotationForTest.java +++ /dev/null @@ -1,16 +0,0 @@ -package cn.hutool.core.annotation; - -import java.lang.annotation.ElementType; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.lang.annotation.Target; - -/** - * @author hongda.li 2022-04-26 17:09 - */ -@AnnotationForTest("repeat-annotation") -@Retention(RetentionPolicy.RUNTIME) -// Target注解决定MyAnnotation注解可以加在哪些成分上,如加在类身上,或者属性身上,或者方法身上等成分 -@Target({ ElementType.METHOD, ElementType.TYPE }) -public @interface RepeatAnnotationForTest { -} From 90772979668cfca8be8dc9c80429b35ee02cd01d Mon Sep 17 00:00:00 2001 From: huangchengxing <841396397@qq.com> Date: Tue, 30 Aug 2022 16:45:17 +0800 Subject: [PATCH 05/13] =?UTF-8?q?=E5=B0=86defaultIfNull=E6=A3=80=E9=AA=8C?= =?UTF-8?q?=E5=8F=82=E6=95=B0=E7=B1=BB=E5=9E=8B=E6=94=B9=E4=B8=BA=E6=B3=9B?= =?UTF-8?q?=E5=9E=8B=EF=BC=8C=E5=B9=B6=E8=A1=A5=E5=85=85=E9=87=8D=E8=BD=BD?= =?UTF-8?q?=E6=96=B9=E6=B3=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/cn/hutool/core/util/ObjUtil.java | 26 ++++++++++++++----- .../java/cn/hutool/core/util/ObjUtilTest.java | 25 ++++++++++-------- 2 files changed, 34 insertions(+), 17 deletions(-) diff --git a/hutool-core/src/main/java/cn/hutool/core/util/ObjUtil.java b/hutool-core/src/main/java/cn/hutool/core/util/ObjUtil.java index c193bd571..18bbdc7f6 100644 --- a/hutool-core/src/main/java/cn/hutool/core/util/ObjUtil.java +++ b/hutool-core/src/main/java/cn/hutool/core/util/ObjUtil.java @@ -13,11 +13,7 @@ import cn.hutool.core.text.StrUtil; import java.lang.reflect.Array; import java.math.BigDecimal; -import java.util.Collection; -import java.util.Enumeration; -import java.util.Iterator; -import java.util.Map; -import java.util.Objects; +import java.util.*; import java.util.function.Function; import java.util.function.Supplier; @@ -301,6 +297,7 @@ public class ObjUtil { /** * 如果给定对象为{@code null} 返回默认值, 如果不为null 返回自定义handle处理后的返回值 * + * @param 返回值类型 * @param 被检查对象类型 * @param source Object 类型对象 * @param handler 非空时自定义的处理方法 @@ -308,13 +305,30 @@ public class ObjUtil { * @return 处理后的返回值 * @since 6.0.0 */ - public static T defaultIfNull(final Object source, final Function handler, final Supplier defaultSupplier) { + public static R defaultIfNull( + final T source, final Function handler, final Supplier defaultSupplier) { if (isNotNull(source)) { return handler.apply(source); } return defaultSupplier.get(); } + /** + * 如果给定对象为{@code null}返回默认值, 如果不为null返回自定义handle处理后的返回值 + * + * @param 返回值类型 + * @param 被检查对象类型 + * @param source Object 类型对象 + * @param handler 非空时自定义的处理方法 + * @param defaultValue 默认为空的返回值 + * @return 处理后的返回值 + * @since 6.0.0 + */ + public static R defaultIfNull( + final T source, final Function handler, final R defaultValue) { + return isNull(source) ? defaultValue : handler.apply(source); + } + /** * 克隆对象
* 如果对象实现Cloneable接口,调用其clone方法
diff --git a/hutool-core/src/test/java/cn/hutool/core/util/ObjUtilTest.java b/hutool-core/src/test/java/cn/hutool/core/util/ObjUtilTest.java index a2b34c992..2b41568ba 100644 --- a/hutool-core/src/test/java/cn/hutool/core/util/ObjUtilTest.java +++ b/hutool-core/src/test/java/cn/hutool/core/util/ObjUtilTest.java @@ -1,18 +1,16 @@ package cn.hutool.core.util; import cn.hutool.core.collection.ListUtil; -import cn.hutool.core.date.DatePattern; -import cn.hutool.core.date.DateUtil; import cn.hutool.core.exceptions.CloneRuntimeException; import org.junit.Assert; import org.junit.Test; import java.math.BigDecimal; -import java.time.Instant; import java.time.LocalDateTime; import java.util.ArrayList; import java.util.HashMap; import java.util.Map; +import java.util.function.Function; public class ObjUtilTest { @@ -99,15 +97,20 @@ public class ObjUtilTest { @Test public void defaultIfNullTest() { - final String dateStr = "2020-10-23 15:12:30"; - final Instant result1 = ObjUtil.defaultIfNull(dateStr, - (v) -> DateUtil.parse(v.toString(), DatePattern.NORM_DATETIME_PATTERN).toInstant(), Instant::now); - Assert.assertNotNull(result1); + final Object val1 = new Object(); + final Object val2 = new Object(); - final String nullValue = null; - final Instant result2 = ObjUtil.defaultIfNull(nullValue, - (v) -> DateUtil.parse(v.toString(), DatePattern.NORM_DATETIME_PATTERN).toInstant(), Instant::now); - Assert.assertNotNull(result2); + Assert.assertSame(val1, ObjUtil.defaultIfNull(val1, () -> val2)); + Assert.assertSame(val2, ObjUtil.defaultIfNull(null, () -> val2)); + + Assert.assertSame(val1, ObjUtil.defaultIfNull(val1, val2)); + Assert.assertSame(val2, ObjUtil.defaultIfNull(null, val2)); + + Assert.assertSame(val1, ObjUtil.defaultIfNull(val1, Function.identity(), () -> val2)); + Assert.assertSame(val2, ObjUtil.defaultIfNull(null, Function.identity(), () -> val2)); + + Assert.assertSame(val1, ObjUtil.defaultIfNull(val1, Function.identity(), val2)); + Assert.assertSame(val2, ObjUtil.defaultIfNull(null, Function.identity(), val2)); } @Test From d9d8ad0d62f03b17aa79ca61ab9dd9eceee3fa11 Mon Sep 17 00:00:00 2001 From: Husky <2466896229@qq.com> Date: Tue, 30 Aug 2022 17:35:45 +0800 Subject: [PATCH 06/13] =?UTF-8?q?WeekTest=20=E5=AE=8C=E5=96=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../test/java/cn/hutool/core/date/WeekTest.java | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/hutool-core/src/test/java/cn/hutool/core/date/WeekTest.java b/hutool-core/src/test/java/cn/hutool/core/date/WeekTest.java index 26f9f95c7..90d158bf8 100644 --- a/hutool-core/src/test/java/cn/hutool/core/date/WeekTest.java +++ b/hutool-core/src/test/java/cn/hutool/core/date/WeekTest.java @@ -4,6 +4,7 @@ import org.junit.Assert; import org.junit.Test; import java.time.DayOfWeek; +import java.util.Calendar; public class WeekTest { @@ -46,6 +47,9 @@ public class WeekTest { Assert.assertEquals(Week.THURSDAY, Week.of(DayOfWeek.THURSDAY)); Assert.assertEquals(Week.FRIDAY, Week.of(DayOfWeek.FRIDAY)); Assert.assertEquals(Week.SATURDAY, Week.of(DayOfWeek.SATURDAY)); + Assert.assertEquals(Week.SATURDAY, Week.of(Calendar.SATURDAY)); + Assert.assertNull(Week.of(10)); + Assert.assertNull(Week.of(-1)); } @Test @@ -58,4 +62,17 @@ public class WeekTest { Assert.assertEquals(DayOfWeek.SATURDAY, Week.SATURDAY.toJdkDayOfWeek()); Assert.assertEquals(DayOfWeek.SUNDAY, Week.SUNDAY.toJdkDayOfWeek()); } + + @Test + public void toChineseTest(){ + Assert.assertEquals("周一",Week.MONDAY.toChinese("周")); + Assert.assertEquals("星期一",Week.MONDAY.toChinese("星期")); + Assert.assertEquals("星期二",Week.TUESDAY.toChinese("星期")); + Assert.assertEquals("星期三",Week.WEDNESDAY.toChinese("星期")); + Assert.assertEquals("星期四",Week.THURSDAY.toChinese("星期")); + Assert.assertEquals("星期五",Week.FRIDAY.toChinese("星期")); + Assert.assertEquals("星期六",Week.SATURDAY.toChinese("星期")); + Assert.assertEquals("星期日",Week.SUNDAY.toChinese("星期")); + Assert.assertEquals("星期一",Week.MONDAY.toChinese()); + } } From 39a618ff10660873bdf542feee328785001a1053 Mon Sep 17 00:00:00 2001 From: Husky <2466896229@qq.com> Date: Tue, 30 Aug 2022 17:44:36 +0800 Subject: [PATCH 07/13] =?UTF-8?q?ZoneUtilTest=20=E5=AE=8C=E5=96=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/cn/hutool/core/date/ZoneUtilTest.java | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) create mode 100644 hutool-core/src/test/java/cn/hutool/core/date/ZoneUtilTest.java diff --git a/hutool-core/src/test/java/cn/hutool/core/date/ZoneUtilTest.java b/hutool-core/src/test/java/cn/hutool/core/date/ZoneUtilTest.java new file mode 100644 index 000000000..c9a3f14ea --- /dev/null +++ b/hutool-core/src/test/java/cn/hutool/core/date/ZoneUtilTest.java @@ -0,0 +1,16 @@ +package cn.hutool.core.date; + +import org.junit.Assert; +import org.junit.Test; + +import java.time.ZoneId; +import java.util.TimeZone; + +public class ZoneUtilTest { + + @Test + public void test() { + Assert.assertEquals(ZoneId.systemDefault(), ZoneUtil.toZoneId(null)); + Assert.assertEquals(TimeZone.getDefault(), ZoneUtil.toTimeZone(null)); + } +} From 0590793775ba51ea0ea47263c7be6d76481389dc Mon Sep 17 00:00:00 2001 From: Looly Date: Tue, 30 Aug 2022 21:35:00 +0800 Subject: [PATCH 08/13] =?UTF-8?q?=E4=BF=AE=E6=94=B9JSON=E7=9A=84=E8=BF=87?= =?UTF-8?q?=E6=BB=A4=E6=8E=A5=E5=8F=A3=EF=BC=8C=E5=AE=9E=E7=8E=B0=E5=A4=9A?= =?UTF-8?q?=E5=B1=82=E8=BF=87=E6=BB=A4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/main/java/cn/hutool/json/JSON.java | 24 +++-- .../main/java/cn/hutool/json/JSONArray.java | 30 +----- .../main/java/cn/hutool/json/JSONObject.java | 22 +--- .../main/java/cn/hutool/json/JSONUtil.java | 26 ----- .../json/jwt/signers/AlgorithmUtil.java | 12 +++ .../json/jwt/signers/JWTSignerUtil.java | 100 ++++++++++++++++++ .../cn/hutool/json/serialize/JSONWriter.java | 82 +++++++------- .../java/cn/hutool/json/IssueI5OMSCTest.java | 26 +++++ .../java/cn/hutool/json/JSONObjectTest.java | 2 +- .../cn/hutool/json/jwt/JWTSignerTest.java | 90 ++++++++++++++++ .../src/test/resources/issueI5OMSC.json | 32 ++++++ 11 files changed, 330 insertions(+), 116 deletions(-) create mode 100755 hutool-json/src/test/java/cn/hutool/json/IssueI5OMSCTest.java create mode 100755 hutool-json/src/test/resources/issueI5OMSC.json diff --git a/hutool-json/src/main/java/cn/hutool/json/JSON.java b/hutool-json/src/main/java/cn/hutool/json/JSON.java index 79e510951..f4561b9d3 100755 --- a/hutool-json/src/main/java/cn/hutool/json/JSON.java +++ b/hutool-json/src/main/java/cn/hutool/json/JSON.java @@ -1,12 +1,14 @@ package cn.hutool.json; import cn.hutool.core.bean.BeanPath; +import cn.hutool.core.lang.mutable.MutableEntry; import cn.hutool.json.convert.JSONConverterOld; import java.io.Serializable; import java.io.StringWriter; import java.io.Writer; import java.lang.reflect.Type; +import java.util.function.Predicate; /** * JSON接口 @@ -23,6 +25,13 @@ public interface JSON extends Cloneable, Serializable { */ JSONConfig getConfig(); + /** + * JSON大小,对于JSONObject,是键值对的多少,JSONArray则是元素的个数 + * + * @return 大小 + */ + int size(); + /** * 通过表达式获取JSON中嵌套的对象
*
    @@ -44,7 +53,7 @@ public interface JSON extends Cloneable, Serializable { * @see BeanPath#get(Object) * @since 4.0.6 */ - default Object getByPath(final String expression){ + default Object getByPath(final String expression) { return BeanPath.of(expression).get(this); } @@ -69,7 +78,7 @@ public interface JSON extends Cloneable, Serializable { * @param expression 表达式 * @param value 值 */ - default void putByPath(final String expression, final Object value){ + default void putByPath(final String expression, final Object value) { BeanPath.of(expression).set(this, value); } @@ -121,7 +130,7 @@ public interface JSON extends Cloneable, Serializable { default String toJSONString(final int indentFactor) throws JSONException { final StringWriter sw = new StringWriter(); synchronized (sw.getBuffer()) { - return this.write(sw, indentFactor, 0).toString(); + return this.write(sw, indentFactor, 0, null).toString(); } } @@ -134,7 +143,7 @@ public interface JSON extends Cloneable, Serializable { * @throws JSONException JSON相关异常 */ default Writer write(final Writer writer) throws JSONException { - return this.write(writer, 0, 0); + return this.write(writer, 0, 0, null); } /** @@ -144,10 +153,11 @@ public interface JSON extends Cloneable, Serializable { * @param writer writer * @param indentFactor 缩进因子,定义每一级别增加的缩进量 * @param indent 本级别缩进量 + * @param predicate 过滤器,可以修改值,key(index)无法修改,{@link Predicate#test(Object)}为{@code true}保留 * @return Writer * @throws JSONException JSON相关异常 */ - Writer write(Writer writer, int indentFactor, int indent) throws JSONException; + Writer write(Writer writer, int indentFactor, int indent, final Predicate> predicate) throws JSONException; /** * 转为实体类对象,转换异常将被抛出 @@ -163,8 +173,8 @@ public interface JSON extends Cloneable, Serializable { /** * 转为实体类对象 * - * @param Bean类型 - * @param type {@link Type} + * @param Bean类型 + * @param type {@link Type} * @return 实体类对象 * @since 4.3.2 */ diff --git a/hutool-json/src/main/java/cn/hutool/json/JSONArray.java b/hutool-json/src/main/java/cn/hutool/json/JSONArray.java index 29fcd1cb3..8fbd73162 100755 --- a/hutool-json/src/main/java/cn/hutool/json/JSONArray.java +++ b/hutool-json/src/main/java/cn/hutool/json/JSONArray.java @@ -533,7 +533,7 @@ public class JSONArray implements JSON, JSONGetter, List, Rando * @return JSON字符串 * @since 5.7.15 */ - public String toJSONString(final int indentFactor, final Predicate> predicate) { + public String toJSONString(final int indentFactor, final Predicate> predicate) { final StringWriter sw = new StringWriter(); synchronized (sw.getBuffer()) { return this.write(sw, indentFactor, 0, predicate).toString(); @@ -541,32 +541,10 @@ public class JSONArray implements JSON, JSONGetter, List, Rando } @Override - public Writer write(final Writer writer, final int indentFactor, final int indent) throws JSONException { - return write(writer, indentFactor, indent, null); - } + public Writer write(final Writer writer, final int indentFactor, final int indent, final Predicate> predicate) throws JSONException { + final JSONWriter jsonWriter = JSONWriter.of(writer, indentFactor, indent, config).beginArray(); - /** - * 将JSON内容写入Writer
    - * 支持过滤器,即选择哪些字段或值不写出 - * - * @param writer writer - * @param indentFactor 缩进因子,定义每一级别增加的缩进量 - * @param indent 本级别缩进量 - * @param predicate 过滤器,可以修改值,key(index)无法修改,{@link Predicate#test(Object)}为{@code true}保留 - * @return Writer - * @throws JSONException JSON相关异常 - * @since 5.7.15 - */ - public Writer write(final Writer writer, final int indentFactor, final int indent, final Predicate> predicate) throws JSONException { - final JSONWriter jsonWriter = JSONWriter.of(writer, indentFactor, indent, config) - .beginArray(); - - CollUtil.forEach(this, (value, index) -> { - final MutableEntry pair = new MutableEntry<>(index, value); - if (null == predicate || predicate.test(pair)) { - jsonWriter.writeValue(pair.getValue()); - } - }); + CollUtil.forEach(this, (value, index) -> jsonWriter.writeField(new MutableEntry<>(index, value), predicate)); jsonWriter.end(); // 此处不关闭Writer,考虑writer后续还需要填内容 return writer; diff --git a/hutool-json/src/main/java/cn/hutool/json/JSONObject.java b/hutool-json/src/main/java/cn/hutool/json/JSONObject.java index 49ce8dcdb..a56427eda 100755 --- a/hutool-json/src/main/java/cn/hutool/json/JSONObject.java +++ b/hutool-json/src/main/java/cn/hutool/json/JSONObject.java @@ -341,18 +341,13 @@ public class JSONObject extends MapWrapper implements JSON, JSON * @return JSON字符串 * @since 5.7.15 */ - public String toJSONString(final int indentFactor, final Predicate> predicate) { + public String toJSONString(final int indentFactor, final Predicate> predicate) { final StringWriter sw = new StringWriter(); synchronized (sw.getBuffer()) { return this.write(sw, indentFactor, 0, predicate).toString(); } } - @Override - public Writer write(final Writer writer, final int indentFactor, final int indent) throws JSONException { - return write(writer, indentFactor, indent, null); - } - /** * 将JSON内容写入Writer
    * 支持过滤器,即选择哪些字段或值不写出 @@ -365,20 +360,11 @@ public class JSONObject extends MapWrapper implements JSON, JSON * @throws JSONException JSON相关异常 * @since 5.7.15 */ - public Writer write(final Writer writer, final int indentFactor, final int indent, final Predicate> predicate) throws JSONException { + @Override + public Writer write(final Writer writer, final int indentFactor, final int indent, final Predicate> predicate) throws JSONException { final JSONWriter jsonWriter = JSONWriter.of(writer, indentFactor, indent, config) .beginObj(); - this.forEach((key, value) -> { - if (null != predicate) { - final MutableEntry pair = new MutableEntry<>(key, value); - if (predicate.test(pair)) { - // 使用修改后的键值对 - jsonWriter.writeField(pair.getKey(), pair.getValue()); - } - } else { - jsonWriter.writeField(key, value); - } - }); + this.forEach((key, value) -> jsonWriter.writeField(new MutableEntry<>(key, value), predicate)); jsonWriter.end(); // 此处不关闭Writer,考虑writer后续还需要填内容 return writer; diff --git a/hutool-json/src/main/java/cn/hutool/json/JSONUtil.java b/hutool-json/src/main/java/cn/hutool/json/JSONUtil.java index 038bea4b7..28a38f2be 100755 --- a/hutool-json/src/main/java/cn/hutool/json/JSONUtil.java +++ b/hutool-json/src/main/java/cn/hutool/json/JSONUtil.java @@ -530,32 +530,6 @@ public class JSONUtil { return (T) json.getByPath(expression); } - /** - * 设置表达式指定位置(或filed对应)的值
    - * 若表达式指向一个JSONArray则设置其坐标对应位置的值,若指向JSONObject则put对应key的值
    - * 注意:如果为JSONArray,则设置值得下标不能大于已有JSONArray的长度
    - *
      - *
    1. .表达式,可以获取Bean对象中的属性(字段)值或者Map中key对应的值
    2. - *
    3. []表达式,可以获取集合等对象中对应index的值
    4. - *
    - *

    - * 表达式栗子: - * - *

    -	 * persion
    -	 * persion.name
    -	 * persons[3]
    -	 * person.friends[5].name
    -	 * 
    - * - * @param json JSON,可以为JSONObject或JSONArray - * @param expression 表达式 - * @param value 值 - */ - public static void putByPath(final JSON json, final String expression, final Object value) { - json.putByPath(expression, value); - } - /** * 格式化JSON字符串,此方法并不严格检查JSON的格式正确与否 * diff --git a/hutool-json/src/main/java/cn/hutool/json/jwt/signers/AlgorithmUtil.java b/hutool-json/src/main/java/cn/hutool/json/jwt/signers/AlgorithmUtil.java index 8833825af..57de4be98 100755 --- a/hutool-json/src/main/java/cn/hutool/json/jwt/signers/AlgorithmUtil.java +++ b/hutool-json/src/main/java/cn/hutool/json/jwt/signers/AlgorithmUtil.java @@ -23,6 +23,10 @@ public class AlgorithmUtil { map.put("HS384", HmacAlgorithm.HmacSHA384.getValue()); map.put("HS512", HmacAlgorithm.HmacSHA512.getValue()); + map.put("HMD5", HmacAlgorithm.HmacMD5.getValue()); + map.put("HSHA1", HmacAlgorithm.HmacSHA1.getValue()); + map.put("SM4CMAC", HmacAlgorithm.SM4CMAC.getValue()); + map.put("RS256", SignAlgorithm.SHA256withRSA.getValue()); map.put("RS384", SignAlgorithm.SHA384withRSA.getValue()); map.put("RS512", SignAlgorithm.SHA512withRSA.getValue()); @@ -34,6 +38,14 @@ public class AlgorithmUtil { map.put("PS256", SignAlgorithm.SHA256withRSA_PSS.getValue()); map.put("PS384", SignAlgorithm.SHA384withRSA_PSS.getValue()); map.put("PS512", SignAlgorithm.SHA512withRSA_PSS.getValue()); + + map.put("RMD2", SignAlgorithm.MD2withRSA.getValue()); + map.put("RMD5", SignAlgorithm.MD5withRSA.getValue()); + map.put("RSHA1", SignAlgorithm.SHA1withRSA.getValue()); + map.put("DNONE", SignAlgorithm.NONEwithDSA.getValue()); + map.put("DSHA1", SignAlgorithm.SHA1withDSA.getValue()); + map.put("ENONE", SignAlgorithm.NONEwithECDSA.getValue()); + map.put("ESHA1", SignAlgorithm.SHA1withECDSA.getValue()); } /** diff --git a/hutool-json/src/main/java/cn/hutool/json/jwt/signers/JWTSignerUtil.java b/hutool-json/src/main/java/cn/hutool/json/jwt/signers/JWTSignerUtil.java index 5fdf6f433..7922ecb00 100755 --- a/hutool-json/src/main/java/cn/hutool/json/jwt/signers/JWTSignerUtil.java +++ b/hutool-json/src/main/java/cn/hutool/json/jwt/signers/JWTSignerUtil.java @@ -120,6 +120,106 @@ public class JWTSignerUtil { return createSigner("ES512", key); } + /** + * HMD5(HmacMD5)签名器 + * + * @param key 密钥 + * @return 签名器 + */ + public static JWTSigner hmd5(final Key key) { + return createSigner("HMD5",key); + } + + /** + * HSHA1(HmacSHA1)签名器 + * + * @param key 密钥 + * @return 签名器 + */ + public static JWTSigner hsha1(final Key key) { + return createSigner("HSHA1",key); + } + + /** + * SM4CMAC(SM4CMAC)签名器 + * + * @param key 密钥 + * @return 签名器 + */ + public static JWTSigner sm4cmac(final Key key) { + return createSigner("SM4CMAC",key); + } + + /** + * RMD2(MD2withRSA)签名器 + * + * @param key 密钥 + * @return 签名器 + */ + public static JWTSigner rmd2(final Key key) { + return createSigner("RMD2",key); + } + + /** + * RMD5(MD5withRSA)签名器 + * + * @param key 密钥 + * @return 签名器 + */ + public static JWTSigner rmd5(final Key key) { + return createSigner("RMD5",key); + } + + /** + * RSHA1(SHA1withRSA)签名器 + * + * @param key 密钥 + * @return 签名器 + */ + public static JWTSigner rsha1(final Key key) { + return createSigner("RSHA1",key); + } + + /** + * DNONE(NONEwithDSA)签名器 + * + * @param key 密钥 + * @return 签名器 + */ + public static JWTSigner dnone(final Key key) { + return createSigner("DNONE",key); + } + + /** + * DSHA1(SHA1withDSA)签名器 + * + * @param key 密钥 + * @return 签名器 + */ + public static JWTSigner dsha1(final Key key) { + return createSigner("DSHA1",key); + } + + /** + * ENONE(NONEwithECDSA)签名器 + * + * @param key 密钥 + * @return 签名器 + */ + public static JWTSigner enone(final Key key) { + return createSigner("ENONE",key); + } + + /** + * ESHA1(SHA1withECDSA)签名器 + * + * @param key 密钥 + * @return 签名器 + */ + public static JWTSigner esha1(final Key key) { + return createSigner("ESHA1",key); + } + /** * 创建签名器 * diff --git a/hutool-json/src/main/java/cn/hutool/json/serialize/JSONWriter.java b/hutool-json/src/main/java/cn/hutool/json/serialize/JSONWriter.java index 9e97ffa8d..61d755b05 100755 --- a/hutool-json/src/main/java/cn/hutool/json/serialize/JSONWriter.java +++ b/hutool-json/src/main/java/cn/hutool/json/serialize/JSONWriter.java @@ -5,6 +5,7 @@ import cn.hutool.core.date.DateUtil; import cn.hutool.core.date.TemporalAccessorUtil; import cn.hutool.core.date.format.GlobalCustomFormat; import cn.hutool.core.io.IORuntimeException; +import cn.hutool.core.lang.mutable.MutableEntry; import cn.hutool.core.math.NumberUtil; import cn.hutool.core.text.StrUtil; import cn.hutool.core.util.CharUtil; @@ -18,6 +19,7 @@ import java.io.Writer; import java.time.temporal.TemporalAccessor; import java.util.Calendar; import java.util.Date; +import java.util.function.Predicate; /** * JSON数据写出器
    @@ -123,54 +125,56 @@ public class JSONWriter extends Writer { return this; } + /** + * 写出字段名及字段值,如果字段值是{@code null}且忽略null值,则不写出任何内容
    + * 在{@link #arrayMode} 为 {@code true} 时,key是数字,此时不写出键,只写值 + * + * @param pair 键值对 + * @param predicate 过滤修改器 + * @return this + * @since 6.0.0 + */ + @SuppressWarnings({"UnusedReturnValue", "resource"}) + public JSONWriter writeField(final MutableEntry pair, final Predicate> predicate) { + final Object value = pair.getValue(); + if (null == value && config.isIgnoreNullValue()) { + return this; + } + + if (null != predicate) { + if (false == predicate.test(pair)) { + // 使用修改后的键值对 + return this; + } + } + + final Object key = pair.getKey(); + if(false == arrayMode){ + // JSONObject模式,写出键,否则只输出值 + writeKey(StrUtil.toString(key)); + } + + return writeValueDirect(value, predicate); + } + /** * 写出键,自动处理分隔符和缩进,并包装键名 * * @param key 键名 * @return this */ + @SuppressWarnings({"resource", "UnusedReturnValue"}) public JSONWriter writeKey(final String key) { if (needSeparator) { //noinspection resource writeRaw(CharUtil.COMMA); } // 换行缩进 - //noinspection resource writeLF().writeSpace(indentFactor + indent); return writeRaw(InternalJSONUtil.quote(key)); } - /** - * 写出值,自动处理分隔符和缩进,自动判断类型,并根据不同类型写出特定格式的值
    - * 如果写出的值为{@code null},且配置忽略null,则跳过。 - * - * @param value 值 - * @return this - */ - public JSONWriter writeValue(final Object value) { - if (null == value && config.isIgnoreNullValue()) { - return this; - } - return writeValueDirect(value); - } - - /** - * 写出字段名及字段值,如果字段值是{@code null}且忽略null值,则不写出任何内容 - * - * @param key 字段名 - * @param value 字段值 - * @return this - * @since 5.7.6 - */ - public JSONWriter writeField(final String key, final Object value) { - if (null == value && config.isIgnoreNullValue()) { - return this; - } - - //noinspection resource - return writeKey(key).writeValueDirect(value); - } - + @SuppressWarnings({"SpellCheckingInspection", "NullableProblems"}) @Override public void write(final char[] cbuf, final int off, final int len) throws IOException { this.writer.write(cbuf, off, len); @@ -195,10 +199,11 @@ public class JSONWriter extends Writer { /** * 写出值,自动处理分隔符和缩进,自动判断类型,并根据不同类型写出特定格式的值 * - * @param value 值 + * @param value 值 + * @param predicate 过滤修改器 * @return this */ - private JSONWriter writeValueDirect(final Object value) { + private JSONWriter writeValueDirect(final Object value, final Predicate> predicate) { if (arrayMode) { if (needSeparator) { //noinspection resource @@ -212,22 +217,23 @@ public class JSONWriter extends Writer { writeRaw(CharUtil.COLON).writeSpace(1); } needSeparator = true; - return writeObjValue(value); + return writeObjValue(value, predicate); } /** * 写出JSON的值,根据值类型不同,输出不同内容 * - * @param value 值 + * @param value 值 + * @param predicate 过滤修改器 * @return this */ - private JSONWriter writeObjValue(final Object value) { + private JSONWriter writeObjValue(final Object value, final Predicate> predicate) { final int indent = indentFactor + this.indent; if (value == null) { //noinspection resource writeRaw(StrUtil.NULL); } else if (value instanceof JSON) { - ((JSON) value).write(writer, indentFactor, indent); + ((JSON) value).write(writer, indentFactor, indent, predicate); } else if (value instanceof Number) { writeNumberValue((Number) value); } else if (value instanceof Date || value instanceof Calendar || value instanceof TemporalAccessor) { diff --git a/hutool-json/src/test/java/cn/hutool/json/IssueI5OMSCTest.java b/hutool-json/src/test/java/cn/hutool/json/IssueI5OMSCTest.java new file mode 100755 index 000000000..3679e1d5b --- /dev/null +++ b/hutool-json/src/test/java/cn/hutool/json/IssueI5OMSCTest.java @@ -0,0 +1,26 @@ +package cn.hutool.json; + +import cn.hutool.core.collection.ListUtil; +import cn.hutool.core.io.resource.ResourceUtil; +import org.junit.Assert; +import org.junit.Test; + +/** + * Predicate多层过滤 + */ +public class IssueI5OMSCTest { + + @Test + public void filterTest(){ + final JSONObject json = JSONUtil.parseObj(ResourceUtil.readUtf8Str("issueI5OMSC.json")); + + final String s = json.toJSONString(0, (entry) -> { + final Object key = entry.getKey(); + if(key instanceof String){ + return ListUtil.of("store", "bicycle", "color", "book", "author").contains(key); + } + return true; + }); + Assert.assertEquals("{\"store\":{\"bicycle\":{\"color\":\"red\"},\"book\":[{\"author\":\"Evelyn Waugh\"},{\"author\":\"Evelyn Waugh02\"}]}}", s); + } +} diff --git a/hutool-json/src/test/java/cn/hutool/json/JSONObjectTest.java b/hutool-json/src/test/java/cn/hutool/json/JSONObjectTest.java index e388e7b3f..709801136 100755 --- a/hutool-json/src/test/java/cn/hutool/json/JSONObjectTest.java +++ b/hutool-json/src/test/java/cn/hutool/json/JSONObjectTest.java @@ -700,7 +700,7 @@ public class JSONObjectTest { .set("d", true); final String s = json1.toJSONString(0, (pair) -> { - pair.setKey(StrUtil.toUnderlineCase(pair.getKey())); + pair.setKey(StrUtil.toUnderlineCase((String)pair.getKey())); return true; }); Assert.assertEquals("{\"a_key\":\"value1\",\"b_job\":\"value2\",\"c_good\":\"value3\",\"d\":true}", s); diff --git a/hutool-json/src/test/java/cn/hutool/json/jwt/JWTSignerTest.java b/hutool-json/src/test/java/cn/hutool/json/jwt/JWTSignerTest.java index 3084874d3..6c9a27c81 100755 --- a/hutool-json/src/test/java/cn/hutool/json/jwt/JWTSignerTest.java +++ b/hutool-json/src/test/java/cn/hutool/json/jwt/JWTSignerTest.java @@ -116,6 +116,96 @@ public class JWTSignerTest { signAndVerify(signer); } + @Test + public void hmd5Test(){ + final String id = "hmd5"; + final JWTSigner signer = JWTSignerUtil.createSigner(id, KeyUtil.generateKey(AlgorithmUtil.getAlgorithm(id))); + Assert.assertEquals(AlgorithmUtil.getAlgorithm(id), signer.getAlgorithm()); + + signAndVerify(signer); + } + + @Test + public void hsha1Test(){ + final String id = "hsha1"; + final JWTSigner signer = JWTSignerUtil.createSigner(id, KeyUtil.generateKey(AlgorithmUtil.getAlgorithm(id))); + Assert.assertEquals(AlgorithmUtil.getAlgorithm(id), signer.getAlgorithm()); + + signAndVerify(signer); + } + + @Test + public void sm4cmacTest(){ + final String id = "sm4cmac"; + final JWTSigner signer = JWTSignerUtil.createSigner(id, KeyUtil.generateKey(AlgorithmUtil.getAlgorithm(id))); + Assert.assertEquals(AlgorithmUtil.getAlgorithm(id), signer.getAlgorithm()); + + signAndVerify(signer); + } + + @Test + public void rmd2Test(){ + final String id = "rmd2"; + final JWTSigner signer = JWTSignerUtil.createSigner(id, KeyUtil.generateKeyPair(AlgorithmUtil.getAlgorithm(id))); + Assert.assertEquals(AlgorithmUtil.getAlgorithm(id), signer.getAlgorithm()); + + signAndVerify(signer); + } + + @Test + public void rmd5Test(){ + final String id = "rmd5"; + final JWTSigner signer = JWTSignerUtil.createSigner(id, KeyUtil.generateKeyPair(AlgorithmUtil.getAlgorithm(id))); + Assert.assertEquals(AlgorithmUtil.getAlgorithm(id), signer.getAlgorithm()); + + signAndVerify(signer); + } + + @Test + public void rsha1Test(){ + final String id = "rsha1"; + final JWTSigner signer = JWTSignerUtil.createSigner(id, KeyUtil.generateKeyPair(AlgorithmUtil.getAlgorithm(id))); + Assert.assertEquals(AlgorithmUtil.getAlgorithm(id), signer.getAlgorithm()); + + signAndVerify(signer); + } + + @Test + public void dnoneTest(){ + final String id = "dnone"; + final JWTSigner signer = JWTSignerUtil.createSigner(id, KeyUtil.generateKeyPair(AlgorithmUtil.getAlgorithm(id))); + Assert.assertEquals(AlgorithmUtil.getAlgorithm(id), signer.getAlgorithm()); + + signAndVerify(signer); + } + + @Test + public void dsha1Test(){ + final String id = "dsha1"; + final JWTSigner signer = JWTSignerUtil.createSigner(id, KeyUtil.generateKeyPair(AlgorithmUtil.getAlgorithm(id))); + Assert.assertEquals(AlgorithmUtil.getAlgorithm(id), signer.getAlgorithm()); + + signAndVerify(signer); + } + + @Test + public void enoneTest(){ + final String id = "enone"; + final JWTSigner signer = JWTSignerUtil.createSigner(id, KeyUtil.generateKeyPair(AlgorithmUtil.getAlgorithm(id))); + Assert.assertEquals(AlgorithmUtil.getAlgorithm(id), signer.getAlgorithm()); + + signAndVerify(signer); + } + + @Test + public void esha1Test(){ + final String id = "esha1"; + final JWTSigner signer = JWTSignerUtil.createSigner(id, KeyUtil.generateKeyPair(AlgorithmUtil.getAlgorithm(id))); + Assert.assertEquals(AlgorithmUtil.getAlgorithm(id), signer.getAlgorithm()); + + signAndVerify(signer); + } + private static void signAndVerify(final JWTSigner signer){ final JWT jwt = JWT.of() .setPayload("sub", "1234567890") diff --git a/hutool-json/src/test/resources/issueI5OMSC.json b/hutool-json/src/test/resources/issueI5OMSC.json new file mode 100755 index 000000000..2cccb2ccb --- /dev/null +++ b/hutool-json/src/test/resources/issueI5OMSC.json @@ -0,0 +1,32 @@ +{ + "store": { + "bicycle": { + "color": "red", + "price": 19.95 + }, + "book": [ + { + "category": "fiction", + "author": "Evelyn Waugh", + "title": "Sword of Honour", + "price": 12.99, + "items": [ + { + "name": "wujing001", + "age": 18 + }, + { + "name": "wujing002", + "age": 18 + } + ] + }, + { + "category": "fiction02", + "author": "Evelyn Waugh02", + "title": "Sword of Honour02", + "price": 12.99 + } + ] + } +} From 1361e1159263cee052205489d1cced0288bbea67 Mon Sep 17 00:00:00 2001 From: Looly Date: Tue, 30 Aug 2022 21:38:57 +0800 Subject: [PATCH 09/13] =?UTF-8?q?=E5=B0=86ObjUtil.defaultIfNull=E6=A3=80?= =?UTF-8?q?=E9=AA=8C=E5=8F=82=E6=95=B0=E7=B1=BB=E5=9E=8B=E4=BB=8EObject?= =?UTF-8?q?=E6=94=B9=E4=B8=BA=E6=B3=9B=E5=9E=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/main/java/cn/hutool/core/util/ObjUtil.java | 9 ++++++--- .../src/test/java/cn/hutool/core/util/ObjUtilTest.java | 4 ++-- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/hutool-core/src/main/java/cn/hutool/core/util/ObjUtil.java b/hutool-core/src/main/java/cn/hutool/core/util/ObjUtil.java index 18bbdc7f6..3d88ceb10 100644 --- a/hutool-core/src/main/java/cn/hutool/core/util/ObjUtil.java +++ b/hutool-core/src/main/java/cn/hutool/core/util/ObjUtil.java @@ -13,7 +13,11 @@ import cn.hutool.core.text.StrUtil; import java.lang.reflect.Array; import java.math.BigDecimal; -import java.util.*; +import java.util.Collection; +import java.util.Enumeration; +import java.util.Iterator; +import java.util.Map; +import java.util.Objects; import java.util.function.Function; import java.util.function.Supplier; @@ -305,8 +309,7 @@ public class ObjUtil { * @return 处理后的返回值 * @since 6.0.0 */ - public static R defaultIfNull( - final T source, final Function handler, final Supplier defaultSupplier) { + public static R defaultIfNull(final T source, final Function handler, final Supplier defaultSupplier) { if (isNotNull(source)) { return handler.apply(source); } diff --git a/hutool-core/src/test/java/cn/hutool/core/util/ObjUtilTest.java b/hutool-core/src/test/java/cn/hutool/core/util/ObjUtilTest.java index 2b41568ba..dd920ee8b 100644 --- a/hutool-core/src/test/java/cn/hutool/core/util/ObjUtilTest.java +++ b/hutool-core/src/test/java/cn/hutool/core/util/ObjUtilTest.java @@ -82,7 +82,7 @@ public class ObjUtilTest { public Obj clone() { try { return (Obj) super.clone(); - } catch (CloneNotSupportedException e) { + } catch (final CloneNotSupportedException e) { throw new CloneRuntimeException(e); } } @@ -122,7 +122,7 @@ public class ObjUtilTest { @Test public void cloneIfPossibleTest() { - String a = "a"; + final String a = "a"; final String a2 = ObjUtil.cloneIfPossible(a); Assert.assertNotSame(a, a2); } From dd3148ca0ae02b27715bda9c31225e130d3cc7ab Mon Sep 17 00:00:00 2001 From: Looly Date: Tue, 30 Aug 2022 21:42:03 +0800 Subject: [PATCH 10/13] =?UTF-8?q?=E6=9B=B4=E6=96=B0=E6=B5=8B=E8=AF=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../core/annotation/AnnotationUtilTest.java | 22 +++++++++---------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/hutool-core/src/test/java/cn/hutool/core/annotation/AnnotationUtilTest.java b/hutool-core/src/test/java/cn/hutool/core/annotation/AnnotationUtilTest.java index 994d3e2d1..5d3612bc8 100755 --- a/hutool-core/src/test/java/cn/hutool/core/annotation/AnnotationUtilTest.java +++ b/hutool-core/src/test/java/cn/hutool/core/annotation/AnnotationUtilTest.java @@ -14,7 +14,7 @@ public class AnnotationUtilTest { @Test public void testToCombination() { - CombinationAnnotationElement element = AnnotationUtil.toCombination(ClassForTest.class); + final CombinationAnnotationElement element = AnnotationUtil.toCombination(ClassForTest.class); Assert.assertEquals(2, element.getAnnotations().length); } @@ -28,7 +28,7 @@ public class AnnotationUtilTest { @Test public void testGetCombinationAnnotations() { - MetaAnnotationForTest[] annotations = AnnotationUtil.getCombinationAnnotations(ClassForTest.class, MetaAnnotationForTest.class); + final MetaAnnotationForTest[] annotations = AnnotationUtil.getCombinationAnnotations(ClassForTest.class, MetaAnnotationForTest.class); Assert.assertEquals(1, annotations.length); } @@ -51,7 +51,7 @@ public class AnnotationUtilTest { @Test public void testGetAnnotation() { - MetaAnnotationForTest annotation = AnnotationUtil.getAnnotation(ClassForTest.class, MetaAnnotationForTest.class); + final MetaAnnotationForTest annotation = AnnotationUtil.getAnnotation(ClassForTest.class, MetaAnnotationForTest.class); Assert.assertNotNull(annotation); } @@ -62,7 +62,7 @@ public class AnnotationUtilTest { @Test public void testGetAnnotationValue() { - AnnotationForTest annotation = ClassForTest.class.getAnnotation(AnnotationForTest.class); + final AnnotationForTest annotation = ClassForTest.class.getAnnotation(AnnotationForTest.class); Assert.assertEquals(annotation.value(), AnnotationUtil.getAnnotationValue(ClassForTest.class, AnnotationForTest.class)); Assert.assertEquals(annotation.value(), AnnotationUtil.getAnnotationValue(ClassForTest.class, AnnotationForTest.class, "value")); Assert.assertNull(AnnotationUtil.getAnnotationValue(ClassForTest.class, AnnotationForTest.class, "property")); @@ -70,8 +70,8 @@ public class AnnotationUtilTest { @Test public void testGetAnnotationValueMap() { - AnnotationForTest annotation = ClassForTest.class.getAnnotation(AnnotationForTest.class); - Map valueMap = AnnotationUtil.getAnnotationValueMap(ClassForTest.class, AnnotationForTest.class); + final AnnotationForTest annotation = ClassForTest.class.getAnnotation(AnnotationForTest.class); + final Map valueMap = AnnotationUtil.getAnnotationValueMap(ClassForTest.class, AnnotationForTest.class); Assert.assertNotNull(valueMap); Assert.assertEquals(1, valueMap.size()); Assert.assertEquals(annotation.value(), valueMap.get("value")); @@ -79,13 +79,13 @@ public class AnnotationUtilTest { @Test public void testGetRetentionPolicy() { - RetentionPolicy policy = AnnotationForTest.class.getAnnotation(Retention.class).value(); + final RetentionPolicy policy = AnnotationForTest.class.getAnnotation(Retention.class).value(); Assert.assertEquals(policy, AnnotationUtil.getRetentionPolicy(AnnotationForTest.class)); } @Test public void testGetTargetType() { - ElementType[] types = AnnotationForTest.class.getAnnotation(Target.class).value(); + final ElementType[] types = AnnotationForTest.class.getAnnotation(Target.class).value(); Assert.assertArrayEquals(types, AnnotationUtil.getTargetType(AnnotationForTest.class)); } @@ -101,8 +101,8 @@ public class AnnotationUtilTest { @Test public void testSetValue() { - AnnotationForTest annotation = ClassForTest.class.getAnnotation(AnnotationForTest.class); - String newValue = "is a new value"; + final AnnotationForTest annotation = ClassForTest.class.getAnnotation(AnnotationForTest.class); + final String newValue = "is a new value"; Assert.assertNotEquals(newValue, annotation.value()); AnnotationUtil.setValue(annotation, "value", newValue); Assert.assertEquals(newValue, annotation.value()); @@ -110,7 +110,7 @@ public class AnnotationUtilTest { @Test public void testGetAnnotationAlias() { - MetaAnnotationForTest annotation = AnnotationUtil.getAnnotationAlias(AnnotationForTest.class, MetaAnnotationForTest.class); + final MetaAnnotationForTest annotation = AnnotationUtil.getAnnotationAlias(AnnotationForTest.class, MetaAnnotationForTest.class); Assert.assertEquals(annotation.value(), annotation.alias()); Assert.assertEquals(MetaAnnotationForTest.class, annotation.annotationType()); } From 4e8060e441f5ed7d2bea194d83bea1e3522bb53d Mon Sep 17 00:00:00 2001 From: Looly Date: Tue, 30 Aug 2022 23:13:55 +0800 Subject: [PATCH 11/13] fix code --- .../core/convert/RegisterConverter.java | 7 +++ .../impl/TemporalAccessorConverter.java | 23 ++++++++ .../core/date/TemporalAccessorUtil.java | 5 +- .../cn/hutool/json/serialize/JSONWriter.java | 11 ++++ .../java/cn/hutool/json/Issue2572Test.java | 53 +++++++++++++++++++ 5 files changed, 98 insertions(+), 1 deletion(-) create mode 100755 hutool-json/src/test/java/cn/hutool/json/Issue2572Test.java diff --git a/hutool-core/src/main/java/cn/hutool/core/convert/RegisterConverter.java b/hutool-core/src/main/java/cn/hutool/core/convert/RegisterConverter.java index 424b99cf8..d1426bc9e 100644 --- a/hutool-core/src/main/java/cn/hutool/core/convert/RegisterConverter.java +++ b/hutool-core/src/main/java/cn/hutool/core/convert/RegisterConverter.java @@ -42,11 +42,14 @@ import java.net.URI; import java.net.URL; import java.nio.charset.Charset; import java.nio.file.Path; +import java.time.DayOfWeek; import java.time.Duration; import java.time.Instant; import java.time.LocalDate; import java.time.LocalDateTime; import java.time.LocalTime; +import java.time.Month; +import java.time.MonthDay; import java.time.OffsetDateTime; import java.time.OffsetTime; import java.time.Period; @@ -201,6 +204,10 @@ public class RegisterConverter implements Converter, Serializable { 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); + defaultConverterMap.put(Period.class, new PeriodConverter()); defaultConverterMap.put(Duration.class, new DurationConverter()); diff --git a/hutool-core/src/main/java/cn/hutool/core/convert/impl/TemporalAccessorConverter.java b/hutool-core/src/main/java/cn/hutool/core/convert/impl/TemporalAccessorConverter.java index daf743f2f..af481448c 100644 --- a/hutool-core/src/main/java/cn/hutool/core/convert/impl/TemporalAccessorConverter.java +++ b/hutool-core/src/main/java/cn/hutool/core/convert/impl/TemporalAccessorConverter.java @@ -12,10 +12,13 @@ import java.time.LocalDate; import java.time.LocalDateTime; import java.time.LocalTime; import java.time.Month; +import java.time.MonthDay; import java.time.OffsetDateTime; import java.time.OffsetTime; import java.time.ZoneId; import java.time.ZonedDateTime; +import java.time.chrono.Era; +import java.time.chrono.IsoEra; import java.time.format.DateTimeFormatter; import java.time.temporal.TemporalAccessor; import java.util.Calendar; @@ -113,6 +116,16 @@ public class TemporalAccessorConverter extends AbstractConverter { return null; } + if(DayOfWeek.class == targetClass){ + return DayOfWeek.valueOf(StrUtil.toString(value)); + } else if(Month.class == targetClass){ + return Month.valueOf(StrUtil.toString(value)); + } else if(Era.class == targetClass){ + return IsoEra.valueOf(StrUtil.toString(value)); + } else if(MonthDay.class == targetClass){ + return MonthDay.parse(value); + } + final Instant instant; final ZoneId zoneId; if (null != this.format) { @@ -139,6 +152,8 @@ public class TemporalAccessorConverter extends AbstractConverter { return Month.of(Math.toIntExact(time)); } else if(targetClass == DayOfWeek.class){ return DayOfWeek.of(Math.toIntExact(time)); + } else if(Era.class == targetClass){ + return IsoEra.of(Math.toIntExact(time)); } return parseFromInstant(targetClass, Instant.ofEpochMilli(time), null); @@ -151,6 +166,14 @@ public class TemporalAccessorConverter extends AbstractConverter { * @return java.time中的对象 */ private TemporalAccessor parseFromTemporalAccessor(final Class targetClass, final TemporalAccessor temporalAccessor) { + if(DayOfWeek.class == targetClass){ + return DayOfWeek.from(temporalAccessor); + } else if(Month.class == targetClass){ + return Month.from(temporalAccessor); + } else if(MonthDay.class == targetClass){ + return MonthDay.from(temporalAccessor); + } + TemporalAccessor result = null; if (temporalAccessor instanceof LocalDateTime) { result = parseFromLocalDateTime(targetClass, (LocalDateTime) temporalAccessor); diff --git a/hutool-core/src/main/java/cn/hutool/core/date/TemporalAccessorUtil.java b/hutool-core/src/main/java/cn/hutool/core/date/TemporalAccessorUtil.java index 275bb267d..0d1eb21eb 100644 --- a/hutool-core/src/main/java/cn/hutool/core/date/TemporalAccessorUtil.java +++ b/hutool-core/src/main/java/cn/hutool/core/date/TemporalAccessorUtil.java @@ -3,15 +3,18 @@ package cn.hutool.core.date; import cn.hutool.core.date.format.GlobalCustomFormat; import cn.hutool.core.text.StrUtil; +import java.time.DayOfWeek; import java.time.Instant; import java.time.LocalDate; import java.time.LocalDateTime; import java.time.LocalTime; import java.time.Month; +import java.time.MonthDay; import java.time.OffsetDateTime; import java.time.OffsetTime; import java.time.ZoneId; import java.time.ZonedDateTime; +import java.time.chrono.Era; import java.time.format.DateTimeFormatter; import java.time.temporal.TemporalAccessor; import java.time.temporal.TemporalField; @@ -94,7 +97,7 @@ public class TemporalAccessorUtil extends TemporalUtil{ return null; } - if(time instanceof Month){ + if(time instanceof DayOfWeek || time instanceof java.time.Month || time instanceof Era || time instanceof MonthDay){ return time.toString(); } diff --git a/hutool-json/src/main/java/cn/hutool/json/serialize/JSONWriter.java b/hutool-json/src/main/java/cn/hutool/json/serialize/JSONWriter.java index 61d755b05..b2bf5cffc 100755 --- a/hutool-json/src/main/java/cn/hutool/json/serialize/JSONWriter.java +++ b/hutool-json/src/main/java/cn/hutool/json/serialize/JSONWriter.java @@ -16,6 +16,9 @@ import cn.hutool.json.JSONException; import java.io.IOException; import java.io.Writer; +import java.time.DayOfWeek; +import java.time.MonthDay; +import java.time.chrono.Era; import java.time.temporal.TemporalAccessor; import java.util.Calendar; import java.util.Date; @@ -237,6 +240,14 @@ public class JSONWriter extends Writer { } else if (value instanceof Number) { writeNumberValue((Number) value); } else if (value instanceof Date || value instanceof Calendar || value instanceof TemporalAccessor) { + // issue#2572@Github + if(value instanceof TemporalAccessor){ + if(value instanceof DayOfWeek || value instanceof java.time.Month || value instanceof Era || value instanceof MonthDay){ + writeQuoteStrValue(value.toString()); + return this; + } + } + final String format = (null == config) ? null : config.getDateFormat(); //noinspection resource writeRaw(formatDate(value, format)); diff --git a/hutool-json/src/test/java/cn/hutool/json/Issue2572Test.java b/hutool-json/src/test/java/cn/hutool/json/Issue2572Test.java new file mode 100755 index 000000000..7e600a7ba --- /dev/null +++ b/hutool-json/src/test/java/cn/hutool/json/Issue2572Test.java @@ -0,0 +1,53 @@ +package cn.hutool.json; + +import cn.hutool.core.reflect.TypeReference; +import org.junit.Assert; +import org.junit.Test; + +import java.time.DayOfWeek; +import java.time.Month; +import java.time.MonthDay; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; + +public class Issue2572Test { + @Test + public void putDayOfWeekTest(){ + final Set weeks = new HashSet<>(); + weeks.add(DayOfWeek.MONDAY); + final JSONObject obj = new JSONObject(); + obj.set("weeks", weeks); + Assert.assertEquals("{\"weeks\":[\"MONDAY\"]}", obj.toString()); + + final Map> monthDays1 = obj.toBean(new TypeReference>>() { + }); + Assert.assertEquals("{weeks=[MONDAY]}", monthDays1.toString()); + } + + @Test + public void putMonthTest(){ + final Set months = new HashSet<>(); + months.add(Month.DECEMBER); + final JSONObject obj = new JSONObject(); + obj.set("months", months); + Assert.assertEquals("{\"months\":[\"DECEMBER\"]}", obj.toString()); + + final Map> monthDays1 = obj.toBean(new TypeReference>>() { + }); + Assert.assertEquals("{months=[DECEMBER]}", monthDays1.toString()); + } + + @Test + public void putMonthDayTest(){ + final Set monthDays = new HashSet<>(); + monthDays.add(MonthDay.of(Month.DECEMBER, 1)); + final JSONObject obj = new JSONObject(); + obj.set("monthDays", monthDays); + Assert.assertEquals("{\"monthDays\":[\"--12-01\"]}", obj.toString()); + + final Map> monthDays1 = obj.toBean(new TypeReference>>() { + }); + Assert.assertEquals("{monthDays=[--12-01]}", monthDays1.toString()); + } +} From fe6c48f08d7332105600363b5fab60e629d4c168 Mon Sep 17 00:00:00 2001 From: Looly Date: Tue, 30 Aug 2022 23:32:15 +0800 Subject: [PATCH 12/13] fix code --- .../hutool/core/date/TemporalAccessorUtil.java | 8 +++++++- .../cn/hutool/json/serialize/JSONWriter.java | 18 ++++++------------ .../java/cn/hutool/json/Issue2572Test.java | 4 ++-- 3 files changed, 15 insertions(+), 15 deletions(-) diff --git a/hutool-core/src/main/java/cn/hutool/core/date/TemporalAccessorUtil.java b/hutool-core/src/main/java/cn/hutool/core/date/TemporalAccessorUtil.java index 0d1eb21eb..2471f1d7d 100644 --- a/hutool-core/src/main/java/cn/hutool/core/date/TemporalAccessorUtil.java +++ b/hutool-core/src/main/java/cn/hutool/core/date/TemporalAccessorUtil.java @@ -114,7 +114,9 @@ public class TemporalAccessorUtil extends TemporalUtil{ /** * {@link TemporalAccessor}转换为 时间戳(从1970-01-01T00:00:00Z开始的毫秒数)
    - * 如果为{@link Month},调用{@link Month#getValue()} + * 如果为{@link Month},调用{@link Month#getValue()}
    + * 如果为{@link DayOfWeek},调用{@link DayOfWeek#getValue()}
    + * 如果为{@link Era},调用{@link Era#getValue()} * * @param temporalAccessor Date对象 * @return {@link Instant}对象 @@ -123,6 +125,10 @@ public class TemporalAccessorUtil extends TemporalUtil{ public static long toEpochMilli(final TemporalAccessor temporalAccessor) { if(temporalAccessor instanceof Month){ return ((Month) temporalAccessor).getValue(); + } else if(temporalAccessor instanceof DayOfWeek){ + return ((DayOfWeek) temporalAccessor).getValue(); + } else if(temporalAccessor instanceof Era){ + return ((Era) temporalAccessor).getValue(); } return toInstant(temporalAccessor).toEpochMilli(); } diff --git a/hutool-json/src/main/java/cn/hutool/json/serialize/JSONWriter.java b/hutool-json/src/main/java/cn/hutool/json/serialize/JSONWriter.java index b2bf5cffc..3e7425b94 100755 --- a/hutool-json/src/main/java/cn/hutool/json/serialize/JSONWriter.java +++ b/hutool-json/src/main/java/cn/hutool/json/serialize/JSONWriter.java @@ -16,9 +16,7 @@ import cn.hutool.json.JSONException; import java.io.IOException; import java.io.Writer; -import java.time.DayOfWeek; import java.time.MonthDay; -import java.time.chrono.Era; import java.time.temporal.TemporalAccessor; import java.util.Calendar; import java.util.Date; @@ -139,8 +137,7 @@ public class JSONWriter extends Writer { */ @SuppressWarnings({"UnusedReturnValue", "resource"}) public JSONWriter writeField(final MutableEntry pair, final Predicate> predicate) { - final Object value = pair.getValue(); - if (null == value && config.isIgnoreNullValue()) { + if (null == pair.getValue() && config.isIgnoreNullValue()) { return this; } @@ -151,13 +148,12 @@ public class JSONWriter extends Writer { } } - final Object key = pair.getKey(); if(false == arrayMode){ // JSONObject模式,写出键,否则只输出值 - writeKey(StrUtil.toString(key)); + writeKey(StrUtil.toString(pair.getKey())); } - return writeValueDirect(value, predicate); + return writeValueDirect(pair.getValue(), predicate); } /** @@ -241,11 +237,9 @@ public class JSONWriter extends Writer { writeNumberValue((Number) value); } else if (value instanceof Date || value instanceof Calendar || value instanceof TemporalAccessor) { // issue#2572@Github - if(value instanceof TemporalAccessor){ - if(value instanceof DayOfWeek || value instanceof java.time.Month || value instanceof Era || value instanceof MonthDay){ - writeQuoteStrValue(value.toString()); - return this; - } + if(value instanceof MonthDay){ + writeQuoteStrValue(value.toString()); + return this; } final String format = (null == config) ? null : config.getDateFormat(); diff --git a/hutool-json/src/test/java/cn/hutool/json/Issue2572Test.java b/hutool-json/src/test/java/cn/hutool/json/Issue2572Test.java index 7e600a7ba..f6be2e764 100755 --- a/hutool-json/src/test/java/cn/hutool/json/Issue2572Test.java +++ b/hutool-json/src/test/java/cn/hutool/json/Issue2572Test.java @@ -18,7 +18,7 @@ public class Issue2572Test { weeks.add(DayOfWeek.MONDAY); final JSONObject obj = new JSONObject(); obj.set("weeks", weeks); - Assert.assertEquals("{\"weeks\":[\"MONDAY\"]}", obj.toString()); + Assert.assertEquals("{\"weeks\":[1]}", obj.toString()); final Map> monthDays1 = obj.toBean(new TypeReference>>() { }); @@ -31,7 +31,7 @@ public class Issue2572Test { months.add(Month.DECEMBER); final JSONObject obj = new JSONObject(); obj.set("months", months); - Assert.assertEquals("{\"months\":[\"DECEMBER\"]}", obj.toString()); + Assert.assertEquals("{\"months\":[12]}", obj.toString()); final Map> monthDays1 = obj.toBean(new TypeReference>>() { }); From 3bddce1a0bc96ad4b5768205fa3146fe11ce1fb3 Mon Sep 17 00:00:00 2001 From: Looly Date: Wed, 31 Aug 2022 00:53:20 +0800 Subject: [PATCH 13/13] fix doc --- .../core/map/multi/AbsCollValueMap.java | 2 +- .../hutool/core/map/multi/MultiValueMap.java | 20 ++++++++++++++++-- .../hutool/core/map/multi/package-info.java | 21 ++++++++++++++++++- .../hutool/http/server/HttpServerRequest.java | 5 ++--- 4 files changed, 41 insertions(+), 7 deletions(-) diff --git a/hutool-core/src/main/java/cn/hutool/core/map/multi/AbsCollValueMap.java b/hutool-core/src/main/java/cn/hutool/core/map/multi/AbsCollValueMap.java index 9c0e960d1..f43841f8b 100644 --- a/hutool-core/src/main/java/cn/hutool/core/map/multi/AbsCollValueMap.java +++ b/hutool-core/src/main/java/cn/hutool/core/map/multi/AbsCollValueMap.java @@ -20,6 +20,7 @@ import java.util.stream.Collectors; * @param 值类型 * @author looly * @since 5.7.4 + * @see CollectionValueMap * @see SetValueMap * @see ListValueMap */ @@ -60,7 +61,6 @@ public abstract class AbsCollValueMap extends MapWrapper> // ------------------------------------------------------------------------- Constructor end - /** * 将集合中的全部元素对追加到指定键对应的值集合中,效果等同于: *
    {@code
    diff --git a/hutool-core/src/main/java/cn/hutool/core/map/multi/MultiValueMap.java b/hutool-core/src/main/java/cn/hutool/core/map/multi/MultiValueMap.java
    index 684d83a54..e7af04b7e 100644
    --- a/hutool-core/src/main/java/cn/hutool/core/map/multi/MultiValueMap.java
    +++ b/hutool-core/src/main/java/cn/hutool/core/map/multi/MultiValueMap.java
    @@ -11,13 +11,13 @@ import java.util.stream.Collectors;
      * 

    一个键对应多个值的集合{@link Map}实现,提供针对键对应的值集合中的元素而非值集合本身的一些快捷操作, * 本身可作为一个值为{@link Collection}类型的{@link Map}使用。
    * - *

    值集合类型

    + *

    值集合类型

    *

    值集合的类型由接口的实现类自行维护,当通过{@link MultiValueMap}定义的方法进行增删改操作时, * 实现类应保证通过通过实例方法获得的集合类型都一致。但是若用户直接通过{@link Map}定义的方法进行增删改操作时, * 实例无法保证通过实例方法获得的集合类型都一致。
    * 因此,若无必要则更推荐通过{@link MultiValueMap}定义的方法进行操作。 * - *

    对值集合的修改

    + *

    对值集合的修改

    *

    当通过实例方法获得值集合时,若该集合允许修改,则对值集合的修改将会影响到其所属的{@link MultiValueMap}实例,反之亦然。 * 因此当同时遍历当前实例或者值集合时,若存在写操作,则需要注意可能引发的{@link ConcurrentModificationException}。 * @@ -40,6 +40,7 @@ public interface MultiValueMap extends Map> { * @param value 键对应的新值集合 * @return 旧值集合 */ + @SuppressWarnings("AbstractMethodOverridesAbstractMethod") @Override Collection put(K key, Collection value); @@ -49,6 +50,7 @@ public interface MultiValueMap extends Map> { * * @param map 需要更新的键值对集合 */ + @SuppressWarnings("AbstractMethodOverridesAbstractMethod") @Override void putAll(Map> map); @@ -100,6 +102,7 @@ public interface MultiValueMap extends Map> { * @param values 待添加的值 * @return boolean */ + @SuppressWarnings("unchecked") default boolean putValues(final K key, final V... values) { return ArrayUtil.isNotEmpty(values) && putAllValues(key, Arrays.asList(values)); } @@ -138,6 +141,7 @@ public interface MultiValueMap extends Map> { * @param values 值数组 * @return 是否成功删除 */ + @SuppressWarnings("unchecked") default boolean removeValues(final K key, final V... values) { return ArrayUtil.isNotEmpty(values) && removeAllValues(key, Arrays.asList(values)); } @@ -189,6 +193,18 @@ public interface MultiValueMap extends Map> { // =================== read operate =================== + /** + * 获取指定序号的值,若值不存在,返回{@code null} + * + * @param key 键 + * @param index 第几个值的索引,越界返回null + * @return 值或null + */ + default V getValue(K key, int index) { + final Collection collection = get(key); + return CollUtil.get(collection, index); + } + /** * 获取键对应的值,若值不存在,则返回{@link Collections#emptyList()}。效果等同于: *

    {@code
    diff --git a/hutool-core/src/main/java/cn/hutool/core/map/multi/package-info.java b/hutool-core/src/main/java/cn/hutool/core/map/multi/package-info.java
    index 2bf5b685f..25b130d0d 100644
    --- a/hutool-core/src/main/java/cn/hutool/core/map/multi/package-info.java
    +++ b/hutool-core/src/main/java/cn/hutool/core/map/multi/package-info.java
    @@ -1,5 +1,24 @@
     /**
    - * 多参数类型的Map实现,包括集合类型值的Map和Table
    + * 多参数类型的Map实现,包括集合类型值的MultiValueMap和Table
    + *
      + *
    • MultiValueMap:一个键对应多个值的集合的实现,类似于树的结构。
    • + *
    • Table:使用两个键映射到一个值,类似于表格结构。
    • + *
    + * + *
    + *                   MultiValueMap
    + *                         |
    + *                   AbsCollValueMap
    + *                         ||
    + *   [CollectionValueMap, SetValueMap, ListValueMap]
    + * 
    + *
    + *                       Table
    + *                         |
    + *                      AbsTable
    + *                         ||
    + *                    [RowKeyTable]
    + * 
    * * @author looly * diff --git a/hutool-http/src/main/java/cn/hutool/http/server/HttpServerRequest.java b/hutool-http/src/main/java/cn/hutool/http/server/HttpServerRequest.java index c39cb67eb..155d2c301 100644 --- a/hutool-http/src/main/java/cn/hutool/http/server/HttpServerRequest.java +++ b/hutool-http/src/main/java/cn/hutool/http/server/HttpServerRequest.java @@ -25,7 +25,6 @@ import java.net.HttpCookie; import java.net.URI; import java.nio.charset.Charset; import java.util.Collection; -import java.util.List; import java.util.Map; /** @@ -303,7 +302,7 @@ public class HttpServerRequest extends HttpServerBase { * @since 5.5.8 */ public String getParam(final String name){ - return getParams().get(name, 0); + return getParams().getValue(name, 0); } /** @@ -313,7 +312,7 @@ public class HttpServerRequest extends HttpServerBase { * @return 参数值 * @since 5.5.8 */ - public List getParams(final String name){ + public Collection getParams(final String name){ return getParams().get(name); }