All Downloads are FREE. Search and download functionalities are using the official Maven repository.

io.github.jinghui70.rainbow.utils.tree.TreeUtils Maven / Gradle / Ivy

The newest version!
package io.github.jinghui70.rainbow.utils.tree;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.StrUtil;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.LinkedList;
import java.util.List;
import java.util.function.Consumer;
import java.util.function.Predicate;

public class TreeUtils {

    /**
     * Recursively traverse a tree structure and apply a consumer to each node.
     *
     * @param treeNode the root node to traverse
     * @param action the consumer to apply to each node
     * @param isPreOrder true: traverse in pre-order; false: traverse in post-order
     * @param       the type of the node
     */
    public static > void traverse(T treeNode, Consumer action, boolean isPreOrder) {
        if (treeNode == null) return;
        if (isPreOrder) action.accept(treeNode);
        traverse(treeNode.getChildren(), action, isPreOrder);
        if (!isPreOrder) action.accept(treeNode);
    }

    /**
     * Recursively traverse a tree structure and apply a consumer to each node.
     *
     * @param tree     the root nodes to traverse
     * @param consumer the consumer to apply to each node
     * @param isPreOrder true: traverse in pre-order; false: traverse in post-order
     * @param       the type of the node
     */
    public static > void traverse(List tree, Consumer consumer, boolean isPreOrder) {
        if (CollUtil.isEmpty(tree)) return;
        for (T node : tree) {
            traverse(node, consumer, isPreOrder);
        }
    }

    /**
     * Recursively traverse a tree structure and apply a consumer to each node.
     *
     * @param tree     the root nodes to traverse
     * @param consumer the consumer to apply to each node
     * @param       the type of the node
     */
    public static > void traverse(List tree, Consumer consumer) {
        traverse(tree, consumer, true);
    }

    @SuppressWarnings("unchecked")
    private static > T cloneWithChildren(T node, List children, Method cloneMethod) {
        if (cloneMethod != null) {
            try {
                node = (T) cloneMethod.invoke(node);
            } catch (IllegalAccessException | InvocationTargetException ignored) {
            }
        }
        node.setChildren(children);
        return node;
    }

    private static > T filter(T node, Predicate predicate, FilterType filterType, Method cloneMethod) {
        boolean hasChild = CollUtil.isNotEmpty(node.getChildren());
        if (predicate.test(node)) {
            switch (filterType) {
                case MATCH_FIRST:
                    return cloneWithChildren(node, null, cloneMethod);
                case MATCH_ALL:
                    if (hasChild) {
                        List children = filter(node.getChildren(), predicate, filterType, cloneMethod);
                        if (CollUtil.isEmpty(children)) children = null;
                        return cloneWithChildren(node, children, cloneMethod);
                    }
                    break;
            }
            return node;
        }
        if (hasChild) {
            List children = filter(node.getChildren(), predicate, filterType, cloneMethod);
            if (CollUtil.isNotEmpty(children)) {
                return cloneWithChildren(node, children, cloneMethod);
            }
        }
        return null;
    }

    private static > List filter(List list, Predicate predicate, FilterType filterType, Method cloneMethod) {
        List result = new LinkedList<>();
        for (T node : list) {
            T matchNode = filter(node, predicate, filterType, cloneMethod);
            if (matchNode != null) {
                result.add(matchNode);
                if (filterType == FilterType.MATCH_FIRST || filterType == FilterType.MATCH_FIRST_FULL) {
                    break;
                }
            }
        }
        return result;
    }

    /**
     * Filter a tree-like list according to the given predicate and filter type.
     *
     * @param list       the tree-like list
     * @param predicate  the condition to filter
     * @param filterType the filter type
     * @return the filtered list
     * @param  tree node data type
     */
    public static > List filter(List list, Predicate predicate, FilterType filterType) {
        if (CollUtil.isEmpty(list)) return list;
        Method cloneMethod = null;
        if (list.get(0) instanceof Cloneable) {
            Class clazz = list.get(0).getClass();
            try {
                cloneMethod = clazz.getDeclaredMethod("clone");
                cloneMethod.setAccessible(true);
            } catch (NoSuchMethodException e) {
                throw new RuntimeException(StrUtil.format("clone method of {} not defined", clazz.getName()));
            }
        }
        return filter(list, predicate, filterType, cloneMethod);
    }

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy