Path: blob/master/src/jdk.jdeps/share/classes/com/sun/tools/jdeps/Graph.java
41161 views
/*1* Copyright (c) 2016, 2017, Oracle and/or its affiliates. All rights reserved.2* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.3*4* This code is free software; you can redistribute it and/or modify it5* under the terms of the GNU General Public License version 2 only, as6* published by the Free Software Foundation. Oracle designates this7* particular file as subject to the "Classpath" exception as provided8* by Oracle in the LICENSE file that accompanied this code.9*10* This code is distributed in the hope that it will be useful, but WITHOUT11* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or12* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License13* version 2 for more details (a copy is included in the LICENSE file that14* accompanied this code).15*16* You should have received a copy of the GNU General Public License version17* 2 along with this work; if not, write to the Free Software Foundation,18* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.19*20* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA21* or visit www.oracle.com if you need additional information or have any22* questions.23*/24package com.sun.tools.jdeps;2526import java.io.PrintWriter;27import java.util.ArrayDeque;28import java.util.Collections;29import java.util.Deque;30import java.util.HashMap;31import java.util.HashSet;32import java.util.Map;33import java.util.Set;34import java.util.function.Consumer;35import java.util.stream.Collectors;36import java.util.stream.Stream;3738public final class Graph<T> {39private final Set<T> nodes;40private final Map<T, Set<T>> edges;4142public Graph(Set<T> nodes, Map<T, Set<T>> edges) {43this.nodes = Collections.unmodifiableSet(nodes);44this.edges = Collections.unmodifiableMap(edges);45}4647public Set<T> nodes() {48return nodes;49}5051public Map<T, Set<T>> edges() {52return edges;53}5455public Set<T> adjacentNodes(T u) {56return edges.get(u);57}5859public boolean contains(T u) {60return nodes.contains(u);61}6263public Set<Edge<T>> edgesFrom(T u) {64return edges.get(u).stream()65.map(v -> new Edge<T>(u, v))66.collect(Collectors.toSet());67}6869/**70* Returns a new Graph after transitive reduction71*/72public Graph<T> reduce() {73Builder<T> builder = new Builder<>();74nodes.stream()75.forEach(u -> {76builder.addNode(u);77edges.get(u).stream()78.filter(v -> !pathExists(u, v, false))79.forEach(v -> builder.addEdge(u, v));80});81return builder.build();82}8384/**85* Returns a new Graph after transitive reduction. All edges in86* the given g takes precedence over this graph.87*88* @throw IllegalArgumentException g must be a subgraph this graph89*/90public Graph<T> reduce(Graph<T> g) {91boolean subgraph = nodes.containsAll(g.nodes) &&92g.edges.keySet().stream()93.allMatch(u -> adjacentNodes(u).containsAll(g.adjacentNodes(u)));94if (!subgraph) {95throw new IllegalArgumentException(g + " is not a subgraph of " + this);96}9798Builder<T> builder = new Builder<>();99nodes.stream()100.forEach(u -> {101builder.addNode(u);102// filter the edge if there exists a path from u to v in the given g103// or there exists another path from u to v in this graph104edges.get(u).stream()105.filter(v -> !g.pathExists(u, v) && !pathExists(u, v, false))106.forEach(v -> builder.addEdge(u, v));107});108109// add the overlapped edges from this graph and the given g110g.edges().keySet().stream()111.forEach(u -> g.adjacentNodes(u).stream()112.filter(v -> isAdjacent(u, v))113.forEach(v -> builder.addEdge(u, v)));114return builder.build().reduce();115}116117/**118* Returns nodes sorted in topological order.119*/120public Stream<T> orderedNodes() {121TopoSorter<T> sorter = new TopoSorter<>(this);122return sorter.result.stream();123}124125/**126* Traverse this graph and performs the given action in topological order127*/128public void ordered(Consumer<T> action) {129TopoSorter<T> sorter = new TopoSorter<>(this);130sorter.ordered(action);131}132133/**134* Traverses this graph and performs the given action in reverse topological order135*/136public void reverse(Consumer<T> action) {137TopoSorter<T> sorter = new TopoSorter<>(this);138sorter.reverse(action);139}140141/**142* Returns a transposed graph from this graph143*/144public Graph<T> transpose() {145Builder<T> builder = new Builder<>();146builder.addNodes(nodes);147// reverse edges148edges.keySet().forEach(u -> {149edges.get(u).stream()150.forEach(v -> builder.addEdge(v, u));151});152return builder.build();153}154155/**156* Returns all nodes reachable from the given set of roots.157*/158public Set<T> dfs(Set<T> roots) {159Deque<T> deque = new ArrayDeque<>(roots);160Set<T> visited = new HashSet<>();161while (!deque.isEmpty()) {162T u = deque.pop();163if (!visited.contains(u)) {164visited.add(u);165if (contains(u)) {166adjacentNodes(u).stream()167.filter(v -> !visited.contains(v))168.forEach(deque::push);169}170}171}172return visited;173}174175private boolean isAdjacent(T u, T v) {176return edges.containsKey(u) && edges.get(u).contains(v);177}178179private boolean pathExists(T u, T v) {180return pathExists(u, v, true);181}182183/**184* Returns true if there exists a path from u to v in this graph.185* If includeAdjacent is false, it returns true if there exists186* another path from u to v of distance > 1187*/188private boolean pathExists(T u, T v, boolean includeAdjacent) {189if (!nodes.contains(u) || !nodes.contains(v)) {190return false;191}192if (includeAdjacent && isAdjacent(u, v)) {193return true;194}195Deque<T> stack = new ArrayDeque<>();196Set<T> visited = new HashSet<>();197stack.push(u);198while (!stack.isEmpty()) {199T node = stack.pop();200if (node.equals(v)) {201return true;202}203if (!visited.contains(node)) {204visited.add(node);205edges.get(node).stream()206.filter(e -> includeAdjacent || !node.equals(u) || !e.equals(v))207.forEach(stack::push);208}209}210assert !visited.contains(v);211return false;212}213214public void printGraph(PrintWriter out) {215out.println("graph for " + nodes);216nodes.stream()217.forEach(u -> adjacentNodes(u).stream()218.forEach(v -> out.format(" %s -> %s%n", u, v)));219}220221@Override222public String toString() {223return nodes.toString();224}225226static class Edge<T> {227final T u;228final T v;229Edge(T u, T v) {230this.u = u;231this.v = v;232}233234@Override235public String toString() {236return String.format("%s -> %s", u, v);237}238239@Override240public boolean equals(Object o) {241if (this == o) return true;242if (o == null || !(o instanceof Edge))243return false;244245@SuppressWarnings("unchecked")246Edge<T> edge = (Edge<T>) o;247248return u.equals(edge.u) && v.equals(edge.v);249}250251@Override252public int hashCode() {253int result = u.hashCode();254result = 31 * result + v.hashCode();255return result;256}257}258259static class Builder<T> {260final Set<T> nodes = new HashSet<>();261final Map<T, Set<T>> edges = new HashMap<>();262263public void addNode(T node) {264if (nodes.contains(node)) {265return;266}267nodes.add(node);268edges.computeIfAbsent(node, _e -> new HashSet<>());269}270271public void addNodes(Set<T> nodes) {272this.nodes.addAll(nodes);273}274275public void addEdge(T u, T v) {276addNode(u);277addNode(v);278edges.get(u).add(v);279}280281public Graph<T> build() {282return new Graph<T>(nodes, edges);283}284}285286/**287* Topological sort288*/289static class TopoSorter<T> {290final Deque<T> result = new ArrayDeque<>();291final Graph<T> graph;292TopoSorter(Graph<T> graph) {293this.graph = graph;294sort();295}296297public void ordered(Consumer<T> action) {298result.iterator().forEachRemaining(action);299}300301public void reverse(Consumer<T> action) {302result.descendingIterator().forEachRemaining(action);303}304305private void sort() {306Set<T> visited = new HashSet<>();307Set<T> done = new HashSet<>();308for (T node : graph.nodes()) {309if (!visited.contains(node)) {310visit(node, visited, done);311}312}313}314315private void visit(T node, Set<T> visited, Set<T> done) {316if (visited.contains(node)) {317if (!done.contains(node)) {318throw new IllegalArgumentException("Cyclic detected: " +319node + " " + graph.edges().get(node));320}321return;322}323visited.add(node);324graph.edges().get(node).stream()325.forEach(x -> visit(x, visited, done));326done.add(node);327result.addLast(node);328}329}330}331332333