1/* 2 * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 26package com.sun.tools.jdeps; 27 28import java.io.PrintWriter; 29import java.lang.module.ModuleDescriptor; 30import java.util.Deque; 31import java.util.HashSet; 32import java.util.LinkedList; 33import java.util.Map; 34import java.util.Optional; 35import java.util.Set; 36import java.util.stream.Stream; 37 38import static java.lang.module.ModuleDescriptor.Requires.Modifier.*; 39import static com.sun.tools.jdeps.Module.*; 40 41/** 42 * A builder to create a Graph<Module> 43 */ 44public class ModuleGraphBuilder extends Graph.Builder<Module> { 45 final JdepsConfiguration config; 46 47 ModuleGraphBuilder(JdepsConfiguration config) { 48 this.config = config; 49 } 50 51 /** 52 * Adds a module to the graph. 53 */ 54 ModuleGraphBuilder addModule(Module module) { 55 addNode(module); 56 return this; 57 } 58 59 /** 60 * Apply transitive reduction on the resulting graph 61 */ 62 public Graph<Module> reduced() { 63 Graph<Module> graph = build(); 64 // transitive reduction 65 Graph<Module> newGraph = buildGraph(graph.edges()).reduce(); 66 67 if (DEBUG) { 68 PrintWriter log = new PrintWriter(System.err); 69 System.err.println("before transitive reduction: "); 70 graph.printGraph(log); 71 System.err.println("after transitive reduction: "); 72 newGraph.printGraph(log); 73 } 74 75 return newGraph; 76 } 77 78 public Graph<Module> buildGraph() { 79 Graph<Module> graph = build(); 80 return buildGraph(graph.edges()); 81 } 82 83 /** 84 * Build a graph of module from the given dependences. 85 * 86 * It transitively includes all implied read edges. 87 */ 88 private Graph<Module> buildGraph(Map<Module, Set<Module>> edges) { 89 Graph.Builder<Module> builder = new Graph.Builder<>(); 90 Set<Module> visited = new HashSet<>(); 91 Deque<Module> deque = new LinkedList<>(); 92 edges.entrySet().stream().forEach(e -> { 93 Module m = e.getKey(); 94 deque.add(m); 95 e.getValue().stream().forEach(v -> { 96 deque.add(v); 97 builder.addEdge(m, v); 98 }); 99 }); 100 101 // read requires transitive from ModuleDescriptor 102 Module source; 103 while ((source = deque.poll()) != null) { 104 if (visited.contains(source)) 105 continue; 106 107 visited.add(source); 108 builder.addNode(source); 109 Module from = source; 110 requiresTransitive(from).forEach(m -> { 111 deque.add(m); 112 builder.addEdge(from, m); 113 }); 114 } 115 return builder.build(); 116 } 117 118 /* 119 * Returns a stream of modules upon which the given module `requires transitive` 120 */ 121 public Stream<Module> requiresTransitive(Module m) { 122 // find requires transitive 123 return m.descriptor() 124 .requires().stream() 125 .filter(req -> req.modifiers().contains(TRANSITIVE)) 126 .map(ModuleDescriptor.Requires::name) 127 .map(config::findModule) 128 .flatMap(Optional::stream); 129 } 130} 131