/* * This file is part of the OSMembrane project. * More informations under www.osmembrane.de * * The project is licensed under the GNU GENERAL PUBLIC LICENSE 3.0. * for more details about the license see http://www.osmembrane.de/license/ * * Source: $HeadURL$ ($Revision$) * Last changed: $Date$ */ package de.osmembrane.model.algorithms; import java.util.ArrayList; import java.util.HashMap; import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.Queue; import java.util.Stack; import de.osmembrane.model.pipeline.AbstractConnector; import de.osmembrane.model.pipeline.AbstractFunction; /** * Checks if functions does create a loop. * * @author jakob_jarosch */ public class TarjanAlgorithm { /** * External function-list. */ private List<AbstractFunction> functions; /** * Internal store for functions. */ private Queue<AbstractFunction> notYetVisistedFunctions = new LinkedList<AbstractFunction>(); /** * Set to check duplicate visited functions. */ private Stack<AbstractFunction> nodeStack = new Stack<AbstractFunction>(); /** * Global index for tarjan. */ private int index; /** * Map for index. */ private Map<AbstractFunction, Integer> nodeIndex = new HashMap<AbstractFunction, Integer>(); /** * Map for lowlink. */ private Map<AbstractFunction, Integer> nodeLowlink = new HashMap<AbstractFunction, Integer>(); /** * strongly connected components. */ List<List<AbstractFunction>> scc = new ArrayList<List<AbstractFunction>>(); /** * Creates a new instance with given functions. * * @param functions * fist of functions */ public TarjanAlgorithm(List<AbstractFunction> functions) { this.functions = functions; } /** * Checks if a Graph has a loop. */ public void run() { reset(); while (notYetVisistedFunctions.size() > 0) { AbstractFunction node = notYetVisistedFunctions.peek(); if (nodeIndex.get(node) == null) { tarjan(node); } } } /** * Returns the SCC in the graph. * * @return scc of given graph */ public List<List<AbstractFunction>> getSCC() { return scc; } /** * Returns all SCCs with at least a given number of entries. * * @param size * minimum size for SCC * @return all SCCs with at least "size" entries */ public List<List<AbstractFunction>> getSCC(int size) { List<List<AbstractFunction>> returnList = new ArrayList<List<AbstractFunction>>(); for (List<AbstractFunction> scc : this.scc) { if (scc.size() >= size) { returnList.add(scc); } } return returnList; } /** * The tarjan algorithm. * * @param node * Node with which should be started. */ private void tarjan(AbstractFunction node) { notYetVisistedFunctions.remove(node); nodeIndex.put(node, index); nodeLowlink.put(node, index); index++; nodeStack.push(node); for (AbstractConnector edges : node.getOutConnectors()) { for (AbstractConnector edge : edges.getConnections()) { AbstractFunction newNode = edge.getParent(); if (nodeIndex.get(newNode) == null) { tarjan(newNode); nodeLowlink.put(node, llMin(node, newNode)); } else if (nodeStack.contains(newNode)) { nodeLowlink.put(node, liMin(node, newNode)); } } } if (nodeLowlink.get(node).equals(nodeIndex.get(node))) { /** * There seems to be a strongly connected component in the graph, * check the size. */ int sccSize = 0; List<AbstractFunction> stackNodes = new ArrayList<AbstractFunction>(); do { stackNodes.add(nodeStack.pop()); sccSize++; } while (!stackNodes.contains(node)); scc.add(stackNodes); } } /** * minimum from lowlevel and lowlevel */ private Integer llMin(AbstractFunction node1, AbstractFunction node2) { if (nodeLowlink.get(node1).intValue() < nodeLowlink.get(node2) .intValue()) { return nodeLowlink.get(node1); } else { return nodeLowlink.get(node2); } } /** * minumum from lowlevel and index */ private Integer liMin(AbstractFunction node1, AbstractFunction node2) { if (nodeLowlink.get(node1).intValue() < nodeIndex.get(node2).intValue()) { return nodeLowlink.get(node1); } else { return nodeIndex.get(node2); } } private void reset() { nodeIndex.clear(); nodeLowlink.clear(); notYetVisistedFunctions.clear(); scc.clear(); index = 0; for (AbstractFunction function : functions) { notYetVisistedFunctions.add(function); } nodeStack.clear(); } }