dagger.internal.codegen.base.TarjanSCCs Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of dagger-compiler Show documentation
Show all versions of dagger-compiler Show documentation
A fast dependency injector for Android and Java.
/*
* Copyright (C) 2021 The Dagger Authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package dagger.internal.codegen.base;
import static com.google.common.base.Preconditions.checkState;
import static java.lang.Math.min;
import com.google.common.collect.ImmutableCollection;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Maps;
import com.google.common.collect.Sets;
import com.google.common.graph.SuccessorsFunction;
import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.Deque;
import java.util.List;
import java.util.Map;
import java.util.Set;
/**
* An implementation of Tarjan's algorithm for finding the SCC of a graph. This is based on the
* psuedo code algorithm here:
* http://en.wikipedia.org/wiki/Tarjan%27s_strongly_connected_components_algorithm
*/
public final class TarjanSCCs {
/** Returns the set of strongly connected components in reverse topological order. */
public static ImmutableList> compute(
ImmutableCollection nodes, SuccessorsFunction successorsFunction) {
return new TarjanSCC<>(nodes, successorsFunction).compute();
}
private static class TarjanSCC {
private final ImmutableCollection nodes;
private final SuccessorsFunction successorsFunction;
private final Deque stack;
private final Set onStack;
private final Map indexes;
private final Map lowLinks;
private final List> stronglyConnectedComponents = new ArrayList<>();
TarjanSCC(ImmutableCollection nodes, SuccessorsFunction successorsFunction) {
this.nodes = nodes;
this.successorsFunction = successorsFunction;
this.stack = new ArrayDeque<>(nodes.size());
this.onStack = Sets.newHashSetWithExpectedSize(nodes.size());
this.indexes = Maps.newHashMapWithExpectedSize(nodes.size());
this.lowLinks = Maps.newHashMapWithExpectedSize(nodes.size());
}
private ImmutableList> compute() {
checkState(indexes.isEmpty(), "TarjanSCC#compute() can only be called once per instance!");
for (NodeT node : nodes) {
if (!indexes.containsKey(node)) {
stronglyConnect(node);
}
}
return ImmutableList.copyOf(stronglyConnectedComponents);
}
private void stronglyConnect(NodeT node) {
// Set the index and lowLink for node to the smallest unused index and add it to the stack
lowLinks.put(node, indexes.size());
indexes.put(node, indexes.size());
stack.push(node);
onStack.add(node);
for (NodeT successor : successorsFunction.successors(node)) {
if (!indexes.containsKey(successor)) {
// Successor has not been processed.
stronglyConnect(successor);
lowLinks.put(node, min(lowLinks.get(node), lowLinks.get(successor)));
} else if (onStack.contains(successor)) {
// Successor is on the stack and hence in the current SCC.
lowLinks.put(node, min(lowLinks.get(node), indexes.get(successor)));
} else {
// Successor is not on the stack and hence in an already processed SCC, so ignore.
}
}
// If node is the root of the SCC, pop the stack until reaching the root to get all SCC nodes.
if (lowLinks.get(node).equals(indexes.get(node))) {
ImmutableSet.Builder scc = ImmutableSet.builder();
NodeT currNode;
do {
currNode = stack.pop();
onStack.remove(currNode);
scc.add(currNode);
} while (!node.equals(currNode));
stronglyConnectedComponents.add(scc.build());
}
}
}
private TarjanSCCs() {}
}