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

com.wl4g.component.common.function.TreeConvertor Maven / Gradle / Ivy

/*
 * Copyright 2017 ~ 2025 the original author or authors. 
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.wl4g.component.common.function;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

import javax.validation.constraints.NotNull;

import org.apache.commons.lang3.StringUtils;

import com.wl4g.component.common.annotation.Todo;

import javax.annotation.Nullable;
import javax.annotation.concurrent.NotThreadSafe;

import static com.wl4g.component.common.function.TreeConvertor.TreeNode;
import static com.wl4g.component.common.lang.Assert2.notNullOf;

/**
 * 
 * 平面(List)树与children树互转器. {@link TreeConvertor}
 *
 * @author Wangl.sir 
 * @version v1.0 2017-09-08
 * @since
 * @param 
 */
@NotThreadSafe
public class TreeConvertor> {
	private final String rootParentId;
	private final NodeIdMatcher matcher;
	private final List subChildrens = new ArrayList<>();
	private final List retList = new ArrayList<>();

	public TreeConvertor() {
		this(null, NodeIdMatcher.defaultInstance);
	}

	/**
	 * 创建树结构转换器
	 * 
	 * @param rootParentId
	 *            跟节点的parentId的值
	 */
	public TreeConvertor(@Nullable String rootParentId, @NotNull NodeIdMatcher matcher) {
		this.rootParentId = rootParentId;
		this.matcher = notNullOf(matcher, "matcher");
	}

	/**
	 * 将平面树格式化为children树
	 * 
	 * @param planeTree
	 *            平面结构的树列表
	 * @param isFilterLowest
	 *            是否过滤(每层)最低级的节点
	 * @return 返回包含children节点关系的树
	 */
	public List formatToChildren(List planeTree, boolean isFilterLowest) {
		// 1.1 parse children tree.
		List childrenTree = new ArrayList();

		for (T n : planeTree) {
			if (n != null && matcher.eq(n.getParentId(), rootParentId)) {
				childrenTree.add(n);
			}

			for (T t : planeTree) {
				if (t != null && n != null && matcher.eq(t.getParentId(), n.getId())) {

					if (emptyChildrens(n)) {
						List childrens = new ArrayList();
						childrens.add(t);
						n.setChildrens(childrens);
					} else
						n.getChildrens().add(t);
				}
			}
		}

		// 2.1 filter children.
		childrenTree = isFilterLowest ? filterChildren(childrenTree) : childrenTree;

		// 2.2 recursion level/total set.
		return childrenLevelSet(null, childrenTree);
	}

	/**
	 * 将children解析为平面树
	 * 
	 * @param childrenTree
	 *            children结构的树列表
	 * @return 返回不包含children节点关系(以parentId做父子关系)的平面树
	 */
	public List parseChildren(List childrenTree) {

		if (childrenTree != null) {
			for (T n : childrenTree) {
				if (!emptyChildrens(n)) {
					parseChildren(n.getChildrens());
					n.getChildrens().clear();
				}
				retList.add(n);
			}
		}

		return retList;
	}

	/**
	 * 依据父ID获取所有子、孙等节点列表
	 * 
	 * @param childrenTree
	 *            children树列表
	 * @param pId
	 *            目标父级ID
	 * @return 返回包含pId以及所有子、孙节点
	 */
	public List subChildrens(List childrenTree, String pId) {

		if (childrenTree != null) {
			for (T t : childrenTree) {
				if (matcher.eq(t.getId(), String.valueOf(pId)))
					subChildrens.add(t);

				// 继续递归直到找到匹配节点为止.
				subChildrens(t.getChildrens(), pId);
			}
		}

		return subChildrens;
	}

	/**
	 * 子节点递归level设置
	 * 
	 * @param parent
	 *            父节点
	 * @param childrenTree
	 *            对应子节点列表
	 * @return
	 * @sine
	 */
	private List childrenLevelSet(T parent, List childrenTree) {

		for (T t : childrenTree) {
			if (matcher.eq(t.getParentId(), rootParentId)) {
				t.setLevel(0);
			} else if (parent != null) {
				increaseLevel(t, parent.getLevel());
			}

			if (!emptyChildrens(t)) {
				// 继续递归下级节点.
				childrenLevelSet(t, t.getChildrens());
			}
		}

		return childrenTree;
	}

	/**
	 * 递归过滤最底层节点
* 注: List做删除时不能直接用list.get(i).remove(object); * http://www.cnblogs.com/zhangfei/p/4510584.html * * @param childrenTree * @return */ private List filterChildren(List childrenTree) { Iterator it = childrenTree.iterator(); while (it.hasNext()) { T t = it.next(); if (!emptyChildrens(t)) // 继续递归直到找到匹配节点为止. filterChildren(t.getChildrens()); else it.remove(); } return childrenTree; } /** * 是否存在子节点 * * @param t * @return */ private boolean emptyChildrens(T t) { return (t.getChildrens() == null || t.getChildrens().isEmpty()); } /** * 增加设置节点level * * @param t * 目标节点 * @param parentLevel * 父节点级别 */ private void increaseLevel(T t, int parentLevel) { t.setLevel(++parentLevel); } /** * 子节点递归累加设置 * * @param t * @param parentLevel */ @Todo public List childrenTotalSet(List childrenTree) { // TODO // for (T t : childrenTree) { // if (matcher.eq(t.getParentId(), rootParentId)) { // // Map nodeSubs = new HashMap(); // t.setSum(0); // } // if (!emptyChildrens(t)) { // // 继续递归下级节点. // childrenTotalSet(t, t.getChildrens()); // } // } return childrenTree; } /** * TreeConvert转换器节点操作接口. {@link TreeNode} * * @author Wangl.sir * @version v1.0 2017-09-08 * @since * @param */ public static interface TreeNode extends Serializable { // --- Tree node basic. --- String getId(); void setId(String id); String getParentId(); void setParentId(String parentId); int getLevel(); void setLevel(int level); List getChildrens(); void setChildrens(List childrens); // --- Node statistics. --- default int getCount() { // Ignore return -1; } default void setCount(int count) { // Ignore } default Double getSum() { // Ignore return null; } default void setSum(Double sum) { // Ignore } default Double getValue() { // Ignore return null; } default void setValue(Double data) { // Ignore } } /** * {@link NodeIdMatcher} * * @author Wangl.sir * @version v1.0 2020-11-04 * @since */ public static interface NodeIdMatcher { /** * Check nodes ID is equals. * * @param nodeId1 * @param nodeId2 * @return */ boolean eq(String nodeId1, String nodeId2); /** * Default {@link NodeIdMatcher} instance of string equals. */ public static final NodeIdMatcher defaultInstance = (nodeId1, nodeId2) -> StringUtils.equals(nodeId1, nodeId2); } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy