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

org.tensorics.core.tree.walking.Trees Maven / Gradle / Ivy

Go to download

Tensorics is a java framework which uses a tensor as a central object. A tensor represents a set of values placed in an N-dimensional space. Wherever you are tempted to use maps of maps, a tensor might be a good choice ;-) Tensorics provides methods to create, transform and performing calculations with those tensors.

There is a newer version: 0.0.81
Show newest version
// @formatter:off
 /*******************************************************************************
 *
 * This file is part of tensorics.
 * 
 * Copyright (c) 2008-2011, CERN. All rights reserved.
 *
 * 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.
 * 
 ******************************************************************************/
// @formatter:on

package org.tensorics.core.tree.walking;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

import org.tensorics.core.tree.domain.Node;
import org.tensorics.core.tree.domain.Path;
import org.tensorics.core.tree.domain.RebuildableNode;
import org.tensorics.core.tree.domain.RebuildingContext;
import org.tensorics.core.tree.domain.RebuildingContextImpl;

import com.google.common.reflect.TypeToken;

/**
 * Provides utility methods to walk through a tree of {@link Node}s.
 * 
 * @author kfuchsbe, agorzaws
 */
@SuppressWarnings("PMD.TooManyMethods")
public final class Trees {

    private static final TreeWalker PARENT_AFTER_CHILDREN_WALKER = new ParentAfterChildrenWalker();

    /**
     * private constructor to avoid instantiation
     */
    private Trees() {
        /* only static methods */
    }

    /**
     * Traverses the tree, starting from the given node in a way, such that all children are visited before the parent
     * node is visited. It allows to provide a callback which will be called at different points of the traversal,
     * depending on the implemented interfaces of the callback.
     * 
     * @see TreeWalker
     * @param startingNode the node which should be considered as the root node for the walk-through
     * @param callback the callback which will be called at certain points of the traversal, depending on the interfaces
     *            which are implemented by the callback.
     * @see NodeCallback
     */
    public static void walkParentAfterChildren(Node startingNode, NodeCallback callback) {
        PARENT_AFTER_CHILDREN_WALKER.walk(startingNode, callback);
    }

    /**
     * Retrieves the content of the given subtree. All children will be returned in the list, before the parent is
     * returned.
     * 
     * @param rootNode The node which shall be taken as root of the tree.
     * @return a list containing all nodes of the tree, where all the children are put before their common parent node.
     */
    public static List subTreeContent(Node rootNode) {
        final List content = new ArrayList();
        PARENT_AFTER_CHILDREN_WALKER.walk(rootNode, new EveryNodeCallback() {
            @Override
            public void onEvery(Node node) {
                content.add(node);
            }
        });
        return content;
    }

    /**
     * Searches in the tree for paths between the two given nodes. The direction of the path will be from the childNode
     * to the ancestor. The childNode will be the first element of each path and the given ancestor node the last
     * element.
     * 
     * @param childNode the node from which the paths to be found shall start
     * @param ancestorNode the ancestor node, which will be the end node of each path
     * @return all the paths from childNode to ancestorNode
     */
    public static List getPathsFromChildToAncestor(final Node childNode, final Node ancestorNode) {
        final List paths = new ArrayList<>();
        if (childNode.equals(ancestorNode)) {
            paths.add(new Path(Arrays.asList(childNode)));
            return paths;
        }

        walkParentAfterChildren(ancestorNode, new StepUpCallback() {

            @Override
            public void onStepUpFromChildToParent(Node child, Node parent) {
                if (childNode.equals(child)) {
                    Path newPath = new Path();
                    newPath.add(child);
                    newPath.add(parent);
                    paths.add(newPath);
                }

                for (Path path : paths) {
                    if (child.equals(getLastElement(path))) {
                        path.add(parent);
                    }
                }
            }

            private Node getLastElement(Path curentPath) {
                List nodeList = curentPath.getPath();
                if (!nodeList.isEmpty()) {
                    return nodeList.get(nodeList.size() - 1);
                }
                return null;
            }

        });
        if (paths.isEmpty()) {
            throw new PathDoesNotExistException(childNode, ancestorNode);
        }
        return paths;
    }

    /**
     * Searches for all bottom nodes of the tree. Bottom nodes are those, which are furthest away from the startingNode
     * on a given branch. (In other words, those with no more children)
     * 
     * @param startingNode the node which shall be used as root of the tree.
     * @return a list of all the nodes which are at the bottom of the tree (i.e. which have no children).
     */
    public static Collection findBottomNodes(Node startingNode) {
        final Set bottomNodes = new HashSet();
        walkParentAfterChildren(startingNode, new BottomNodeCallback() {
            @Override
            public void onBottom(Node node) {
                bottomNodes.add(node);
            }
        });
        return bottomNodes;
    }

