summaryrefslogtreecommitdiff
path: root/base/src/main/java/bjc/utils/funcutils/TreeUtils.java
blob: daab8a18a1ce8d7891bb2a22251ea73ad2c9ffd9 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
package bjc.utils.funcutils;

import java.util.LinkedList;
import java.util.function.*;

import bjc.data.*;
import bjc.funcdata.*;

/** Implements various utilities for trees.
 *
 * @author Benjamin Culkin */
public class TreeUtils {
	/** Convert a tree into a list of outline nodes that match a certain path.
	 *
	 * @param <T> The type contained in the tree.
	 *
	 * @param tre The tree to outline.
	 * @param leafMarker The path to mark nodes with.
	 *
	 * @return The list of marked paths. */
	public static <T> ListEx<ListEx<T>> outlineTree(Tree<T> tre, Predicate<T> leafMarker) {
		ListEx<ListEx<T>> paths = new FunctionalList<>();

		LinkedList<T> path = new LinkedList<>();
		path.add(tre.getHead());

		tre.doForChildren(child -> findPath(child, path, leafMarker, paths));

		return paths;
	}

	/* Find a path in a tree. */
	private static <T> void findPath(Tree<T> subtree, LinkedList<T> path,
			Predicate<T> leafMarker, ListEx<ListEx<T>> paths) {
		if (subtree.getChildrenCount() == 0 && leafMarker.test(subtree.getHead())) {
			/* We're at a matching leaf node. Add it. */
			ListEx<T> finalPath = new FunctionalList<>();

			for (T ePath : path) finalPath.add(ePath);

			finalPath.add(subtree.getHead());

			paths.add(finalPath);
		} else {
			/* Check the children of this node. */
			path.add(subtree.getHead());

			subtree.doForChildren(child -> findPath(child, path, leafMarker, paths));

			path.removeLast();
		}
	}
	
	/** Performs 'variable substitution' or something along those lines on a tree.
	 * 
	 * @param <ContainedType> The type of element contained in the tree.
	 *
	 * @param tree The tree to do expansion in.
	 * @param marker The function to mark which nodes should be expanded.
	 * @param expander The function to expand nodes.
	 *
	 * @return A transformed copy of the tree. */
	public static <ContainedType> Tree<ContainedType> substitute(
			Tree<ContainedType> tree,
			Predicate<ContainedType> marker,
			Function<ContainedType, Tree<ContainedType>> expander) {
		tree.topDownTransform((contents) -> {
			if (marker.test(contents)) return TopDownTransformResult.TRANSFORM;
			else                       return TopDownTransformResult.PASSTHROUGH;
		}, (node) -> {
			return expander.apply(node.getHead());
		});
		return tree;
	}
	
	/** Performs 'variable substitution' or something along those lines on a tree.
	 * 
	 * @param <ContainedType> The type of element contained in the tree.
	 *
	 * @param tree The tree to do expansion in.
	 * @param environment A map which contains the variables to substitute.
	 *
	 * @return A transformed copy of the tree. */
	public static <ContainedType> Tree<ContainedType> substitute(
			Tree<ContainedType> tree,
			MapEx<ContainedType, Tree<ContainedType>> environment) {
		return substitute(
				tree,
				environment::containsKey,
				(element) -> environment.get(element).get());
	}
}