/*
* ******************************************************************************
* MontiCore Language Workbench
* Copyright (c) 2015, MontiCore, All rights reserved.
*
* This project is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 3.0 of the License, or (at your option) any later version.
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this project. If not, see <http://www.gnu.org/licenses/>.
* ******************************************************************************
*/
package de.monticore.utils;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.Iterables;
import com.google.common.collect.Lists;
import de.monticore.ast.ASTNode;
import de.se_rwth.commons.Util;
import java.util.Collection;
import java.util.List;
/**
* Helper class containing common operations concerning ASTNodes
*
* @author Sebastian Oberhoff
*/
public final class ASTNodes {
private ASTNodes() {
// noninstantiable
}
/**
* Checks whether two ASTNodes are in a vertical relationship with each other with any number of
* other nodes in between.
* <p>
* This operation is O(n), where n is the number of nodes contained in the subtree spanned by the
* ancestor.
*
* @param ancestor the node further up in the AST
* @param successor the node further down in the AST
* @return true if there exists a descending path from the ancestor to the successor
*/
public static boolean areAncestorAndSuccessor(ASTNode ancestor, ASTNode successor) {
Iterable<ASTNode> successors = Util.preOrder(ancestor, ASTNode::get_Children);
return Iterables.any(successors, otherSuccessor -> otherSuccessor == successor);
}
/**
* Calculates the list of nodes spanning a vertical path from ancestor node to successor.
* <p>
* The returned list will be empty if no path exists between ancestor and successor.
* <p>
* The returned list also contains both ancestor and successor if a path is indeed present.
* <p>
* This operation is O(n), where n is the number of nodes contained in the subtree spanned by
* the ancestor.
*
* @param ancestor the node at the top of the path
* @param successor the node at the bottom of the path
* @return the list of nodes from the ancestor to the successor, sorted from ancestor (first
* element) to successor (last element)
*/
public static List<ASTNode> getIntermediates(ASTNode ancestor, ASTNode successor) {
ImmutableMap<ASTNode, ASTNode> childToParentMap = childToParentMap(ancestor);
List<ASTNode> allAncestors =
Util.listTillPredicate(successor, childToParentMap::get, node -> node != null);
return Lists.reverse(allAncestors);
}
/**
* Gathers a list of all successor nodes of an ASTNode with a specified type.
*
* @param ancestor the ancestor node of which successors with the specified type are to be
* gathered
* @param nodeType the type of successors to be gathered
* @return the list of successors of the ancestor node with the specified type, ordered according
* to a pre-order traversal
*/
public static <T extends ASTNode> List<T> getSuccessors(ASTNode ancestor, Class<T> nodeType) {
Iterable<ASTNode> successors = Util.preOrder(ancestor, ASTNode::get_Children);
Iterable<T> successorsWithMatchingType = Iterables.filter(successors, nodeType);
return Lists.newArrayList(successorsWithMatchingType);
}
/**
* Gathers a list of all successor nodes of an ASTNode with a specified type.
*
* @param ancestor the ancestor node of which successors with the specified type are to be
* gathered
* @param nodeType the type of successors to be gathered
* @return the list of successors of the ancestor node with the specified type, ordered according
* to a pre-order traversal
*/
public static List<ASTNode> getSuccessors(ASTNode ancestor, Collection<Class<? extends ASTNode>> types) {
Iterable<ASTNode> successors = Util.preOrder(ancestor, ASTNode::get_Children);
Iterable<ASTNode> successorsWithMatchingType = Iterables.filter(successors, successor -> types.contains(successor.getClass()));
return Lists.newArrayList(successorsWithMatchingType);
}
/**
* @param root the root of the subtree for which the map is to be created
* @return an {@link ImmutableMap} from child-ASTNode to parent-ASTNode for all ASTNodes below the given root
*/
public static ImmutableMap<ASTNode, ASTNode> childToParentMap(ASTNode root) {
ImmutableMap.Builder<ASTNode, ASTNode> builder = ImmutableMap.builder();
Util.preOrder(root, ASTNode::get_Children).stream().forEach(node -> {
for (ASTNode child : node.get_Children()) {
builder.put(child, node);
}
});
return builder.build();
}
}