1 // Copyright 2000-2017 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
2 package org.jetbrains.java.decompiler.modules.decompiler;
4 import org.jetbrains.annotations.NotNull;
5 import org.jetbrains.java.decompiler.modules.decompiler.stats.Statement;
6 import org.jetbrains.java.decompiler.util.ListStack;
11 * The class finds the strongly connected components (SCCs) of a directed graph,
12 * implementing "Tarjan's strongly connected components" algorithm.
13 * Running time is linear.
15 // todo should be replaced or reuse InferenceGraphNode.strongConnect or DFSTBuilder.Tarjan?
16 public class StrongConnectivityHelper {
17 private final List<List<Statement>> components = new ArrayList<>();
18 private final Set<Statement> setProcessed = new HashSet<>();
19 private final ListStack<Statement> component = new ListStack<>();
20 private final Set<Statement> visited = new HashSet<>();
21 private final Map<Statement, Integer> indices = new HashMap<>();
22 private final Map<Statement, Integer> lowIndices = new HashMap<>();
24 private int nextIndex;
26 public StrongConnectivityHelper(@NotNull Statement startStatement) {
27 visitTree(startStatement.getFirst());
28 for (Statement statement : startStatement.getStats()) {
29 if (!setProcessed.contains(statement) && statement.getPredecessorEdges(Statement.STATEDGE_DIRECT_ALL).isEmpty()) {
33 // should not find any more nodes! FIXME: ??
34 for (Statement statement : startStatement.getStats()) {
35 if (!setProcessed.contains(statement)) {
41 private void visitTree(@NotNull Statement statement) {
50 setProcessed.addAll(visited);
51 setProcessed.add(statement);
54 private void visit(@NotNull Statement statement) {
55 component.push(statement);
56 indices.put(statement, nextIndex);
57 lowIndices.put(statement, nextIndex);
59 List<Statement> successors = statement.getNeighbours(StatEdge.TYPE_REGULAR, Statement.DIRECTION_FORWARD); // TODO: set?
60 successors.removeAll(setProcessed);
61 for (Statement successor : successors) {
63 if (visited.contains(successor)) {
64 successorIndex = indices.get(successor);
67 visited.add(successor);
69 successorIndex = lowIndices.get(successor);
71 lowIndices.put(statement, Math.min(lowIndices.get(statement), successorIndex));
73 if (lowIndices.get(statement).intValue() == indices.get(statement).intValue()) {
74 List<Statement> component = new ArrayList<>();
75 Statement statementInComponent;
77 statementInComponent = this.component.pop();
78 component.add(statementInComponent);
80 while (statementInComponent != statement);
81 components.add(component);
85 public static boolean isExitComponent(@NotNull List<? extends Statement> component) {
86 Set<Statement> statements = new HashSet<>();
87 for (Statement statement : component) {
88 statements.addAll(statement.getNeighbours(StatEdge.TYPE_REGULAR, Statement.DIRECTION_FORWARD));
90 for (Statement statement : component) {
91 statements.remove(statement);
93 return statements.size() == 0;
96 public @NotNull List<Statement> getExitReps() {
97 List<Statement> result = new ArrayList<>();
98 for (List<Statement> component : components) {
99 if (isExitComponent(component)) {
100 result.add(component.get(0));
106 public @NotNull List<@NotNull List<Statement>> getComponents() {