From 681535c5759c12bfa43135dc82a58045b9e597a3 Mon Sep 17 00:00:00 2001 From: Looly Date: Sat, 16 Jul 2022 22:46:31 +0800 Subject: [PATCH] fix code --- .../core/collection/partition/Partition.java | 12 +- .../impl/TemporalAccessorConverter.java | 2 + .../java/cn/hutool/core/date/TimeUtil.java | 24 + .../main/java/cn/hutool/core/map/Dict.java | 43 +- .../java/cn/hutool/core/map/ForestMap.java | 312 ++++++++ .../cn/hutool/core/map/LinkedForestMap.java | 744 ++++++++++++++++++ .../java/cn/hutool/core/map/TransMap.java | 2 +- .../java/cn/hutool/core/map/TreeEntry.java | 122 +++ .../main/java/cn/hutool/core/tree/Node.java | 0 .../main/java/cn/hutool/core/tree/Tree.java | 0 .../java/cn/hutool/core/tree/TreeBuilder.java | 0 .../java/cn/hutool/core/tree/TreeNode.java | 0 .../cn/hutool/core/tree/TreeNodeConfig.java | 0 .../java/cn/hutool/core/tree/TreeUtil.java | 0 .../cn/hutool/core/tree/package-info.java | 0 .../core/tree/parser/DefaultNodeParser.java | 0 .../hutool/core/tree/parser/NodeParser.java | 0 .../hutool/core/collection/ListUtilTest.java | 2 +- .../src/test/java/cn/hutool/db/CRUDTest.java | 2 +- .../java/cn/hutool/http/HttpResponse.java | 4 + .../main/java/cn/hutool/http/HttpUtil.java | 10 +- .../java/cn/hutool/http/HttpUtilTest.java | 16 + .../cn/hutool/json/mapper/package-info.java | Bin 113 -> 78 bytes .../java/cn/hutool/json/Issue2365Test.java | Bin 552 -> 0 bytes .../java/cn/hutool/json/Issue2369Test.java | Bin 557 -> 0 bytes .../java/cn/hutool/json/Issue2380Test.java | Bin 335 -> 0 bytes .../java/cn/hutool/json/Issue2447Test.java | 29 + .../java/cn/hutool/socket/SocketUtil.java | 6 +- .../java/cn/hutool/socket/nio/NioClient.java | 6 +- .../java/cn/hutool/socket/nio/NioServer.java | 5 +- 30 files changed, 1296 insertions(+), 45 deletions(-) create mode 100644 hutool-core/src/main/java/cn/hutool/core/map/ForestMap.java create mode 100644 hutool-core/src/main/java/cn/hutool/core/map/LinkedForestMap.java create mode 100644 hutool-core/src/main/java/cn/hutool/core/map/TreeEntry.java mode change 100755 => 100644 hutool-core/src/main/java/cn/hutool/core/tree/Node.java mode change 100755 => 100644 hutool-core/src/main/java/cn/hutool/core/tree/Tree.java mode change 100755 => 100644 hutool-core/src/main/java/cn/hutool/core/tree/TreeBuilder.java mode change 100755 => 100644 hutool-core/src/main/java/cn/hutool/core/tree/TreeNode.java mode change 100755 => 100644 hutool-core/src/main/java/cn/hutool/core/tree/TreeNodeConfig.java mode change 100755 => 100644 hutool-core/src/main/java/cn/hutool/core/tree/TreeUtil.java mode change 100755 => 100644 hutool-core/src/main/java/cn/hutool/core/tree/package-info.java mode change 100755 => 100644 hutool-core/src/main/java/cn/hutool/core/tree/parser/DefaultNodeParser.java mode change 100755 => 100644 hutool-core/src/main/java/cn/hutool/core/tree/parser/NodeParser.java delete mode 100644 hutool-json/src/test/java/cn/hutool/json/Issue2365Test.java delete mode 100644 hutool-json/src/test/java/cn/hutool/json/Issue2369Test.java delete mode 100644 hutool-json/src/test/java/cn/hutool/json/Issue2380Test.java create mode 100644 hutool-json/src/test/java/cn/hutool/json/Issue2447Test.java diff --git a/hutool-core/src/main/java/cn/hutool/core/collection/partition/Partition.java b/hutool-core/src/main/java/cn/hutool/core/collection/partition/Partition.java index fef05920d..1f81f3be0 100644 --- a/hutool-core/src/main/java/cn/hutool/core/collection/partition/Partition.java +++ b/hutool-core/src/main/java/cn/hutool/core/collection/partition/Partition.java @@ -1,5 +1,7 @@ package cn.hutool.core.collection.partition; +import cn.hutool.core.lang.Assert; + import java.util.AbstractList; import java.util.List; @@ -25,7 +27,7 @@ public class Partition extends AbstractList> { * @param size 每个分区的长度 */ public Partition(final List list, final int size) { - this.list = list; + this.list = Assert.notNull(list); this.size = Math.min(size, list.size()); } @@ -41,11 +43,9 @@ public class Partition extends AbstractList> { // 此处采用动态计算,以应对list变 final int size = this.size; final int total = list.size(); - int length = total / size; - if(total % size > 0){ - length += 1; - } - return length; + // 类似于判断余数,当总数非整份size时,多余的数>=1,则相当于被除数多一个size,做到+1目的 + // 类似于:if(total % size > 0){length += 1;} + return (total + size - 1) / size; } @Override 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 94da2e376..11a849ee1 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 @@ -85,6 +85,8 @@ public class TemporalAccessorConverter extends AbstractConverter { protected TemporalAccessor convertInternal(final Class targetClass, final Object value) { if (value instanceof Long) { return parseFromLong(targetClass, (Long) value); + }else if (value instanceof Integer) { + return parseFromLong(targetClass, ((Integer) value).longValue()); } else if (value instanceof TemporalAccessor) { return parseFromTemporalAccessor(targetClass, (TemporalAccessor) value); } else if (value instanceof Date) { diff --git a/hutool-core/src/main/java/cn/hutool/core/date/TimeUtil.java b/hutool-core/src/main/java/cn/hutool/core/date/TimeUtil.java index d092dd944..fa9b2c4a2 100644 --- a/hutool-core/src/main/java/cn/hutool/core/date/TimeUtil.java +++ b/hutool-core/src/main/java/cn/hutool/core/date/TimeUtil.java @@ -599,4 +599,28 @@ public class TimeUtil { public static int weekOfYear(final TemporalAccessor date) { return TemporalAccessorUtil.get(date, WeekFields.ISO.weekOfYear()); } + + /** + * 比较两个日期是否为同一天 + * + * @param date1 日期1 + * @param date2 日期2 + * @return 是否为同一天 + * @since 5.8.5 + */ + public static boolean isSameDay(final LocalDateTime date1, final LocalDateTime date2) { + return date1 != null && date2 != null && isSameDay(date1.toLocalDate(), date2.toLocalDate()); + } + + /** + * 比较两个日期是否为同一天 + * + * @param date1 日期1 + * @param date2 日期2 + * @return 是否为同一天 + * @since 5.8.5 + */ + public static boolean isSameDay(final LocalDate date1, final LocalDate date2) { + return date1 != null && date2 != null && date1.isEqual(date2); + } } diff --git a/hutool-core/src/main/java/cn/hutool/core/map/Dict.java b/hutool-core/src/main/java/cn/hutool/core/map/Dict.java index 1ed25c336..6ecd496dd 100755 --- a/hutool-core/src/main/java/cn/hutool/core/map/Dict.java +++ b/hutool-core/src/main/java/cn/hutool/core/map/Dict.java @@ -5,6 +5,7 @@ import cn.hutool.core.bean.BeanUtil; import cn.hutool.core.bean.copier.CopyOptions; import cn.hutool.core.collection.SetUtil; import cn.hutool.core.convert.Convert; +import cn.hutool.core.exceptions.CloneRuntimeException; import cn.hutool.core.lang.Assert; import cn.hutool.core.lang.func.Func0; import cn.hutool.core.lang.func.LambdaUtil; @@ -23,11 +24,11 @@ import java.util.Map; import java.util.Objects; /** - * 字典对象,扩充了HashMap中的方法 + * 字典对象,扩充了LinkedHashMap中的方法 * - * @author loolly + * @author looly */ -public class Dict extends LinkedHashMap implements BasicTypeGetter { +public class Dict extends CustomKeyMap implements BasicTypeGetter { private static final long serialVersionUID = 6135423866861206530L; static final float DEFAULT_LOAD_FACTOR = 0.75f; @@ -165,7 +166,7 @@ public class Dict extends LinkedHashMap implements BasicTypeGett * @since 4.5.16 */ public Dict(final int initialCapacity, final float loadFactor, final boolean caseInsensitive) { - super(initialCapacity, loadFactor); + super(new LinkedHashMap<>(initialCapacity, loadFactor)); this.caseInsensitive = caseInsensitive; } @@ -568,37 +569,21 @@ public class Dict extends LinkedHashMap implements BasicTypeGett } // -------------------------------------------------------------------- Get end - @Override - public Object get(final Object key) { - return super.get(customKey((String) key)); - } - - @Override - public Object put(final String key, final Object value) { - return super.put(customKey(key), value); - } - - @Override - public void putAll(final Map m) { - m.forEach(this::put); - } - @Override public Dict clone() { - return (Dict) super.clone(); + try { + return (Dict) super.clone(); + } catch (CloneNotSupportedException e) { + throw new CloneRuntimeException(e); + } } - /** - * 将Key转为小写 - * - * @param key KEY - * @return 小写KEY - */ - private String customKey(String key) { + @Override + protected String customKey(Object key) { if (this.caseInsensitive && null != key) { - key = key.toLowerCase(); + key = ((String)key).toLowerCase(); } - return key; + return (String) key; } /** diff --git a/hutool-core/src/main/java/cn/hutool/core/map/ForestMap.java b/hutool-core/src/main/java/cn/hutool/core/map/ForestMap.java new file mode 100644 index 000000000..997900843 --- /dev/null +++ b/hutool-core/src/main/java/cn/hutool/core/map/ForestMap.java @@ -0,0 +1,312 @@ +package cn.hutool.core.map; + +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.collection.SetUtil; +import cn.hutool.core.lang.Opt; +import cn.hutool.core.util.ObjUtil; + +import java.util.Collection; +import java.util.Collections; +import java.util.Map; +import java.util.Set; +import java.util.function.BiConsumer; +import java.util.function.Function; + +/** + * 基于多个{@link TreeEntry}构成的、彼此平行的树结构构成的森林集合。 + * + * @param key类型 + * @param value类型 + * @author huangchengxing + * @see TreeEntry + */ +public interface ForestMap extends Map> { + + // ===================== Map接口方法的重定义 ===================== + + /** + * 添加一个节点,效果等同于 {@code putNode(key, node.getValue())} + *
    + *
  • 若key对应节点不存在,则以传入的键值创建一个新的节点;
  • + *
  • 若key对应节点存在,则将该节点的值替换为{@code node}指定的值;
  • + *
+ * + * @param key 节点的key值 + * @param node 节点 + * @return 节点,若key已有对应节点,则返回具有旧值的节点,否则返回null + * @see #putNode(Object, Object) + */ + @Override + default TreeEntry put(K key, TreeEntry node) { + return putNode(key, node.getValue()); + } + + /** + * 批量添加节点,若节点具有父节点或者子节点,则一并在当前实例中引入该关系 + * + * @param treeEntryMap 节点集合 + */ + @Override + default void putAll(Map> treeEntryMap) { + if (CollUtil.isEmpty(treeEntryMap)) { + return; + } + treeEntryMap.forEach((k, v) -> { + if (v.hasParent()) { + final TreeEntry parent = v.getDeclaredParent(); + putLinkedNodes(parent.getKey(), parent.getValue(), v.getKey(), v.getValue()); + } else { + putNode(v.getKey(), v.getValue()); + } + }); + } + + // ===================== 节点操作 ===================== + + /** + * 批量添加节点 + * + * @param 集合类型 + * @param values 要添加的值 + * @param keyGenerator 从值中获取key的方法 + * @param parentKeyGenerator 从值中获取父节点key的方法 + * @param ignoreNullNode 是否获取到的key为null的子节点/父节点 + */ + default > void putAllNode( + C values, Function keyGenerator, Function parentKeyGenerator, boolean ignoreNullNode) { + if (CollUtil.isEmpty(values)) { + return; + } + values.forEach(v -> { + final K key = keyGenerator.apply(v); + final K parentKey = parentKeyGenerator.apply(v); + + // 不忽略keu为null节点 + final boolean hasKey = ObjUtil.isNotNull(key); + final boolean hasParentKey = ObjUtil.isNotNull(parentKey); + if (!ignoreNullNode || (hasKey && hasParentKey)) { + linkNodes(parentKey, key); + get(key).setValue(v); + return; + } + + // 父子节点的key都为null + if (!hasKey && !hasParentKey) { + return; + } + + // 父节点key为null + if (hasKey) { + putNode(key, v); + return; + } + + // 子节点key为null + putNode(parentKey, null); + }); + } + + /** + * 添加一个节点 + *
    + *
  • 若key对应节点不存在,则以传入的键值创建一个新的节点;
  • + *
  • 若key对应节点存在,则将该节点的值替换为{@code node}指定的值;
  • + *
+ * + * @param key 节点的key + * @param value 节点的value + * @return 节点,若key已有对应节点,则返回具有旧值的节点,否则返回null + */ + TreeEntry putNode(K key, V value); + + /** + * 同时添加父子节点: + *
    + *
  • 若{@code parentKey}或{@code childKey}对应的节点不存在,则会根据键值创建一个对应的节点;
  • + *
  • 若{@code parentKey}或{@code childKey}对应的节点存在,则会更新对应节点的值;
  • + *
+ * 该操作等同于: + *
{@code
+	 *     putNode(parentKey, parentValue);
+	 *     putNode(childKey, childValue);
+	 *     linkNodes(parentKey, childKey);
+	 * }
+ * + * @param parentKey 父节点的key + * @param parentValue 父节点的value + * @param childKey 子节点的key + * @param childValue 子节点的值 + */ + default void putLinkedNodes(K parentKey, V parentValue, K childKey, V childValue) { + putNode(parentKey, parentValue); + putNode(childKey, childValue); + linkNodes(parentKey, childKey); + } + + /** + * 添加子节点,并为子节点指定父节点: + *
    + *
  • 若{@code parentKey}或{@code childKey}对应的节点不存在,则会根据键值创建一个对应的节点;
  • + *
  • 若{@code parentKey}或{@code childKey}对应的节点存在,则会更新对应节点的值;
  • + *
+ * + * @param parentKey 父节点的key + * @param childKey 子节点的key + * @param childValue 子节点的值 + */ + void putLinkedNodes(K parentKey, K childKey, V childValue); + + /** + * 为集合中的指定的节点建立父子关系 + * + * @param parentKey 父节点的key + * @param childKey 子节点的key + */ + default void linkNodes(K parentKey, K childKey) { + linkNodes(parentKey, childKey, null); + } + + /** + * 为集合中的指定的节点建立父子关系 + * + * @param parentKey 父节点的key + * @param childKey 子节点的key + * @param consumer 对父节点和子节点的操作,允许为null + */ + void linkNodes(K parentKey, K childKey, BiConsumer, TreeEntry> consumer); + + /** + * 若{@code parentKey}或{@code childKey}对应节点都存在,则移除指定该父节点与其直接关联的指定子节点间的引用关系 + * + * @param parentKey 父节点的key + * @param childKey 子节点 + */ + void unlinkNode(K parentKey, K childKey); + + // ===================== 父节点相关方法 ===================== + + /** + * 获取指定节点所在树结构的全部树节点
+ * 比如:存在 a -> b -> c 的关系,则输入 a/b/c 都将返回 a, b, c + * + * @param key 指定节点的key + * @return 节点 + */ + default Set> getTreeNodes(K key) { + final TreeEntry target = get(key); + if (ObjUtil.isNull(target)) { + return Collections.emptySet(); + } + final Set> results = SetUtil.ofLinked(target.getRoot()); + CollUtil.addAll(results, target.getRoot().getChildren().values()); + return results; + } + + /** + * 获取以指定节点作为叶子节点的树结构,然后获取该树结构的根节点
+ * 比如:存在 a -> b -> c 的关系,则输入 a/b/c 都将返回 a + * + * @param key 指定节点的key + * @return 节点 + */ + default TreeEntry getRootNode(K key) { + return Opt.ofNullable(get(key)) + .map(TreeEntry::getRoot) + .orElse(null); + } + + /** + * 获取指定节点的直接父节点
+ * 比如:若存在 a -> b -> c 的关系,此时输入 a 将返回 null,输入 b 将返回 a,输入 c 将返回 b + * + * @param key 指定节点的key + * @return 节点 + */ + default TreeEntry getDeclaredParentNode(K key) { + return Opt.ofNullable(get(key)) + .map(TreeEntry::getDeclaredParent) + .orElse(null); + } + + /** + * 获取以指定节点作为叶子节点的树结构,然后获取该树结构中指定节点的指定父节点 + * + * @param key 指定节点的key + * @param parentKey 指定父节点key + * @return 节点 + */ + default TreeEntry getParentNode(K key, K parentKey) { + return Opt.ofNullable(get(key)) + .map(t -> t.getParent(parentKey)) + .orElse(null); + } + + /** + * 获取以指定节点作为叶子节点的树结构,然后确认该树结构中当前节点是否存在指定父节点 + * + * @param key 指定节点的key + * @param parentKey 指定父节点的key + * @return 是否 + */ + default boolean containsParentNode(K key, K parentKey) { + return Opt.ofNullable(get(key)) + .map(m -> m.containsParent(parentKey)) + .orElse(false); + } + + /** + * 获取指定节点的值 + * + * @param key 节点的key + * @return 节点值,若节点不存在,或节点值为null都将返回null + */ + default V getNodeValue(K key) { + return Opt.ofNullable(get(key)) + .map(TreeEntry::getValue) + .get(); + } + + // ===================== 子节点相关方法 ===================== + + /** + * 判断以该父节点作为根节点的树结构中是否具有指定子节点 + * + * @param parentKey 父节点 + * @param childKey 子节点 + * @return 是否 + */ + default boolean containsChildNode(K parentKey, K childKey) { + return Opt.ofNullable(get(parentKey)) + .map(m -> m.containsChild(childKey)) + .orElse(false); + } + + /** + * 获取指定父节点直接关联的子节点
+ * 比如:若存在 a -> b -> c 的关系,此时输入 b 将返回 c,输入 a 将返回 b + * + * @param key key + * @return 节点 + */ + default Collection> getDeclaredChildNodes(K key) { + return Opt.ofNullable(get(key)) + .map(TreeEntry::getDeclaredChildren) + .map(Map::values) + .orElseGet(Collections::emptyList); + } + + /** + * 获取指定父节点的全部子节点
+ * 比如:若存在 a -> b -> c 的关系,此时输入 b 将返回 c,输入 a 将返回 b,c + * + * @param key key + * @return 该节点的全部子节点 + */ + default Collection> getChildNodes(K key) { + return Opt.ofNullable(get(key)) + .map(TreeEntry::getChildren) + .map(Map::values) + .orElseGet(Collections::emptyList); + } + +} diff --git a/hutool-core/src/main/java/cn/hutool/core/map/LinkedForestMap.java b/hutool-core/src/main/java/cn/hutool/core/map/LinkedForestMap.java new file mode 100644 index 000000000..80c91bbfd --- /dev/null +++ b/hutool-core/src/main/java/cn/hutool/core/map/LinkedForestMap.java @@ -0,0 +1,744 @@ +package cn.hutool.core.map; + +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.collection.ListUtil; +import cn.hutool.core.lang.Assert; +import cn.hutool.core.reflect.ClassUtil; +import cn.hutool.core.text.StrUtil; +import cn.hutool.core.util.ObjUtil; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.Deque; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.Set; +import java.util.function.BiConsumer; +import java.util.function.BiPredicate; +import java.util.function.Consumer; +import java.util.function.Predicate; +import java.util.stream.Collectors; + +/** + * {@link ForestMap}的基本实现。 + * + *

该集合可以被视为以{@link TreeEntryNode#getKey()}作为key,{@link TreeEntryNode}实例作为value的{@link LinkedHashMap}。
+ * 使用时,将每一对键与值对视为一个{@link TreeEntryNode}节点,节点的id即为{@link TreeEntryNode#getKey()}, + * 任何情况下使用相同的key都将会访问到同一个节点。
+ * + *

节点通过key形成父子关系,并最终构成多叉树结构,多组平行的多叉树将在当前集合中构成森林。 + * 使用者可以通过{@link ForestMap}本身的方法来对森林进行操作或访问, + * 也可以在获取到{@link TreeEntry}后,使用节点本身的方法对数进行操作或访问。 + * + * @param key类型 + * @author huangchengxing + */ +public class LinkedForestMap implements ForestMap { + + /** + * 节点集合 + */ + private final Map> nodes; + + /** + * 当指定节点已经与其他节点构成了父子关系,是否允许将该节点的父节点强制替换为指定节点 + */ + private final boolean allowOverrideParent; + + /** + * 构建{@link LinkedForestMap} + * + * @param allowOverrideParent 当指定节点已经与其他节点构成了父子关系,是否允许将该节点的父节点强制替换为指定节点 + */ + public LinkedForestMap(boolean allowOverrideParent) { + this.allowOverrideParent = allowOverrideParent; + this.nodes = new LinkedHashMap<>(); + } + + // ====================== Map接口实现 ====================== + + /** + * 获取当前实例中的节点个数 + * + * @return 节点个数 + */ + @Override + public int size() { + return nodes.size(); + } + + /** + * 当前实例是否为空 + * + * @return 是否 + */ + @Override + public boolean isEmpty() { + return nodes.isEmpty(); + } + + /** + * 当前实例中是否存在key对应的节点 + * + * @param key key + * @return 是否 + */ + @Override + public boolean containsKey(Object key) { + return nodes.containsKey(key); + } + + /** + * 当前实例中是否存在对应的{@link TreeEntry}实例 + * + * @param value {@link TreeEntry}实例 + * @return 是否 + */ + @Override + public boolean containsValue(Object value) { + return nodes.containsValue(value); + } + + /** + * 获取key对应的节点 + * + * @param key key + * @return 节点 + */ + @Override + public TreeEntry get(Object key) { + return nodes.get(key); + } + + /** + * 将指定节点从当前{@link Map}中删除 + *

    + *
  • 若存在父节点或子节点,则将其断开其与父节点或子节点的引用关系;
  • + *
  • + * 若同时存在父节点或子节点,则会在删除后将让子节点直接成为父节点的子节点,比如:
    + * 现有引用关系 a -> b -> c,删除 b 后,将有 a -> c + *
  • + *
+ * + * @param key 节点的key + * @return 删除的且引用关系已经改变的节点,若key没有对应节点,则返回null + */ + @Override + public TreeEntry remove(Object key) { + final TreeEntryNode target = nodes.remove(key); + if (ObjUtil.isNull(target)) { + return null; + } + // 若存在父节点: + // 1.将该目标从父节点的子节点中移除 + // 2.将目标的子节点直接将目标的父节点作为父节点 + if (target.hasParent()) { + final TreeEntryNode parent = target.getDeclaredParent(); + final Map> targetChildren = target.getChildren(); + parent.removeDeclaredChild(target.getKey()); + target.clear(); + targetChildren.forEach((k, c) -> parent.addChild((TreeEntryNode) c)); + } + return target; + } + + /** + * 将当前集合清空,并清除全部节点间的引用关系 + */ + @Override + public void clear() { + nodes.values().forEach(TreeEntryNode::clear); + nodes.clear(); + } + + /** + * 返回当前实例中全部的key组成的{@link Set}集合 + * + * @return 集合 + */ + @Override + public Set keySet() { + return nodes.keySet(); + } + + /** + * 返回当前实例中全部{@link TreeEntry}组成的{@link Collection}集合 + * + * @return 集合 + */ + @Override + public Collection> values() { + return new ArrayList<>(nodes.values()); + } + + /** + * 由key与{@link TreeEntry}组成的键值对实体的{@link Set}集合。 + * 注意,返回集合中{@link Map.Entry#setValue(Object)}不支持调用。 + * + * @return 集合 + */ + @Override + public Set>> entrySet() { + return nodes.entrySet().stream() + .map(this::wrap) + .collect(Collectors.toSet()); + } + + /** + * 将{@link TreeEntryNode}包装为{@link EntryNodeWrapper} + */ + private Map.Entry> wrap(Map.Entry> nodeEntry) { + return new EntryNodeWrapper<>(nodeEntry.getValue()); + } + + // ====================== ForestMap接口实现 ====================== + + /** + * 添加一个节点 + *
    + *
  • 若key对应节点不存在,则以传入的键值创建一个新的节点;
  • + *
  • 若key对应节点存在,则将该节点的值替换为{@code node}指定的值;
  • + *
+ * + * @param key 节点的key + * @param value 节点的value + * @return 节点,若key已有对应节点,则返回具有旧值的节点,否则返回null + */ + @Override + public TreeEntryNode putNode(K key, V value) { + TreeEntryNode target = nodes.get(key); + if (ObjUtil.isNotNull(target)) { + final V oldVal = target.getValue(); + target.setValue(value); + return target.copy(oldVal); + } + target = new TreeEntryNode<>(null, key, value); + nodes.put(key, target); + return null; + } + + /** + * 同时添加父子节点: + *
    + *
  • 若{@code parentKey}或{@code childKey}对应的节点不存在,则会根据键值创建一个对应的节点;
  • + *
  • 若{@code parentKey}或{@code childKey}对应的节点存在,则会更新对应节点的值;
  • + *
+ * 该操作等同于: + *
+	 *     TreeEntry<K, V>  parent = putNode(parentKey, parentValue);
+	 *     TreeEntry<K, V>  child = putNode(childKey, childValue);
+	 *     linkNodes(parentKey, childKey);
+	 * 
+ * + * @param parentKey 父节点的key + * @param parentValue 父节点的value + * @param childKey 子节点的key + * @param childValue 子节点的值 + */ + @Override + public void putLinkedNodes(K parentKey, V parentValue, K childKey, V childValue) { + linkNodes(parentKey, childKey, (parent, child) -> { + parent.setValue(parentValue); + child.setValue(childValue); + }); + } + + /** + * 添加子节点,并为子节点指定父节点: + *
    + *
  • 若{@code parentKey}或{@code childKey}对应的节点不存在,则会根据键值创建一个对应的节点;
  • + *
  • 若{@code parentKey}或{@code childKey}对应的节点存在,则会更新对应节点的值;
  • + *
+ * + * @param parentKey 父节点的key + * @param childKey 子节点的key + * @param childValue 子节点的值 + */ + @Override + public void putLinkedNodes(K parentKey, K childKey, V childValue) { + linkNodes(parentKey, childKey, (parent, child) -> child.setValue(childValue)); + } + + /** + * 为指定的节点建立父子关系,若{@code parentKey}或{@code childKey}对应节点不存在,则会创建一个对应的值为null的空节点 + * + * @param parentKey 父节点的key + * @param childKey 子节点的key + * @param consumer 对父节点和子节点的操作,允许为null + */ + @Override + public void linkNodes(K parentKey, K childKey, BiConsumer, TreeEntry> consumer) { + consumer = ObjUtil.defaultIfNull(consumer, (parent, child) -> { + }); + final TreeEntryNode parentNode = nodes.computeIfAbsent(parentKey, t -> new TreeEntryNode<>(null, t)); + TreeEntryNode childNode = nodes.get(childKey); + + // 1.子节点不存在 + if (ObjUtil.isNull(childNode)) { + childNode = new TreeEntryNode<>(parentNode, childKey); + consumer.accept(parentNode, childNode); + nodes.put(childKey, childNode); + return; + } + + // 2.子节点存在,且已经是该父节点的子节点了 + if (ObjUtil.equals(parentNode, childNode.getDeclaredParent())) { + consumer.accept(parentNode, childNode); + return; + } + + // 3.子节点存在,但是未与其他节点构成父子关系 + if (false == childNode.hasParent()) { + parentNode.addChild(childNode); + } + // 4.子节点存在,且已经与其他节点构成父子关系,但是允许子节点直接修改其父节点 + else if (allowOverrideParent) { + childNode.getDeclaredParent().removeDeclaredChild(childNode.getKey()); + parentNode.addChild(childNode); + } + // 5.子节点存在,且已经与其他节点构成父子关系,但是不允许子节点直接修改其父节点 + else { + throw new IllegalArgumentException(StrUtil.format( + "[{}] has been used as child of [{}], can not be overwrite as child of [{}]", + childNode.getKey(), childNode.getDeclaredParent().getKey(), parentKey + )); + } + consumer.accept(parentNode, childNode); + } + + /** + * 移除指定父节点与其直接关联的子节点间的引用关系,但是不会将该节点从集合中删除 + * + * @param parentKey 父节点的key + * @param childKey 子节点 + */ + @Override + public void unlinkNode(K parentKey, K childKey) { + final TreeEntryNode childNode = nodes.get(childKey); + if (ObjUtil.isNull(childNode)) { + return; + } + if (childNode.hasParent()) { + childNode.getDeclaredParent().removeDeclaredChild(childNode.getKey()); + } + } + + /** + * 树节点 + * + * @param key类型 + * @author huangchengxing + */ + public static class TreeEntryNode implements TreeEntry { + + /** + * 根节点 + */ + private TreeEntryNode root; + + /** + * 父节点 + */ + private TreeEntryNode parent; + + /** + * 权重,表示到根节点的距离 + */ + private int weight; + + /** + * 子节点 + */ + private final Map> children; + + /** + * key + */ + private final K key; + + /** + * 值 + */ + private V value; + + /** + * 创建一个节点 + * + * @param parent 节点的父节点 + * @param key 节点的key + */ + public TreeEntryNode(TreeEntryNode parent, K key) { + this(parent, key, null); + } + + /** + * 创建一个节点 + * + * @param parent 节点的父节点 + * @param key 节点的key + * @param value 节点的value + */ + public TreeEntryNode(TreeEntryNode parent, K key, V value) { + this.parent = parent; + this.key = key; + this.value = value; + this.children = new LinkedHashMap<>(); + if (ObjUtil.isNull(parent)) { + this.root = this; + this.weight = 0; + } else { + parent.addChild(this); + this.weight = parent.weight + 1; + this.root = parent.root; + } + } + + /** + * 获取当前节点的key + * + * @return 节点的key + */ + @Override + public K getKey() { + return key; + } + + /** + * 获取当前节点与根节点的距离 + * + * @return 当前节点与根节点的距离 + */ + @Override + public int getWeight() { + return weight; + } + + /** + * 获取节点的value + * + * @return 节点的value + */ + @Override + public V getValue() { + return value; + } + + /** + * 设置节点的value + * + * @param value 节点的value + * @return 节点的旧value + */ + @Override + public V setValue(V value) { + final V oldVal = getValue(); + this.value = value; + return oldVal; + } + + // ================== 父节点的操作 ================== + + /** + * 从当前节点开始,向上递归当前节点的父节点 + * + * @param includeCurrent 是否处理当前节点 + * @param consumer 对节点的操作 + * @param breakTraverse 是否终止遍历 + * @return 遍历到的最后一个节点 + */ + TreeEntryNode traverseParentNodes( + boolean includeCurrent, Consumer> consumer, Predicate> breakTraverse) { + breakTraverse = ObjUtil.defaultIfNull(breakTraverse, n -> false); + TreeEntryNode curr = includeCurrent ? this : this.parent; + while (ObjUtil.isNotNull(curr)) { + consumer.accept(curr); + if (breakTraverse.test(curr)) { + break; + } + curr = curr.parent; + } + return curr; + } + + /** + * 当前节点是否为根节点 + * + * @return 当前节点是否为根节点 + */ + public boolean isRoot() { + return getRoot() == this; + } + + /** + * 获取以当前节点作为叶子节点的树结构,然后获取该树结构的根节点 + * + * @return 根节点 + */ + @Override + public TreeEntryNode getRoot() { + if (ObjUtil.isNotNull(this.root)) { + return this.root; + } else { + this.root = traverseParentNodes(true, p -> { + }, p -> !p.hasParent()); + } + return this.root; + } + + /** + * 获取当前节点直接关联的父节点 + * + * @return 父节点,当节点不存在对应父节点时返回null + */ + @Override + public TreeEntryNode getDeclaredParent() { + return parent; + } + + /** + * 获取以当前节点作为叶子节点的树结构,然后获取该树结构中当前节点的指定父节点 + * + * @param key 指定父节点的key + * @return 指定父节点,当不存在时返回null + */ + @Override + public TreeEntryNode getParent(K key) { + return traverseParentNodes(false, p -> { + }, p -> p.equalsKey(key)); + } + + /** + * 获取以当前节点作为根节点的树结构,然后遍历所有节点 + * + * @param includeSelf 是否处理当前节点 + * @param nodeConsumer 对节点的处理 + */ + @Override + public void forEachChild(boolean includeSelf, Consumer> nodeConsumer) { + traverseChildNodes(includeSelf, (index, child) -> nodeConsumer.accept(child), null); + } + + /** + * 指定key与当前节点的key是否相等 + * + * @param key 要比较的key + * @return 是否key一致 + */ + public boolean equalsKey(K key) { + return ObjUtil.equals(getKey(), key); + } + + // ================== 子节点的操作 ================== + + /** + * 从当前节点开始,按广度优先向下遍历当前节点的所有子节点 + * + * @param includeCurrent 是否包含当前节点 + * @param consumer 对节点与节点和当前节点的距离的操作,当{code includeCurrent}为false时下标从1开始,否则从0开始 + * @param breakTraverse 是否终止遍历,为null时默认总是返回{@code true} + * @return 遍历到的最后一个节点 + */ + TreeEntryNode traverseChildNodes( + boolean includeCurrent, BiConsumer> consumer, BiPredicate> breakTraverse) { + breakTraverse = ObjUtil.defaultIfNull(breakTraverse, (i, n) -> false); + final Deque>> keyNodeDeque = ListUtil.ofLinked(ListUtil.of(this)); + boolean needProcess = includeCurrent; + int index = includeCurrent ? 0 : 1; + TreeEntryNode lastNode = null; + while (!keyNodeDeque.isEmpty()) { + final List> curr = keyNodeDeque.removeFirst(); + final List> next = new ArrayList<>(); + for (final TreeEntryNode node : curr) { + if (needProcess) { + consumer.accept(index, node); + if (breakTraverse.test(index, node)) { + return node; + } + } else { + needProcess = true; + } + CollUtil.addAll(next, node.children.values()); + } + if (!next.isEmpty()) { + keyNodeDeque.addLast(next); + } + lastNode = CollUtil.getLast(next); + index++; + } + return lastNode; + } + + + /** + * 添加子节点 + * + * @param child 子节点 + * @throws IllegalArgumentException 当要添加的子节点已经是其自身父节点时抛出 + */ + void addChild(TreeEntryNode child) { + if (containsChild(child.key)) { + return; + } + + // 检查循环引用 + traverseParentNodes(true, s -> Assert.notEquals( + s.key, child.key, + "circular reference between [{}] and [{}]!", + s.key, this.key + ), null); + + // 调整该节点的信息 + child.parent = this; + child.traverseChildNodes(true, (i, c) -> { + c.root = getRoot(); + c.weight = i + getWeight() + 1; + }, null); + + // 将该节点添加为当前节点的子节点 + children.put(child.key, child); + } + + /** + * 移除子节点 + * + * @param key 子节点 + */ + void removeDeclaredChild(K key) { + final TreeEntryNode child = children.get(key); + if (ObjUtil.isNull(child)) { + return; + } + + // 断开该节点与其父节点的关系 + this.children.remove(key); + + // 重置子节点及其下属节点的相关属性 + child.parent = null; + child.traverseChildNodes(true, (i, c) -> { + c.root = child; + c.weight = i; + }, null); + } + + /** + * 获取以当前节点作为根节点的树结构,然后获取该树结构中的当前节点的指定子节点 + * + * @param key 指定子节点的key + * @return 节点 + */ + @Override + public TreeEntryNode getChild(K key) { + return traverseChildNodes(false, (i, c) -> { + }, (i, c) -> c.equalsKey(key)); + } + + /** + * 获取当前节点直接关联的子节点 + * + * @return 节点 + */ + @Override + public Map> getDeclaredChildren() { + return new LinkedHashMap<>(this.children); + } + + /** + * 获取以当前节点作为根节点的树结构,然后按广度优先获取该树结构中的当前节点的全部子节点 + * + * @return 节点 + */ + @Override + public Map> getChildren() { + final Map> childrenMap = new LinkedHashMap<>(); + traverseChildNodes(false, (i, c) -> childrenMap.put(c.getKey(), c), null); + return childrenMap; + } + + /** + * 移除对子节点、父节点与根节点的全部引用 + */ + void clear() { + this.root = null; + this.children.clear(); + this.parent = null; + } + + /** + * 比较目标对象与当前{@link TreeEntry}是否相等。
+ * 默认只要{@link TreeEntry#getKey()}的返回值相同,即认为两者相等 + * + * @param o 目标对象 + * @return 是否 + */ + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || this.getClass().equals(o.getClass()) || ClassUtil.isAssignable(this.getClass(), o.getClass())) { + return false; + } + final TreeEntry treeEntry = (TreeEntry) o; + return ObjUtil.equals(this.getKey(), treeEntry.getKey()); + } + + /** + * 返回当前{@link TreeEntry}的哈希值。
+ * 默认总是返回{@link TreeEntry#getKey()}的哈希值 + * + * @return 哈希值 + */ + @Override + public int hashCode() { + return Objects.hash(getKey()); + } + + /** + * 复制一个当前节点 + * + * @param value 复制的节点的值 + * @return 节点 + */ + TreeEntryNode copy(V value) { + TreeEntryNode copiedNode = new TreeEntryNode<>(this.parent, this.key, ObjUtil.defaultIfNull(value, this.value)); + copiedNode.children.putAll(children); + return copiedNode; + } + + } + + /** + * {@link java.util.Map.Entry}包装类 + * + * @param key类型 + * @param value类型 + * @param 包装的{@link TreeEntry}类型 + * @see #entrySet() + * @see #values() + */ + public static class EntryNodeWrapper> implements Map.Entry> { + private final N entryNode; + + EntryNodeWrapper(N entryNode) { + this.entryNode = entryNode; + } + + @Override + public K getKey() { + return entryNode.getKey(); + } + + @Override + public TreeEntry getValue() { + return entryNode; + } + + @Override + public TreeEntry setValue(TreeEntry value) { + throw new UnsupportedOperationException(); + } + } + +} diff --git a/hutool-core/src/main/java/cn/hutool/core/map/TransMap.java b/hutool-core/src/main/java/cn/hutool/core/map/TransMap.java index 3a33f0f88..e8c1b6dd2 100755 --- a/hutool-core/src/main/java/cn/hutool/core/map/TransMap.java +++ b/hutool-core/src/main/java/cn/hutool/core/map/TransMap.java @@ -70,7 +70,7 @@ public abstract class TransMap extends MapWrapper { @Override public boolean replace(final K key, final V oldValue, final V newValue) { - return super.replace(customKey(key), customValue(oldValue), customValue(values())); + return super.replace(customKey(key), customValue(oldValue), customValue(newValue)); } @Override diff --git a/hutool-core/src/main/java/cn/hutool/core/map/TreeEntry.java b/hutool-core/src/main/java/cn/hutool/core/map/TreeEntry.java new file mode 100644 index 000000000..f1dc68f80 --- /dev/null +++ b/hutool-core/src/main/java/cn/hutool/core/map/TreeEntry.java @@ -0,0 +1,122 @@ +package cn.hutool.core.map; + +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.util.ObjUtil; + +import java.util.Map; +import java.util.function.Consumer; + +/** + * 允许拥有一个父节点与多个子节点的{@link Map.Entry}实现, + * 表示一个以key作为唯一标识,并且可以挂载一个对应值的树节点, + * 提供一些基于该节点对其所在树结构进行访问的方法 + * + * @param 节点的key类型 + * @param 节点的value类型 + * @author huangchengxing + * @see ForestMap + */ +public interface TreeEntry extends Map.Entry { + + // ===================== 父节点相关方法 ===================== + + /** + * 获取以当前节点作为叶子节点的树结构,然后获取当前节点与根节点的距离 + * + * @return 当前节点与根节点的距离 + */ + int getWeight(); + + /** + * 获取以当前节点作为叶子节点的树结构,然后获取该树结构的根节点 + * + * @return 根节点 + */ + TreeEntry getRoot(); + + /** + * 当前节点是否存在直接关联的父节点 + * + * @return 是否 + */ + default boolean hasParent() { + return ObjUtil.isNotNull(getDeclaredParent()); + } + + /** + * 获取当前节点直接关联的父节点 + * + * @return 父节点,当节点不存在对应父节点时返回null + */ + TreeEntry getDeclaredParent(); + + /** + * 获取以当前节点作为叶子节点的树结构,然后获取该树结构中当前节点的指定父节点 + * + * @param key 指定父节点的key + * @return 指定父节点,当不存在时返回null + */ + TreeEntry getParent(K key); + + /** + * 获取以当前节点作为叶子节点的树结构,然后确认该树结构中当前节点是否存在指定父节点 + * + * @param key 指定父节点的key + * @return 是否 + */ + default boolean containsParent(K key) { + return ObjUtil.isNotNull(getParent(key)); + } + + // ===================== 子节点相关方法 ===================== + + /** + * 获取以当前节点作为根节点的树结构,然后遍历所有节点 + * + * @param includeSelf 是否处理当前节点 + * @param nodeConsumer 对节点的处理 + */ + void forEachChild(boolean includeSelf, Consumer> nodeConsumer); + + /** + * 获取当前节点直接关联的子节点 + * + * @return 节点 + */ + Map> getDeclaredChildren(); + + /** + * 获取以当前节点作为根节点的树结构,然后获取该树结构中的当前节点的全部子节点 + * + * @return 节点 + */ + Map> getChildren(); + + /** + * 当前节点是否有子节点 + * + * @return 是否 + */ + default boolean hasChildren() { + return CollUtil.isNotEmpty(getDeclaredChildren()); + } + + /** + * 获取以当前节点作为根节点的树结构,然后获取该树结构中的当前节点的指定子节点 + * + * @param key 指定子节点的key + * @return 节点 + */ + TreeEntry getChild(K key); + + /** + * 获取以当前节点作为根节点的树结构,然后确认该树结构中当前节点是否存在指定子节点 + * + * @param key 指定子节点的key + * @return 是否 + */ + default boolean containsChild(K key) { + return ObjUtil.isNotNull(getChild(key)); + } + +} diff --git a/hutool-core/src/main/java/cn/hutool/core/tree/Node.java b/hutool-core/src/main/java/cn/hutool/core/tree/Node.java old mode 100755 new mode 100644 diff --git a/hutool-core/src/main/java/cn/hutool/core/tree/Tree.java b/hutool-core/src/main/java/cn/hutool/core/tree/Tree.java old mode 100755 new mode 100644 diff --git a/hutool-core/src/main/java/cn/hutool/core/tree/TreeBuilder.java b/hutool-core/src/main/java/cn/hutool/core/tree/TreeBuilder.java old mode 100755 new mode 100644 diff --git a/hutool-core/src/main/java/cn/hutool/core/tree/TreeNode.java b/hutool-core/src/main/java/cn/hutool/core/tree/TreeNode.java old mode 100755 new mode 100644 diff --git a/hutool-core/src/main/java/cn/hutool/core/tree/TreeNodeConfig.java b/hutool-core/src/main/java/cn/hutool/core/tree/TreeNodeConfig.java old mode 100755 new mode 100644 diff --git a/hutool-core/src/main/java/cn/hutool/core/tree/TreeUtil.java b/hutool-core/src/main/java/cn/hutool/core/tree/TreeUtil.java old mode 100755 new mode 100644 diff --git a/hutool-core/src/main/java/cn/hutool/core/tree/package-info.java b/hutool-core/src/main/java/cn/hutool/core/tree/package-info.java old mode 100755 new mode 100644 diff --git a/hutool-core/src/main/java/cn/hutool/core/tree/parser/DefaultNodeParser.java b/hutool-core/src/main/java/cn/hutool/core/tree/parser/DefaultNodeParser.java old mode 100755 new mode 100644 diff --git a/hutool-core/src/main/java/cn/hutool/core/tree/parser/NodeParser.java b/hutool-core/src/main/java/cn/hutool/core/tree/parser/NodeParser.java old mode 100755 new mode 100644 diff --git a/hutool-core/src/test/java/cn/hutool/core/collection/ListUtilTest.java b/hutool-core/src/test/java/cn/hutool/core/collection/ListUtilTest.java index 65c57ef62..2d7bd9dc5 100644 --- a/hutool-core/src/test/java/cn/hutool/core/collection/ListUtilTest.java +++ b/hutool-core/src/test/java/cn/hutool/core/collection/ListUtilTest.java @@ -88,7 +88,7 @@ public class ListUtilTest { @Test public void editTest() { final List a = ListUtil.ofLinked("1", "2", "3"); - final List filter = (List) CollUtil.edit(a, str -> "edit" + str); + final List filter = CollUtil.edit(a, str -> "edit" + str); Assert.assertEquals("edit1", filter.get(0)); Assert.assertEquals("edit2", filter.get(1)); Assert.assertEquals("edit3", filter.get(2)); diff --git a/hutool-db/src/test/java/cn/hutool/db/CRUDTest.java b/hutool-db/src/test/java/cn/hutool/db/CRUDTest.java index 63f86e5ff..0e99f1dda 100644 --- a/hutool-db/src/test/java/cn/hutool/db/CRUDTest.java +++ b/hutool-db/src/test/java/cn/hutool/db/CRUDTest.java @@ -185,7 +185,7 @@ public class CRUDTest { Console.log(data1); - final int[] result = db.insert(ListUtil.of(data1)); + final int[] result = db.insert(ListUtil.of(new Entity[]{data1})); Console.log(result); } diff --git a/hutool-http/src/main/java/cn/hutool/http/HttpResponse.java b/hutool-http/src/main/java/cn/hutool/http/HttpResponse.java index 262c48d44..69e5f78d3 100755 --- a/hutool-http/src/main/java/cn/hutool/http/HttpResponse.java +++ b/hutool-http/src/main/java/cn/hutool/http/HttpResponse.java @@ -72,6 +72,7 @@ public class HttpResponse extends HttpBase implements Closeable { * @param isIgnoreBody 是否忽略读取响应体 * @since 3.1.2 */ + @SuppressWarnings("resource") protected HttpResponse(final HttpConnection httpConnection, final HttpConfig config, final Charset charset, final boolean isAsync, final boolean isIgnoreBody) { this.httpConnection = httpConnection; this.config = config; @@ -249,6 +250,7 @@ public class HttpResponse extends HttpBase implements Closeable { * * @return byte[] */ + @SuppressWarnings("resource") @Override public byte[] bodyBytes() { sync(); @@ -403,6 +405,7 @@ public class HttpResponse extends HttpBase implements Closeable { * @param bodyBytes 主体 * @return this */ + @SuppressWarnings("resource") public HttpResponse body(final byte[] bodyBytes) { sync(); if (null != bodyBytes) { @@ -479,6 +482,7 @@ public class HttpResponse extends HttpBase implements Closeable { * @return this * @throws HttpException IO异常 */ + @SuppressWarnings("resource") private HttpResponse initWithDisconnect() throws HttpException { try { init(); diff --git a/hutool-http/src/main/java/cn/hutool/http/HttpUtil.java b/hutool-http/src/main/java/cn/hutool/http/HttpUtil.java index 7b91c4120..d97d8d761 100755 --- a/hutool-http/src/main/java/cn/hutool/http/HttpUtil.java +++ b/hutool-http/src/main/java/cn/hutool/http/HttpUtil.java @@ -51,7 +51,7 @@ public class HttpUtil { * @return 是否https */ public static boolean isHttps(final String url) { - return url.toLowerCase().startsWith("https:"); + return StrUtil.startWithIgnoreCase(url, "https:"); } /** @@ -62,7 +62,7 @@ public class HttpUtil { * @since 5.3.8 */ public static boolean isHttp(final String url) { - return url.toLowerCase().startsWith("http:"); + return StrUtil.startWithIgnoreCase(url, "http:"); } /** @@ -118,6 +118,7 @@ public class HttpUtil { * @param customCharset 自定义请求字符集,如果字符集获取不到,使用此字符集 * @return 返回内容,如果只检查状态码,正常只返回 "",不正常返回 null */ + @SuppressWarnings("resource") public static String get(final String urlString, final Charset customCharset) { return HttpRequest.get(urlString).charset(customCharset).execute().body(); } @@ -140,6 +141,7 @@ public class HttpUtil { * @return 返回内容,如果只检查状态码,正常只返回 "",不正常返回 null * @since 3.2.0 */ + @SuppressWarnings("resource") public static String get(final String urlString, final int timeout) { return HttpRequest.get(urlString).timeout(timeout).execute().body(); } @@ -151,6 +153,7 @@ public class HttpUtil { * @param paramMap post表单数据 * @return 返回数据 */ + @SuppressWarnings("resource") public static String get(final String urlString, final Map paramMap) { return HttpRequest.get(urlString).form(paramMap).execute().body(); } @@ -164,6 +167,7 @@ public class HttpUtil { * @return 返回数据 * @since 3.3.0 */ + @SuppressWarnings("resource") public static String get(final String urlString, final Map paramMap, final int timeout) { return HttpRequest.get(urlString).form(paramMap).timeout(timeout).execute().body(); } @@ -188,6 +192,7 @@ public class HttpUtil { * @return 返回数据 * @since 3.2.0 */ + @SuppressWarnings("resource") public static String post(final String urlString, final Map paramMap, final int timeout) { return HttpRequest.post(urlString).form(paramMap).timeout(timeout).execute().body(); } @@ -224,6 +229,7 @@ public class HttpUtil { * @return 返回数据 * @since 3.2.0 */ + @SuppressWarnings("resource") public static String post(final String urlString, final String body, final int timeout) { return HttpRequest.post(urlString).timeout(timeout).body(body).execute().body(); } diff --git a/hutool-http/src/test/java/cn/hutool/http/HttpUtilTest.java b/hutool-http/src/test/java/cn/hutool/http/HttpUtilTest.java index 1162e825f..2890839ff 100755 --- a/hutool-http/src/test/java/cn/hutool/http/HttpUtilTest.java +++ b/hutool-http/src/test/java/cn/hutool/http/HttpUtilTest.java @@ -14,8 +14,24 @@ import java.util.LinkedHashMap; import java.util.List; import java.util.Map; +@SuppressWarnings("resource") public class HttpUtilTest { + @Test + public void isHttpTest(){ + Assert.assertTrue(HttpUtil.isHttp("Http://aaa.bbb")); + Assert.assertTrue(HttpUtil.isHttp("HTTP://aaa.bbb")); + Assert.assertFalse(HttpUtil.isHttp("FTP://aaa.bbb")); + } + + @Test + public void isHttpsTest(){ + Assert.assertTrue(HttpUtil.isHttps("Https://aaa.bbb")); + Assert.assertTrue(HttpUtil.isHttps("HTTPS://aaa.bbb")); + Assert.assertTrue(HttpUtil.isHttps("https://aaa.bbb")); + Assert.assertFalse(HttpUtil.isHttps("ftp://aaa.bbb")); + } + @Test @Ignore public void postTest() { diff --git a/hutool-json/src/main/java/cn/hutool/json/mapper/package-info.java b/hutool-json/src/main/java/cn/hutool/json/mapper/package-info.java index 443134405f6c4af91ae082b230ce05a23d7c3ace..c02f2db21955570bf2133bffc6d39f78b0b82780 100644 GIT binary patch literal 78 zcmdPb(&AFkQgBL5%zHYi$1B+1@5#>Ym(#aApVjhg#)79CTAprbe6hIoaYGYOnLbxR iVsdt3da6Qlo?b?2Nq&BgURH5_o?dQZK|yMfH5UMTaUwwg literal 113 LcmZQzpfdmf0C4~T diff --git a/hutool-json/src/test/java/cn/hutool/json/Issue2365Test.java b/hutool-json/src/test/java/cn/hutool/json/Issue2365Test.java deleted file mode 100644 index a3a35949d126532e85a4e7e365d715cdaf3ab164..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 552 PcmZQz7zLvtKsW>d0w@3h diff --git a/hutool-json/src/test/java/cn/hutool/json/Issue2369Test.java b/hutool-json/src/test/java/cn/hutool/json/Issue2369Test.java deleted file mode 100644 index fb2a6fd414d3e12f9cb90fb35a5e2fbc6429a7b7..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 557 QcmZQz7zLvtKx7C200J!l0RR91 diff --git a/hutool-json/src/test/java/cn/hutool/json/Issue2380Test.java b/hutool-json/src/test/java/cn/hutool/json/Issue2380Test.java deleted file mode 100644 index 69b0a135ed75b97dba0e84347a545fd01f8e49d3..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 335 NcmZQz7zI=e0RRC{00961 diff --git a/hutool-json/src/test/java/cn/hutool/json/Issue2447Test.java b/hutool-json/src/test/java/cn/hutool/json/Issue2447Test.java new file mode 100644 index 000000000..d9ccb17e8 --- /dev/null +++ b/hutool-json/src/test/java/cn/hutool/json/Issue2447Test.java @@ -0,0 +1,29 @@ +package cn.hutool.json; + +import lombok.Data; +import org.junit.Assert; +import org.junit.Test; + +import java.time.LocalDateTime; + +public class Issue2447Test { + + @Test + public void addIntegerTest() { + Time time = new Time(); + time.setTime(LocalDateTime.of(1970, 1, 2, 10, 0, 1, 0)); + String timeStr = JSONUtil.toJsonStr(time); + Assert.assertEquals(timeStr, "{\"time\":93601000}"); + Assert.assertEquals(JSONUtil.toBean(timeStr, Time.class).getTime(), time.getTime()); + } + + @Data + static class Time { + private LocalDateTime time; + + @Override + public String toString() { + return time.toString(); + } + } +} diff --git a/hutool-socket/src/main/java/cn/hutool/socket/SocketUtil.java b/hutool-socket/src/main/java/cn/hutool/socket/SocketUtil.java index b4a508449..e8f464697 100644 --- a/hutool-socket/src/main/java/cn/hutool/socket/SocketUtil.java +++ b/hutool-socket/src/main/java/cn/hutool/socket/SocketUtil.java @@ -23,8 +23,9 @@ public class SocketUtil { * * @param channel {@link AsynchronousSocketChannel} * @return 远程端的地址信息,包括host和端口,null表示channel为null或者远程主机未连接 + * @throws IORuntimeException IO异常 */ - public static SocketAddress getRemoteAddress(final AsynchronousSocketChannel channel) { + public static SocketAddress getRemoteAddress(final AsynchronousSocketChannel channel) throws IORuntimeException { try { return (null == channel) ? null : channel.getRemoteAddress(); } catch (final ClosedChannelException e) { @@ -41,8 +42,9 @@ public class SocketUtil { * * @param channel {@link AsynchronousSocketChannel} * @return 远程主机是否处于连接状态 + * @throws IORuntimeException IO异常 */ - public static boolean isConnected(final AsynchronousSocketChannel channel) { + public static boolean isConnected(final AsynchronousSocketChannel channel) throws IORuntimeException{ return null != getRemoteAddress(channel); } diff --git a/hutool-socket/src/main/java/cn/hutool/socket/nio/NioClient.java b/hutool-socket/src/main/java/cn/hutool/socket/nio/NioClient.java index f20ea4ca6..fd53eeb0e 100644 --- a/hutool-socket/src/main/java/cn/hutool/socket/nio/NioClient.java +++ b/hutool-socket/src/main/java/cn/hutool/socket/nio/NioClient.java @@ -3,6 +3,7 @@ package cn.hutool.socket.nio; import cn.hutool.core.io.IORuntimeException; import cn.hutool.core.io.IoUtil; import cn.hutool.core.thread.ThreadUtil; +import cn.hutool.log.Log; import cn.hutool.socket.SocketRuntimeException; import java.io.Closeable; @@ -21,6 +22,7 @@ import java.util.Iterator; * @since 4.4.5 */ public class NioClient implements Closeable { + private static final Log log = Log.get(); private Selector selector; private SocketChannel channel; @@ -32,6 +34,7 @@ public class NioClient implements Closeable { * @param host 服务器地址 * @param port 端口 */ + @SuppressWarnings("resource") public NioClient(final String host, final int port) { init(new InetSocketAddress(host, port)); } @@ -41,6 +44,7 @@ public class NioClient implements Closeable { * * @param address 服务器地址 */ + @SuppressWarnings("resource") public NioClient(final InetSocketAddress address) { init(address); } @@ -91,7 +95,7 @@ public class NioClient implements Closeable { try { doListen(); } catch (final IOException e) { - e.printStackTrace(); + log.error("Listen failed", e); } }); } diff --git a/hutool-socket/src/main/java/cn/hutool/socket/nio/NioServer.java b/hutool-socket/src/main/java/cn/hutool/socket/nio/NioServer.java index dedc0cd66..204a18cae 100644 --- a/hutool-socket/src/main/java/cn/hutool/socket/nio/NioServer.java +++ b/hutool-socket/src/main/java/cn/hutool/socket/nio/NioServer.java @@ -3,7 +3,6 @@ package cn.hutool.socket.nio; import cn.hutool.core.io.IORuntimeException; import cn.hutool.core.io.IoUtil; import cn.hutool.log.Log; -import cn.hutool.log.StaticLog; import java.io.Closeable; import java.io.IOException; @@ -34,6 +33,7 @@ public class NioServer implements Closeable { * * @param port 端口 */ + @SuppressWarnings("resource") public NioServer(final int port) { init(new InetSocketAddress(port)); } @@ -58,6 +58,7 @@ public class NioServer implements Closeable { // 服务器套接字注册到Selector中 并指定Selector监控连接事件 this.serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT); } catch (final IOException e) { + close(); throw new IORuntimeException(e); } @@ -140,7 +141,7 @@ public class NioServer implements Closeable { handler.handle(socketChannel); } catch (final Exception e){ IoUtil.close(socketChannel); - StaticLog.error(e); + log.error(e); } } }