/* * Copyright 2015 S. Webber * * 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 org.oakgp.node.walk; import java.util.function.Function; import java.util.function.Predicate; import org.oakgp.Arguments; import org.oakgp.node.FunctionNode; import org.oakgp.node.Node; import org.oakgp.node.NodeType; /** Provides a mechanism for recursively visiting <i>all</i> nodes in a tree structure. */ public final class NodeWalk { /** Private constructor as all methods are static. */ private NodeWalk() { // do nothing } /** * Returns a {@code Node} from the tree structure represented by the given {@code Node}. * * @param index * the index of the {@code Node}, in the tree structure represented by {@code node}, that needs to be returned * @return the {@code Node} at {@code index} of the tree structure represented by {@code node} */ public static Node getAt(Node node, int index) { if (NodeType.isFunction(node)) { FunctionNode functionNode = (FunctionNode) node; Arguments arguments = functionNode.getArguments(); int total = 0; for (int i = 0; i < arguments.getArgCount(); i++) { Node child = arguments.getArg(i); int c = child.getNodeCount(); if (total + c > index) { return getAt(child, index - total); } else { total += c; } } } return node; } /** * Returns a new {@code Node} resulting from replacing the {@code Node} at position {@code index} of the given {@code Node} with the result of * {@code replacement}. * * @param index * the index of the {@code Node}, in the tree structure represented by {@code node}, that needs to be replaced * @param replacement * the function to apply to the {@code Node} at {@code index} to determine the {@code Node} that should replace it * @return a new {@code Node} derived from replacing the {@code Node} at {@code index} with the result of {@code replacement} */ public static Node replaceAt(Node node, int index, Function<Node, Node> replacement) { if (NodeType.isFunction(node)) { FunctionNode functionNode = (FunctionNode) node; Arguments arguments = functionNode.getArguments(); int total = 0; for (int i = 0; i < arguments.getArgCount(); i++) { Node child = arguments.getArg(i); int c = child.getNodeCount(); if (total + c > index) { return new FunctionNode(functionNode.getFunction(), arguments.replaceAt(i, replaceAt(child, index - total, replacement))); } else { total += c; } } } return replacement.apply(node); } /** * Returns a new {@code Node} resulting from replacing any components that match the specified predicate with the result of applying the specified function. * * @param criteria * the predicate used to determine if a node should be replaced * @param replacement * the function used to determine what a node should be replaced with */ public static Node replaceAll(Node node, Predicate<Node> criteria, Function<Node, Node> replacement) { if (NodeType.isFunction(node)) { if (criteria.test(node)) { return replaceAll(replacement.apply(node), criteria, replacement); } else { FunctionNode functionNode = (FunctionNode) node; Arguments arguments = functionNode.getArguments(); boolean updated = false; Node[] replacementArgs = new Node[arguments.getArgCount()]; for (int i = 0; i < arguments.getArgCount(); i++) { Node arg = arguments.getArg(i); Node replacedArg = replaceAll(arg, criteria, replacement); if (arg != replacedArg) { updated = true; } replacementArgs[i] = replacedArg; } if (updated) { return new FunctionNode(functionNode.getFunction(), Arguments.createArguments(replacementArgs)); } else { return node; } } } else { if (criteria.test(node)) { return replacement.apply(node); } else { return node; } } } }