blob: f0471da49dc717d01b1854ec451ba9a58e0c5cb2 [file] [log] [blame]
dpbe8d79aa2018-03-19 07:28:13 -07001/*
2 * Copyright (C) 2018 The Dagger Authors.
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package dagger.internal.codegen;
18
19import com.google.common.collect.ImmutableList;
20import com.google.common.collect.ImmutableSet;
21import com.google.common.graph.SuccessorsFunction;
22import java.util.ArrayDeque;
23import java.util.HashMap;
24import java.util.Map;
25import java.util.Queue;
26import java.util.Set;
27
28/** Utility methods for {@link com.google.common.graph} types. */
29final class DaggerGraphs {
30 /**
31 * Returns a shortest path from {@code nodeU} to {@code nodeV} in {@code graph} as a list of the
32 * nodes visited in sequence, including both {@code nodeU} and {@code nodeV}. (Note that there may
33 * be many possible shortest paths.)
34 *
35 * <p>If {@code nodeV} is not {@link
36 * com.google.common.graph.Graphs#reachableNodes(com.google.common.graph.Graph, Object) reachable}
37 * from {@code nodeU}, the list returned is empty.
38 *
39 * @throws IllegalArgumentException if {@code nodeU} or {@code nodeV} is not present in {@code
40 * graph}
41 */
42 static <N> ImmutableList<N> shortestPath(SuccessorsFunction<N> graph, N nodeU, N nodeV) {
43 if (nodeU.equals(nodeV)) {
44 return ImmutableList.of(nodeU);
45 }
46 Set<N> successors = ImmutableSet.copyOf(graph.successors(nodeU));
47 if (successors.contains(nodeV)) {
48 return ImmutableList.of(nodeU, nodeV);
49 }
50
51 Map<N, N> visitedNodeToPathPredecessor = new HashMap<>(); // encodes shortest path tree
dpbe8d79aa2018-03-19 07:28:13 -070052 for (N node : successors) {
53 visitedNodeToPathPredecessor.put(node, nodeU);
54 }
cgdecker531f1c62018-07-11 12:01:39 -070055 Queue<N> currentNodes = new ArrayDeque<N>(successors);
56 Queue<N> nextNodes = new ArrayDeque<N>();
dpbe8d79aa2018-03-19 07:28:13 -070057
58 // Perform a breadth-first traversal starting with the successors of nodeU.
59 while (!currentNodes.isEmpty()) {
60 while (!currentNodes.isEmpty()) {
61 N currentNode = currentNodes.remove();
62 for (N nextNode : graph.successors(currentNode)) {
63 if (visitedNodeToPathPredecessor.containsKey(nextNode)) {
64 continue; // we already have a shortest path to nextNode
65 }
66 visitedNodeToPathPredecessor.put(nextNode, currentNode);
67 if (nextNode.equals(nodeV)) {
68 ImmutableList.Builder<N> builder = ImmutableList.builder();
69 N node = nodeV;
70 builder.add(node);
71 while (!node.equals(nodeU)) {
72 node = visitedNodeToPathPredecessor.get(node);
73 builder.add(node);
74 }
75 return builder.build().reverse();
76 }
77 nextNodes.add(nextNode);
78 }
79 }
80 Queue<N> emptyQueue = currentNodes;
81 currentNodes = nextNodes;
82 nextNodes = emptyQueue; // reusing empty queue faster than allocating new one
83 }
84
85 return ImmutableList.of();
86 }
87
88 private DaggerGraphs() {}
89}