From bcf1a4e5a0e9d6df437abf0490a811189eb3fff8 Mon Sep 17 00:00:00 2001 From: ZhouXY108 Date: Sun, 4 Jun 2023 00:00:00 +0800 Subject: [PATCH] =?UTF-8?q?=E6=96=B0=E5=A2=9E=20TreeBuilder=20=E5=B7=A5?= =?UTF-8?q?=E5=85=B7=E7=B1=BB=E3=80=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../plusone/commons/util/TreeBuilder.java | 39 +++++ .../commons/util/TreeBuilderTests.java | 136 ++++++++++++++++++ 2 files changed, 175 insertions(+) create mode 100644 src/main/java/xyz/zhouxy/plusone/commons/util/TreeBuilder.java create mode 100644 src/test/java/xyz/zhouxy/plusone/commons/util/TreeBuilderTests.java diff --git a/src/main/java/xyz/zhouxy/plusone/commons/util/TreeBuilder.java b/src/main/java/xyz/zhouxy/plusone/commons/util/TreeBuilder.java new file mode 100644 index 0000000..ca4d49e --- /dev/null +++ b/src/main/java/xyz/zhouxy/plusone/commons/util/TreeBuilder.java @@ -0,0 +1,39 @@ +package xyz.zhouxy.plusone.commons.util; + +import java.util.Collection; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.function.BiConsumer; +import java.util.function.Function; +import java.util.stream.Collectors; + +public class TreeBuilder { + private final Collection nodes; + private final Function identityGetter; + private final Function> parentIdentityGetter; + private final BiConsumer addChildrenMethod; + + public TreeBuilder(Collection nodes, Function identityGetter, + Function> parentIdentityGetter, BiConsumer addChildren) { + this.nodes = nodes; + this.identityGetter = identityGetter; + this.parentIdentityGetter = parentIdentityGetter; + this.addChildrenMethod = addChildren; + } + + public List buildTree() { + Map identityNodeMap = MoreCollections.toHashMap(nodes, identityGetter); + List result = this.nodes.stream() + .filter(node -> !this.parentIdentityGetter.apply(node).isPresent()) + .collect(Collectors.toList()); + for (T node : this.nodes) { + Optional parentIdentity = parentIdentityGetter.apply(node); + if (parentIdentity.isPresent() && identityNodeMap.containsKey(parentIdentity.get())) { + T parentNode = identityNodeMap.get(parentIdentity.get()); + addChildrenMethod.accept(parentNode, node); + } + } + return result; + } +} diff --git a/src/test/java/xyz/zhouxy/plusone/commons/util/TreeBuilderTests.java b/src/test/java/xyz/zhouxy/plusone/commons/util/TreeBuilderTests.java new file mode 100644 index 0000000..446ec35 --- /dev/null +++ b/src/test/java/xyz/zhouxy/plusone/commons/util/TreeBuilderTests.java @@ -0,0 +1,136 @@ +package xyz.zhouxy.plusone.commons.util; + +import java.util.List; +import java.util.Optional; + +import org.junit.jupiter.api.Test; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.google.common.collect.Lists; + +import lombok.ToString; + +class TreeBuilderTests { + + private static final Logger log = LoggerFactory.getLogger(TreeBuilderTests.class); + + @Test + void testBuildTree() { + List menus = Lists.newArrayList( + MenuItem.of("A", "首页", "/home"), + MenuList.of("B", "系统管理"), + MenuItem.of("B", "B001", "功能管理", "/sys/function-mgmt"), + MenuItem.of("B", "B002", "角色管理", "/sys/role-mgmt"), + MenuItem.of("B", "B003", "账号管理", "/sys/account-mgmt"), + MenuItem.of("B", "B004", "系统参数管理", "/sys/param-mgmt"), + MenuList.of("C", "一级菜单C"), + MenuList.of("C", "C1", "二级菜单C1"), + MenuItem.of("C1", "C1001", "三级菜单C1001", "/c/c1/c1001"), + MenuItem.of("C1", "C1002", "三级菜单C1002", "/c/c1/c1002"), + MenuItem.of("C", "C2", "二级菜单C2", "/c/c2"), + MenuItem.of("C", "C3", "二级菜单C3", "/c/c3") + ); + List menuTree = new TreeBuilder( + menus, + Menu::getMenuCode, + Menu::getParentMenuCode, + Menu::addChild) + .buildTree(); + log.info("menuTree: {}", menuTree); + } +} + +@ToString +abstract class Menu { + protected final String parentMenuCode; + protected final String menuCode; + protected final String title; + + public Menu(String parentMenuCode, String menuCode, String title) { + this.parentMenuCode = parentMenuCode; + this.menuCode = menuCode; + this.title = title; + } + + public String getMenuCode() { + return menuCode; + } + + public Optional getParentMenuCode() { + return Optional.ofNullable(parentMenuCode); + } + + public String getTitle() { + return title; + } + + public abstract void addChild(Menu child); +} + +@ToString(callSuper = true) +class MenuItem extends Menu { + + private final String url; + + private MenuItem(String parentMenuCode, String menuCode, String title, String url) { + super(parentMenuCode, menuCode, title); + this.url = url; + } + + static MenuItem of(String parentMenuCode, String menuCode, String title, String url) { + return new MenuItem(parentMenuCode, menuCode, title, url); + } + + static MenuItem of(String menuCode, String title, String url) { + return new MenuItem(null, menuCode, title, url); + } + + public String getUrl() { + return url; + } + + @Override + @Deprecated + public void addChild(Menu child) { + throw new UnsupportedOperationException("Unimplemented method 'addChild'"); + } +} + +@ToString(callSuper = true) +class MenuList extends Menu { + + private List children; + + private MenuList(String parentMenuCode, String menuCode, String title) { + super(parentMenuCode, menuCode, title); + } + + static MenuList of(String parentMenuCode, String menuCode, String title) { + return new MenuList(parentMenuCode, menuCode, title); + } + + static MenuList of(String menuCode, String title) { + return new MenuList(null, menuCode, title); + } + + static MenuList of(String menuCode, String title, Iterable children) { + return of(null, menuCode, title, children); + } + + static MenuList of(String parentMenuCode, String menuCode, String title, Iterable children) { + MenuList instance = of(parentMenuCode, menuCode, title); + for (Menu child : children) { + instance.addChild(child); + } + return instance; + } + + @Override + public void addChild(Menu child) { + if (this.children == null) { + this.children = Lists.newArrayList(); + } + this.children.add(child); + } +}