private static <N> ImmutableMap<N, GraphConnections<N, Presence>> getNodeConnections( Graph<N> graph) { // ImmutableMap.Builder maintains the order of the elements as inserted, so the map will have // whatever ordering the graph's nodes do, so ImmutableSortedMap is unnecessary even if the // input nodes are sorted. ImmutableMap.Builder<N, GraphConnections<N, Presence>> nodeConnections = ImmutableMap.builder(); for (N node : graph.nodes()) { nodeConnections.put(node, connectionsOf(graph, node)); } return nodeConnections.build(); }
/** * Returns true if {@code graph} has at least one cycle. A cycle is defined as a non-empty subset * of edges in a graph arranged to form a path (a sequence of adjacent outgoing edges) starting * and ending with the same node. * * <p>This method will detect any non-empty cycle, including self-loops (a cycle of length 1). */ public static <N> boolean hasCycle(Graph<N> graph) { int numEdges = graph.edges().size(); if (numEdges == 0) { return false; // An edge-free graph is acyclic by definition. } if (!graph.isDirected() && numEdges >= graph.nodes().size()) { return true; // Optimization for the undirected case: at least one cycle must exist. } Map<Object, NodeVisitState> visitedNodes = Maps.newHashMapWithExpectedSize(graph.nodes().size()); for (N node : graph.nodes()) { if (subgraphHasCycle(graph, visitedNodes, node, null)) { return true; } } return false; }
/** * Returns the set of nodes that are reachable from {@code node}. Node B is defined as reachable * from node A if there exists a path (a sequence of adjacent outgoing edges) starting at node A * and ending at node B. Note that a node is always reachable from itself via a zero-length path. * * <p>This is a "snapshot" based on the current topology of {@code graph}, rather than a live view * of the set of nodes reachable from {@code node}. In other words, the returned {@link Set} will * not be updated after modifications to {@code graph}. * * @throws IllegalArgumentException if {@code node} is not present in {@code graph} */ public static <N> Set<N> reachableNodes(Graph<N> graph, N node) { checkArgument(graph.nodes().contains(node), NODE_NOT_IN_GRAPH, node); Set<N> visitedNodes = new LinkedHashSet<N>(); Queue<N> queuedNodes = new ArrayDeque<N>(); visitedNodes.add(node); queuedNodes.add(node); // Perform a breadth-first traversal rooted at the input node. while (!queuedNodes.isEmpty()) { N currentNode = queuedNodes.remove(); for (N successor : graph.successors(currentNode)) { if (visitedNodes.add(successor)) { queuedNodes.add(successor); } } } return Collections.unmodifiableSet(visitedNodes); }
private static <N> ImmutableMap<N, GraphConnections<N, Presence>> getNodeConnections( Graph<N> graph) { // ImmutableMap.Builder maintains the order of the elements as inserted, so the map will have // whatever ordering the graph's nodes do, so ImmutableSortedMap is unnecessary even if the // input nodes are sorted. ImmutableMap.Builder<N, GraphConnections<N, Presence>> nodeConnections = ImmutableMap.builder(); for (N node : graph.nodes()) { nodeConnections.put(node, connectionsOf(graph, node)); } return nodeConnections.build(); }
private static <N> ImmutableMap<N, GraphConnections<N, Presence>> getNodeConnections( Graph<N> graph) { // ImmutableMap.Builder maintains the order of the elements as inserted, so the map will have // whatever ordering the graph's nodes do, so ImmutableSortedMap is unnecessary even if the // input nodes are sorted. ImmutableMap.Builder<N, GraphConnections<N, Presence>> nodeConnections = ImmutableMap.builder(); for (N node : graph.nodes()) { nodeConnections.put(node, connectionsOf(graph, node)); } return nodeConnections.build(); }
/** * Returns true if {@code graph} has at least one cycle. A cycle is defined as a non-empty subset * of edges in a graph arranged to form a path (a sequence of adjacent outgoing edges) starting * and ending with the same node. * * <p>This method will detect any non-empty cycle, including self-loops (a cycle of length 1). */ public static <N> boolean hasCycle(Graph<N> graph) { int numEdges = graph.edges().size(); if (numEdges == 0) { return false; // An edge-free graph is acyclic by definition. } if (!graph.isDirected() && numEdges >= graph.nodes().size()) { return true; // Optimization for the undirected case: at least one cycle must exist. } Map<Object, NodeVisitState> visitedNodes = Maps.newHashMapWithExpectedSize(graph.nodes().size()); for (N node : graph.nodes()) { if (subgraphHasCycle(graph, visitedNodes, node, null)) { return true; } } return false; }
/** * Returns the set of nodes that are reachable from {@code node}. Node B is defined as reachable * from node A if there exists a path (a sequence of adjacent outgoing edges) starting at node A * and ending at node B. Note that a node is always reachable from itself via a zero-length path. * * <p>This is a "snapshot" based on the current topology of {@code graph}, rather than a live view * of the set of nodes reachable from {@code node}. In other words, the returned {@link Set} will * not be updated after modifications to {@code graph}. * * @throws IllegalArgumentException if {@code node} is not present in {@code graph} */ public static <N> Set<N> reachableNodes(Graph<N> graph, N node) { checkArgument(graph.nodes().contains(node), NODE_NOT_IN_GRAPH, node); Set<N> visitedNodes = new LinkedHashSet<N>(); Queue<N> queuedNodes = new ArrayDeque<N>(); visitedNodes.add(node); queuedNodes.add(node); // Perform a breadth-first traversal rooted at the input node. while (!queuedNodes.isEmpty()) { N currentNode = queuedNodes.remove(); for (N successor : graph.successors(currentNode)) { if (visitedNodes.add(successor)) { queuedNodes.add(successor); } } } return Collections.unmodifiableSet(visitedNodes); }
/** Creates a mutable copy of {@code graph} with the same nodes and edges. */ public static <N> MutableGraph<N> copyOf(Graph<N> graph) { MutableGraph<N> copy = GraphBuilder.from(graph).expectedNodeCount(graph.nodes().size()).build(); for (N node : graph.nodes()) { copy.addNode(node); } for (EndpointPair<N> edge : graph.edges()) { copy.putEdge(edge.nodeU(), edge.nodeV()); } return copy; }
/** * Returns true if {@code graph} has at least one cycle. A cycle is defined as a non-empty subset * of edges in a graph arranged to form a path (a sequence of adjacent outgoing edges) starting * and ending with the same node. * * <p>This method will detect any non-empty cycle, including self-loops (a cycle of length 1). */ public static <N> boolean hasCycle(Graph<N> graph) { int numEdges = graph.edges().size(); if (numEdges == 0) { return false; // An edge-free graph is acyclic by definition. } if (!graph.isDirected() && numEdges >= graph.nodes().size()) { return true; // Optimization for the undirected case: at least one cycle must exist. } Map<Object, NodeVisitState> visitedNodes = Maps.newHashMapWithExpectedSize(graph.nodes().size()); for (N node : graph.nodes()) { if (subgraphHasCycle(graph, visitedNodes, node, null)) { return true; } } return false; }
@Override public final boolean equals(@Nullable Object obj) { if (obj == this) { return true; } if (!(obj instanceof Graph)) { return false; } Graph<?> other = (Graph<?>) obj; return isDirected() == other.isDirected() && nodes().equals(other.nodes()) && edges().equals(other.edges()); }
private MutableGraph<EntityDescriptor> resolveDependencyGraph(Graph<EntityDescriptor> dependencyGraph, Set<EntityDescriptor> resolvedEntities) { final MutableGraph<EntityDescriptor> mutableGraph = GraphBuilder.from(dependencyGraph).build(); Graphs.merge(mutableGraph, dependencyGraph); for (EntityDescriptor entityDescriptor : dependencyGraph.nodes()) { LOG.debug("Resolving entity {}", entityDescriptor); if (resolvedEntities.contains(entityDescriptor)) { LOG.debug("Entity {} already resolved, skipping.", entityDescriptor); continue; } final EntityFacade<?> facade = entityFacades.getOrDefault(entityDescriptor.type(), UnsupportedEntityFacade.INSTANCE); final Graph<EntityDescriptor> graph = facade.resolveNativeEntity(entityDescriptor); LOG.trace("Dependencies of entity {}: {}", entityDescriptor, graph); Graphs.merge(mutableGraph, graph); LOG.trace("New dependency graph: {}", mutableGraph); resolvedEntities.add(entityDescriptor); final Graph<EntityDescriptor> result = resolveDependencyGraph(mutableGraph, resolvedEntities); Graphs.merge(mutableGraph, result); } return mutableGraph; }
@Override public final boolean equals(@NullableDecl Object obj) { if (obj == this) { return true; } if (!(obj instanceof Graph)) { return false; } Graph<?> other = (Graph<?>) obj; return isDirected() == other.isDirected() && nodes().equals(other.nodes()) && edges().equals(other.edges()); }
/** Creates a mutable copy of {@code graph} with the same nodes and edges. */ public static <N> MutableGraph<N> copyOf(Graph<N> graph) { MutableGraph<N> copy = GraphBuilder.from(graph).expectedNodeCount(graph.nodes().size()).build(); for (N node : graph.nodes()) { copy.addNode(node); } for (EndpointPair<N> edge : graph.edges()) { copy.putEdge(edge.nodeU(), edge.nodeV()); } return copy; }
@Override public final boolean equals(@NullableDecl Object obj) { if (obj == this) { return true; } if (!(obj instanceof Graph)) { return false; } Graph<?> other = (Graph<?>) obj; return isDirected() == other.isDirected() && nodes().equals(other.nodes()) && edges().equals(other.edges()); }
private static <N> void checkTransitiveClosure(Graph<N> originalGraph, Graph<N> expectedClosure) { for (N node : originalGraph.nodes()) { assertThat(reachableNodes(originalGraph, node)).isEqualTo(expectedClosure.successors(node)); } assertThat(transitiveClosure(originalGraph)).isEqualTo(expectedClosure); }
/** * Merge all nodes and edges of two graphs. * * @param graph1 A {@link MutableGraph} into which all nodes and edges of {@literal graph2} will be merged * @param graph2 The {@link Graph} whose nodes and edges will be merged into {@literal graph1} * @param <N> The class of the nodes */ public static <N> void merge(MutableGraph<N> graph1, Graph<N> graph2) { for (N node : graph2.nodes()) { graph1.addNode(node); } for (EndpointPair<N> edge : graph2.edges()) { graph1.putEdge(edge.nodeU(), edge.nodeV()); } } }
/** Creates a mutable copy of {@code graph} with the same nodes, edges, and edge values. */ public static <N, V> MutableValueGraph<N, V> copyOf(ValueGraph<N, V> graph) { MutableValueGraph<N, V> copy = ValueGraphBuilder.from(graph).expectedNodeCount(graph.nodes().size()).build(); for (N node : graph.nodes()) { copy.addNode(node); } for (EndpointPair<N> edge : graph.edges()) { copy.putEdgeValue( edge.nodeU(), edge.nodeV(), graph.edgeValueOrDefault(edge.nodeU(), edge.nodeV(), null)); } return copy; }
@After public void validateGraphState() { assertStronglyEquivalent(graph, Graphs.copyOf(graph)); assertStronglyEquivalent(graph, ImmutableValueGraph.copyOf(graph)); Graph<Integer> asGraph = graph.asGraph(); AbstractGraphTest.validateGraph(asGraph); assertThat(graph.nodes()).isEqualTo(asGraph.nodes()); assertThat(graph.edges()).isEqualTo(asGraph.edges()); assertThat(graph.nodeOrder()).isEqualTo(asGraph.nodeOrder()); assertThat(graph.isDirected()).isEqualTo(asGraph.isDirected()); assertThat(graph.allowsSelfLoops()).isEqualTo(asGraph.allowsSelfLoops()); for (Integer node : graph.nodes()) { assertThat(graph.adjacentNodes(node)).isEqualTo(asGraph.adjacentNodes(node)); assertThat(graph.predecessors(node)).isEqualTo(asGraph.predecessors(node)); assertThat(graph.successors(node)).isEqualTo(asGraph.successors(node)); assertThat(graph.degree(node)).isEqualTo(asGraph.degree(node)); assertThat(graph.inDegree(node)).isEqualTo(asGraph.inDegree(node)); assertThat(graph.outDegree(node)).isEqualTo(asGraph.outDegree(node)); for (Integer otherNode : graph.nodes()) { boolean hasEdge = graph.hasEdgeConnecting(node, otherNode); assertThat(hasEdge).isEqualTo(asGraph.hasEdgeConnecting(node, otherNode)); assertThat(graph.edgeValueOrDefault(node, otherNode, null) != null).isEqualTo(hasEdge); assertThat(!graph.edgeValueOrDefault(node, otherNode, DEFAULT).equals(DEFAULT)) .isEqualTo(hasEdge); } } }
for (N node : sanityCheckSet(graph.nodes())) { assertThat(nodeString).contains(node.toString());