    /**
     * Rebuilds the tree by taking elements from given context.
     * 
     * @param rootNode the root node of the tree
     * @param rebuildingContext the context containing the elements needed for rebuilding of the tree
     * @return a new root node of rebuilt tree
     */
    public static  T rebuildTree(T rootNode, final RebuildingContext rebuildingContext) {
        final RebuildingContext newRebuildingContext = new RebuildingContextImpl(rebuildingContext);
        walkParentAfterChildren(rootNode, new EveryNodeCallback() {
            @Override
            public void onEvery(Node node) {
                if ((node instanceof RebuildableNode) && (!newRebuildingContext.containsUpdated(node))) {
                    newRebuildingContext.put(node,
                            ((RebuildableNode) node).rebuildWithNewChildren(newRebuildingContext));
                }
            }
        });
        return newRebuildingContext.getUpdatedOrSame(rootNode);
    }

    /**
     * walks through the tree, starting from the given rootNode and collects all the nodes which implement the given
     * class.
     * 
     * @param rootNode the node from which to start the search
     * @param nodeClassToFind the class of the nodes to find
     * @return a set of all found nodes, which implement the given class
     */
    public static  Set findNodesOfClass(Node rootNode, final Class nodeClassToFind) {
        final Set foundNodes = new HashSet<>();
        walkParentAfterChildren(rootNode, new EveryNodeCallback() {
            @Override
            public void onEvery(Node node) {
                if (nodeClassToFind.isInstance(node)) {
                    foundNodes.add(nodeClassToFind.cast(node));
                }
            }
        });
        return foundNodes;
    }

    /**
     * walks through the tree, starting from the given rootNode and collects the highest node which implement the given
     * class.
     * 
     * @param rootNode the node from which to start the search
     * @param nodeClassToFind the class of the nodes to find
     * @return a set of one found Node, which implement the given class
     */
    public static  Set findHighestNodeOfClass(final Node rootNode,
            final Class nodeClassToFind) {
        final Set foundNodes = new HashSet<>();
        walkParentAfterChildren(rootNode, new EveryNodeCallback() {
            @Override
            public void onEvery(Node node) {
                if (nodeClassToFind.isInstance(node)) {
                    foundNodes.clear();
                    foundNodes.add(nodeClassToFind.cast(node));
                }
            }
        });
        return foundNodes;
    }

    /**
     * Searches ancestor nodes which match the given {@link TypeToken} (taking into account all generic parameters!),
     * starting from the given child not while using the tree as defined by the given rootNode. This is eg. used for
     * finding nodes, that can handle Exceptions. If the child node id not part of the tree then an empty set is
     * returned. If there exist paths between the two nodes, then it is enforced, that in this paths a matching node is
     * found. If this is not the case, an exception will be thrown. If the same node is found in different paths, then
     * the corresponding node is only contained once in the set.
     * 
     * @param childNode the node for which the closest ancestors shall be found.
     * @param rootNode the node which defines the root of the tree.
     * @param nodeToken the token, against which the nodes are matched.
     * @return a set of {@link Node} containing for each path the closest {@link Node} that match the given
     *         {@link TypeToken}
     * @throws NoMatchingNodeFoundException if there exists a path, that does not contain a matching node.
     */
    public static  Set findClosestAncestorNodeFromNodesToRootOfType(Node childNode, Node rootNode,
            TypeToken nodeToken) throws NoMatchingNodeFoundException {
        List paths = getPathsFromChildToAncestor(childNode, rootNode);

        Set toReturn = new HashSet();
        for (Path path : paths) {
            T foundNode = findFirstNodeOfType(path.getPath(), nodeToken);
            if (foundNode == null) {
                throw new NoMatchingNodeFoundException(
                        "No matching node was found for at least one path. This is not allowed.");
            }
            toReturn.add(foundNode);
        }
        return toReturn;
    }

    @SuppressWarnings("unchecked")
    private static  T findFirstNodeOfType(List currentPath, TypeToken nodeToken) {
        for (Node actualCheckedNode : currentPath) {
            if (nodeToken.isAssignableFrom(actualCheckedNode.getClass())) {
                return (T) actualCheckedNode;
            }
        }
        return null;
    }

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy