/* * ****************************************************************************** * 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 java.util.ArrayList; import java.util.Iterator; import java.util.LinkedHashSet; import java.util.List; import java.util.Set; import de.monticore.ast.ASTNode; import java.util.Optional; import com.google.common.collect.Iterables; import com.google.common.collect.Iterators; import com.google.common.collect.Sets; public class NodeWithParent<T extends ASTNode> extends ForwardingASTNode<T> implements Iterable<NodeWithParent<?>> { private final T astNode; private final NodeWithParent<? extends ASTNode> parent; private final Set<NodeWithParent<?>> children = new LinkedHashSet<>(); public NodeWithParent(T astNode) { this.astNode = astNode; this.parent = null; for (ASTNode childNode : astNode.get_Children()) { this.children.add(new NodeWithParent<>(childNode, this)); } } private NodeWithParent(T astNode, NodeWithParent<?> parent) { this.astNode = astNode; this.parent = parent; for (ASTNode childNode : astNode.get_Children()) { this.children.add(new NodeWithParent<>(childNode, this)); } } @Override public T delegate() { return astNode; } @Override public Iterator<NodeWithParent<?>> iterator() { Iterator<NodeWithParent<?>> childIterator = Iterables.concat(children).iterator(); return Iterators.concat(Iterators.singletonIterator(this), childIterator); } public Optional<NodeWithParent<?>> getParentNodeWithParent() { return Optional.ofNullable(parent); } public Set<NodeWithParent<?>> getChildNodesWithParent() { return children; } public List<NodeWithParent<?>> getAncestorNodesWithParent() { List<NodeWithParent<?>> ancestors = new ArrayList<>(); for (NodeWithParent<?> ancestor = this; ancestor != null; ancestor = ancestor .getParentNodeWithParent().orElse(null)) { ancestors.add(ancestor); } return ancestors; } /** * This method is a more general and typed version of {@link #getParentNodeWithParent()}. The set * of examined ancestors includes this NodeWithParent. * * @param nodeType the type of ancestor to be returned * @return the closest ancestor of the specified type. The Optional wrapper should serve as a * reminder that such an ancestor may not necessarily exist. */ // This method requires raw types since the generic type of any NodeWithParent is erased at // runtime and the compiler can't confirm the correctness of the runtime check used in this // method. @SuppressWarnings({ "unchecked", "rawtypes" }) public <C extends ASTNode> Optional<NodeWithParent<C>> getClosestAncestor(Class<C> nodeType) { Optional closestAncestor = Optional.empty(); for (NodeWithParent<?> ancestor : getAncestorNodesWithParent()) { if (nodeType.isInstance(ancestor.delegate())) { closestAncestor = Optional.of(ancestor); break; } } return closestAncestor; } /** * Examines the entire subtree below and including this NodeWithParent. * * @param nodeType the type of successor to be returned * @return the set of all successors of the specified type */ // This method requires raw types since the generic type of any NodeWithParent is erased at // runtime and the compiler can't confirm the correctness of the runtime check used in this // method. @SuppressWarnings({ "unchecked", "rawtypes" }) public <C extends ASTNode> Set<NodeWithParent<C>> getSuccessors(Class<C> nodeType) { Set filteredNodesWithParent = Sets.newLinkedHashSet(Iterables.filter(this, node -> nodeType.isInstance(node.astNode))); return filteredNodesWithParent; } }