/* * ****************************************************************************** * 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.Iterator; import java.util.LinkedHashSet; import java.util.List; import java.util.Set; import javax.annotation.Nullable; import de.monticore.ast.ASTNode; import com.google.common.collect.Iterables; import com.google.common.collect.Sets; import de.se_rwth.commons.TreeUtil; import de.se_rwth.commons.Util; /** * Represents a link between two ASTNodes. * <p> * When writing transformations that translate from one AST to another, it is often cleaner not to * translate all information in a single operation. Splitting this translation process however * necessitates that sequentially executed translations always operate on the same source and target * nodes. For this purpose a Link provides a way for the program to "remember" which source nodes * are associated with which target nodes. * <p> * Going beyond that, Links also form a tree that mirrors the structure in both ASTs. This is based * on the assumption that the overall structure of both source and target AST will also be * equivalent. * * @author Sebastian Oberhoff */ public final class Link<S extends ASTNode, T extends ASTNode> implements Iterable<Link<?, ?>> { private final S source; private final T target; private final Link<?, ?> parent; private final Set<Link<?, ?>> childLinks = new LinkedHashSet<Link<?, ?>>(); public Link(S source, T target, @Nullable Link<?, ?> parent) { this.source = source; this.target = target; this.parent = parent; if (parent != null) { parent.addChildLink(this); } } private void addChildLink(Link<?, ?> childLink) { childLinks.add(childLink); } /** * @return the source node of this Link */ public S source() { return source; } /** * @return the target node of this Link */ public T target() { return target; } /** * @return the parent Link of this Link */ public Link<?, ?> parent() { return parent; } /** * @return the topmost Link in the tree to which this Link belongs */ public Link<?, ?> rootLink() { List<Link<?, ?>> parents = Util.listTillNull(this, Link::parent); return Iterables.getLast(parents); } @Override public Iterator<Link<?, ?>> iterator() { Iterable<Link<?, ?>> subtree = TreeUtil.preOrder(this, link -> link.childLinks); return subtree.iterator(); } /** * Looks up all Links in the subtree spanned by this Link by type of source and target. You can * use super classes like 'ASTNode.class' if you want to filter more loosely. * * @param sourceType the class of the source node * @param targetType the class of the target node * @return the set of all Links with the specified source and target types */ @SuppressWarnings(value = { "unchecked", "rawtypes" }) public <O extends ASTNode, D extends ASTNode> Set<Link<O, D>> getLinks(Class<O> sourceType, Class<D> targetType) { Set matchingLinks = Sets.newLinkedHashSet(Iterables.filter(this, link -> sourceType.isInstance(link.source) && targetType.isInstance(link.target))); return matchingLinks; } }