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

org.codefx.libfx.collection.tree.stream.BackwardsDfsTreeIterationStrategy Maven / Gradle / Ivy

The newest version!
package org.codefx.libfx.collection.tree.stream;

import java.util.Objects;
import java.util.Optional;

import org.codefx.libfx.collection.tree.navigate.TreeNavigator;

/**
 * A {@link TreeIterationStrategy} which iterates a tree's nodes with a backwards depth-first search.
 * 

* This means that given an initial path from the root {@code R} to some node {@code N} , this strategy will start with * {@code N} and move backwards visiting parents, left siblings and their children (from right to left) until it reaches * {@code R}. This will enumerate the same nodes as a depth-first search which started in {@code R} and ended in * {@code N} but in reverse order (containing both {@code N} and {@code R}). *

* This implementation is only guaranteed to work on trees, i.e. a connected, directed, acyclic graph. Using it on other * graphs can lead to unexpected behavior including infinite loops. * * @param * the type of elements contained in the tree */ final class BackwardsDfsTreeIterationStrategy implements TreeIterationStrategy { // #begin FIELDS private final TreeNavigator navigator; /** * The path from the root to the last node returned by {@link #goToNextNode()} *

* The path will also contain at least one node before the first call to {@link #goToNextNode()}. These are the * nodes specified during construction and the one at the end of the path must be returned by the first call to * {@code goToNextNode()}. Otherwise the end of the path would never be returned by the strategy. Whether * {@code goToNextNode()} must return this node or find a new one is indicated by {@link #beforeFirst}. *

* If the path is empty, no more nodes will be returned. */ private final TreePath> path; /** * Indicates whether {@link #goToNextNode()} was not already called at least once. */ private boolean beforeFirst; // #end FIELDS // #begin CONSTRUCTION /** * Creates a new backwards depth-first search strategy starting with the specified initial path. *

* The iteration will begin with the node at the end of the initial path. It will stop when it successfully * backtracked to the first node in that path, i.e. the (sub-)tree's root. (Remember, this does not happen on a * straight route from nodes to their parents but via backwards depth-first search.) *

* The specified path must correspond to the navigator's view on the tree, i.e. each element in the path must be the * parent (in the tree) of the next one. *

* See {@link TreePathFactory} for easy ways to create an initial path. * * @param navigator * the navigator used to navigate the tree * @param initialPath * the initial path from the root of the (sub-)tree iterated by this strategy; must contain at least one * element; the last element in the path will be returned by the first call to {@link #goToNextNode()} */ public BackwardsDfsTreeIterationStrategy(TreeNavigator navigator, TreePath> initialPath) { Objects.requireNonNull(navigator, "The argument 'navigator' must not be null."); Objects.requireNonNull(initialPath, "The argument 'initialPath' must not be null."); if (initialPath.isEmpty()) throw new IllegalArgumentException("The 'initialPath' must not be empty."); this.navigator = navigator; this.path = initialPath; this.beforeFirst = true; } // #end CONSTRUCTION @Override public Optional goToNextNode() { // if the path is empty, iteration ended and no more nodes will be returned if (path.isEmpty()) return Optional.empty(); // if this is the first call, do not move to the next node if (beforeFirst) beforeFirst = false; else movePathEndToNextNode(); return path.getEnd().map(TreeNode::getElement); } private void movePathEndToNextNode() { Optional> leftSibling = goToParentAndGetLeftSibling(); if (leftSibling.isPresent()) goToRightmostAncestor(leftSibling.get()); } private Optional> goToParentAndGetLeftSibling() { TreeNode node = path.removeEnd(); Optional> parent = path.getEnd(); if (parent.isPresent()) { int leftSiblingIndex = node.getChildIndex().getAsInt() - 1; return navigator .getChild(parent.get().getElement(), leftSiblingIndex) .map(ls -> SimpleTreeNode.innerNode(ls, leftSiblingIndex)); } else return Optional.empty(); } private void goToRightmostAncestor(TreeNode leftSibling) { Optional> rightmostChild = Optional.of(leftSibling); while (rightmostChild.isPresent()) { path.append(rightmostChild.get()); int rightmostChildIndex = navigator.getChildrenCount(rightmostChild.get().getElement()) - 1; rightmostChild = navigator .getChild(rightmostChild.get().getElement(), rightmostChildIndex) .map(child -> SimpleTreeNode.innerNode(child, rightmostChildIndex)); } } }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy