util
public class TreeUtil {
private static final Logger log = LoggerFactory.getLogger(TreeUtil.class);
public static final Long rootId = 0L;
@Deprecated
@SuppressWarnings("unchecked")
public static <E extends TreeNode<E, T>, T> List<E> buildTree(List<E> source) {
return source.stream()
.filter(TreeUtil::isRootNode)
.peek(treeNode -> treeNode.setChildren(isNotEmpty(buildChildrenTree(treeNode, source)) ? (List<TreeNode<E, T>>) buildChildrenTree(treeNode, source) : null))
.collect(Collectors.toList());
}
@Deprecated
@SuppressWarnings("unchecked")
public static <E extends TreeNode<E, T>, T> List<E> buildChildrenTree(TreeNode<E, T> currentNode, List<E> source) {
if (currentNode == null || isNotValid(source)) {
log.error("currentNode is not be null or source is not be null and parentId, id is not be null!");
throw new IllegalArgumentException("currentNode is not be null or source is not valid!");
}
return source.stream()
.filter(treeNode -> Objects.equals(currentNode.getId(), treeNode.getParentId()))
.peek(treeNode -> treeNode.setChildren(isNotEmpty(buildChildrenTree(treeNode, source)) ? (List<TreeNode<E, T>>) buildChildrenTree(treeNode, source) : null))
.collect(Collectors.toList());
}
public static <E extends TreeNode<E, T>, T> List<E> toTree(List<E> source) {
if (isNotValid(source)) {
log.error("source is not be null and parentId, id is not be null!");
throw new IllegalArgumentException("source is not valid!");
}
Map<T, List<TreeNode<E, T>>> treeNodeMap = source.stream().collect(Collectors.groupingBy(TreeNode::getParentId, LinkedHashMap::new, Collectors.toList()));
return source.stream().filter(Objects::nonNull).peek(node -> node.setChildren(treeNodeMap.get(node.getId()))).filter(TreeUtil::isRootNode).collect(Collectors.toList());
}
public static <E extends TreeNode<E, T>, T> List<E> toTree(T currentNodeId, List<E> source) {
if (currentNodeId == null || isNotValid(source)) {
log.error("currentNodeId is not be null or source is not be null and parentId, id is not be null!");
throw new IllegalArgumentException("currentNodeId is not be null or source is not valid!");
}
Map<T, List<TreeNode<E, T>>> treeNodeMap = source.stream().collect(Collectors.groupingBy(TreeNode::getParentId, LinkedHashMap::new, Collectors.toList()));
return source.stream().filter(Objects::nonNull).peek(node -> node.setChildren(treeNodeMap.get(node.getId()))).filter(node -> Objects.equals(node.getId(), currentNodeId)).collect(Collectors.toList());
}
public static <E extends TreeNode<E, T>, T> List<E> parentTree(T currentNodeId, List<E> source) {
if (currentNodeId == null || isNotValid(source)) {
log.error("currentNodeId is not be null or source is not be null and parentId, id is not be null!");
throw new IllegalArgumentException("currentNodeId is not be null or source is not valid!");
}
Map<T, E> nodeMap = source.stream().collect(Collectors.toMap(E::getId, Function.identity()));
E currentNode = nodeMap.get(currentNodeId);
if (currentNode == null) {
return Collections.emptyList();
}
List<E> parents = new ArrayList<>();
E parentNode = nodeMap.get(currentNode.getParentId());
while (parentNode != null) {
parents.add(parentNode);
parentNode = nodeMap.get(parentNode.getParentId());
}
return toTree(parents);
}
public static <E, T> boolean isRootNode(TreeNode<E, T> treeNode) {
return treeNode.getParentId().equals(rootId);
}
public static <E, T> boolean isNotRootNode(TreeNode<E, T> treeNode) {
return !isRootNode(treeNode);
}
public static <E extends TreeNode<E, T>, T> boolean isNotValid(List<E> source) {
return source == null || source.stream().anyMatch(e ->
Objects.isNull(e.getParentId()) || Objects.isNull(e.getId()) || Objects.equals(e.getId(), e.getParentId()));
}
public static boolean isEmpty(Collection<?> source) {
return source == null || source.isEmpty();
}
public static boolean isNotEmpty(Collection<?> source) {
return !isEmpty(source);
}
}
TreeNode
@Data
@SuperBuilder
@AllArgsConstructor
@NoArgsConstructor
public abstract class TreeNode<E, T> implements Serializable{
private static final long serialVersionUID = 8817694414118390415L;
private T id;
private T parentId;
private String label;
private Map<String, Object> extra;
private List<TreeNode<E, T>> children;
}