package bjc.utils.funcdata; import java.util.function.Consumer; import java.util.function.Function; import java.util.function.Predicate; import java.util.function.UnaryOperator; import bjc.utils.funcdata.bst.TreeLinearizationMethod; import bjc.utils.funcutils.StringUtils; /** * A node in a homogenous tree. * * @author ben * * @param */ public class Tree implements ITree { private ContainedType data; private IFunctionalList> children; private boolean hasChildren; private int childCount; /** * Create a new leaf node in a tree * * @param leafToken * The data to store as a leaf node */ public Tree(ContainedType leafToken) { data = leafToken; hasChildren = false; } /** * Create a new tree node with the specified children * * @param leafToken * The data to hold in this node * @param childrn * A list of children for this node */ @SafeVarargs public Tree(ContainedType leafToken, ITree... childrn) { data = leafToken; hasChildren = true; childCount = 0; children = new FunctionalList<>(); for (ITree child : childrn) { children.add(child); childCount++; } } /** * Create a new tree node with the specified children * * @param leafToken * The data to hold in this node * @param childrn * A list of children for this node */ public Tree(ContainedType leafToken, IFunctionalList> childrn) { data = leafToken; hasChildren = true; childCount = childrn.getSize(); children = childrn; } @Override public void addChild(ITree child) { if (hasChildren == false) { hasChildren = true; children = new FunctionalList<>(); } childCount++; children.add(child); } @Override public TransformedType transformHead( Function transformer) { return transformer.apply(data); } @Override public int getChildrenCount() { return childCount; } @Override public TransformedType transformChild(int childNo, Function, TransformedType> transformer) { if (childNo < 0 || childNo > (childCount - 1)) { throw new IllegalArgumentException( "Child index #" + childNo + " is invalid"); } return transformer.apply(children.getByIndex(childNo)); } @Override public ReturnedType collapse( Function leafTransform, Function, NewType>> nodeCollapser, Function resultTransformer) { return resultTransformer .apply(internalCollapse(leafTransform, nodeCollapser)); } protected NewType internalCollapse( Function leafTransform, Function, NewType>> nodeCollapser) { if (hasChildren) { Function, NewType> nodeTransformer = nodeCollapser.apply(data); IFunctionalList collapsedChildren = children.map((child) -> { return child.collapse(leafTransform, nodeCollapser, (subTreeVal) -> subTreeVal); }); return nodeTransformer.apply(collapsedChildren); } return leafTransform.apply(data); } @Override public ITree flatMapTree( Function> mapper) { if (hasChildren) { ITree flatMappedData = mapper.apply(data); children.map((child) -> child.flatMapTree(mapper)) .forEach((child) -> flatMappedData.addChild(child)); return flatMappedData; } return mapper.apply(data); } @Override public void selectiveTransform(Predicate nodePicker, UnaryOperator transformer) { if (hasChildren) { children.forEach((child) -> child .selectiveTransform(nodePicker, transformer)); } else { data = transformer.apply(data); } } @Override public ITree transformTree( Function transformer) { if (hasChildren) { IFunctionalList> transformedChildren = children.map( (child) -> child.transformTree(transformer)); return new Tree<>(transformer.apply(data), transformedChildren); } return new Tree<>(transformer.apply(data)); } @Override public void traverse(TreeLinearizationMethod linearizationMethod, Consumer action) { if (hasChildren) { switch (linearizationMethod) { case INORDER: if (childCount != 2) { throw new IllegalArgumentException( "Can only do in-order traversal for binary trees."); } children.getByIndex(0).traverse(linearizationMethod, action); action.accept(data); children.getByIndex(1).traverse(linearizationMethod, action); break; case POSTORDER: children.forEach((child) -> child .traverse(linearizationMethod, action)); action.accept(data); break; case PREORDER: action.accept(data); children.forEach((child) -> child .traverse(linearizationMethod, action)); break; default: break; } } else { action.accept(data); } } @Override public ITree rebuildTree( Function leafTransformer, Function operatorTransformer) { if (hasChildren) { IFunctionalList> mappedChildren = children.map((child) -> { return child.rebuildTree(leafTransformer, operatorTransformer); }); return new Tree<>(operatorTransformer.apply(data), mappedChildren); } return new Tree<>(leafTransformer.apply(data)); } @Override public String toString() { StringBuilder builder = new StringBuilder(); internalToString(builder, 1, true); builder.deleteCharAt(builder.length() - 1); return builder.toString(); } protected void internalToString(StringBuilder builder, int indentLevel, boolean initial) { if (!initial) { StringUtils.indentNLevels(builder, indentLevel); } builder.append("Node: "); builder.append(data == null ? "(null)" : data.toString()); builder.append("\n"); if (hasChildren) { children.forEach((child) -> { ((Tree) child).internalToString(builder, indentLevel + 2, false); }); } } @Override public ITree topDownTransform( Function transformPicker, UnaryOperator> transformer) { TopDownTransformResult transformResult = transformPicker.apply(data); switch (transformResult) { case PASSTHROUGH: ITree result = new Tree<>(data); if (hasChildren) { children.forEach((child) -> { result.addChild(child.topDownTransform( transformPicker, transformer)); }); } return result; case SKIP: return this; case TRANSFORM: return transformer.apply(this); case PUSHDOWN: result = new Tree<>(data); if (hasChildren) { children.forEach((child) -> { result.addChild(child.topDownTransform( transformPicker, transformer)); }); } return transformer.apply(result); default: throw new IllegalArgumentException( "Recieved unknown transform result " + transformResult); } } }