/* * Minecraft Forge * Copyright (c) 2016-2019. * * This library 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 version 2.1 * of the License. * * 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 library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ package net.minecraftforge.fml.loading.toposort; import com.google.common.graph.Graph; import java.util.BitSet; import java.util.HashMap; import java.util.HashSet; import java.util.Map; import java.util.Set; /** * An object that splits a graph into strongly connected components lazily with * Tarjan's Strongly Connected Components Algorithm. * *

This algorithm allows to detect all cycles in dependencies that prevent topological * sorting. * *

This detector evaluates the graph lazily and won't reflect the modifications in the * graph after initial evaluation. */ public class StronglyConnectedComponentDetector { private final Graph graph; private Map ids; private T[] elements; private int[] dfn; private int[] low; private int[] stack; private int top; private BitSet onStack; private Set> components; public StronglyConnectedComponentDetector(Graph graph) { this.graph = graph; } public Set> getComponents() { if (components == null) { calculate(); } return components; } @SuppressWarnings("unchecked") private void calculate() { components = new HashSet<>(); int t = 0; ids = new HashMap<>(); Set nodes = graph.nodes(); elements = (T[]) new Object[nodes.size()]; for (T node : nodes) { ids.put(node, t); elements[t] = node; t++; } final int n = nodes.size(); dfn = new int[n]; low = new int[n]; stack = new int[n]; onStack = new BitSet(n); top = -1; for (int i = 0; i < n; i++) { if (dfn[i] == 0) { dfs(i, 1); } } } private void dfs(int now, int depth) { dfn[now] = depth; low[now] = depth; top++; stack[top] = now; onStack.set(now); for (T each : graph.successors(elements[now])) { int to = ids.get(each); if (dfn[to] != 0) { if (low[now] > dfn[to]) { low[now] = dfn[to]; } } else { dfs(to, depth + 1); if (low[now] > low[to]) { low[now] = low[to]; } } } if (dfn[now] == low[now]) { Set component = new HashSet<>(); while (top >= 0) { final int t = stack[top]; component.add(elements[t]); onStack.clear(t); top--; if (t == now) { break; } } components.add(component); } } }