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 26 package com.sun.tools.jdeps; 27 28 import java.io.IOException; 29 import java.io.PrintWriter; 30 import java.lang.module.ModuleDescriptor; 31 import java.util.Comparator; 32 import java.util.HashMap; 33 import java.util.HashSet; 34 import java.util.Map; 35 import java.util.Set; 36 import java.util.TreeSet; 37 import java.util.stream.Collectors; 38 import java.util.stream.Stream; 39 40 /** 41 * Analyze module dependences and any reference to JDK internal APIs. 42 * It can apply transition reduction on the resulting module graph. 43 * 44 * The result prints one line per module it depends on 45 * one line per JDK internal API package it references: 46 * $MODULE[/$PACKAGE] 47 * 48 */ 49 public class ModuleExportsAnalyzer extends DepsAnalyzer { 50 // source archive to its dependences and JDK internal APIs it references 51 private final Map<Archive, Map<Archive,Set<String>>> deps = new HashMap<>(); 52 private final boolean showJdkInternals; 53 private final boolean reduced; 54 private final PrintWriter writer; 55 private final String separator; 56 public ModuleExportsAnalyzer(JdepsConfiguration config, 57 JdepsFilter filter, 58 boolean showJdkInternals, 59 boolean reduced, 60 PrintWriter writer, 61 String separator) { 62 super(config, filter, null, 63 Analyzer.Type.PACKAGE, 64 false /* all classes */); 65 this.showJdkInternals = showJdkInternals; 66 this.reduced = reduced; 67 this.writer = writer; 68 this.separator = separator; 69 } 70 71 @Override 72 public boolean run() throws IOException { 73 // analyze dependences 74 boolean rc = super.run(); 75 76 // A visitor to record the module-level dependences as well as 77 // use of JDK internal APIs 78 Analyzer.Visitor visitor = (origin, originArchive, target, targetArchive) -> { 79 Set<String> jdkInternals = 80 deps.computeIfAbsent(originArchive, _k -> new HashMap<>()) 81 .computeIfAbsent(targetArchive, _k -> new HashSet<>()); 82 83 Module module = targetArchive.getModule(); 84 if (showJdkInternals && originArchive.getModule() != module && 85 module.isJDK() && !module.isExported(target)) { 86 // use of JDK internal APIs 87 jdkInternals.add(target); 88 } 89 }; 90 91 // visit the dependences 92 archives.stream() 93 .filter(analyzer::hasDependences) 94 .sorted(Comparator.comparing(Archive::getName)) 95 .forEach(archive -> analyzer.visitDependences(archive, visitor)); 96 97 Set<Module> modules = modules(); 98 if (showJdkInternals) { 99 // print modules and JDK internal API dependences 100 printDependences(modules); 101 } else { 102 // print module dependences 103 writer.println(modules.stream().map(Module::name).sorted() 104 .collect(Collectors.joining(separator))); 105 } 106 return rc; 107 } 108 109 private Set<Module> modules() { 110 // build module graph 111 ModuleGraphBuilder builder = new ModuleGraphBuilder(configuration); 112 Module root = new RootModule("root"); 113 builder.addModule(root); 114 // find named module dependences 115 dependenceStream() 116 .flatMap(map -> map.keySet().stream()) 117 .filter(m -> m.getModule().isNamed() 118 && !configuration.rootModules().contains(m)) 119 .map(Archive::getModule) 120 .forEach(m -> builder.addEdge(root, m)); 121 122 // build module dependence graph 123 // if reduced is set, apply transition reduction 124 Graph<Module> g = reduced ? builder.reduced() : builder.build(); 125 return g.adjacentNodes(root); 126 } 127 128 private void printDependences(Set<Module> modules) { 129 // find use of JDK internals 130 Map<Module, Set<String>> jdkinternals = new HashMap<>(); 131 dependenceStream() 132 .flatMap(map -> map.entrySet().stream()) 133 .filter(e -> e.getValue().size() > 0) 134 .forEach(e -> jdkinternals.computeIfAbsent(e.getKey().getModule(), 135 _k -> new TreeSet<>()) 136 .addAll(e.getValue())); 137 138 // print modules and JDK internal API dependences 139 Stream.concat(modules.stream(), jdkinternals.keySet().stream()) 140 .sorted(Comparator.comparing(Module::name)) 141 .distinct() 142 .forEach(m -> { 143 if (jdkinternals.containsKey(m)) { 144 jdkinternals.get(m).stream() 145 .forEach(pn -> writer.format(" %s/%s%s", m, pn, separator)); 146 } else { 147 writer.format(" %s%s", m, separator); 148 } 149 }); 150 } 151 152 /* 153 * Returns a stream of dependence map from an Archive to the set of JDK 154 * internal APIs being used. 155 */ 156 private Stream<Map<Archive, Set<String>>> dependenceStream() { 157 return deps.keySet().stream() 158 .filter(source -> !source.getModule().isNamed() 159 || configuration.rootModules().contains(source)) 160 .map(deps::get); 161 } 162 163 private class RootModule extends Module { 164 final ModuleDescriptor descriptor; 165 RootModule(String name) { 166 super(name); 167 168 ModuleDescriptor.Builder builder = ModuleDescriptor.newModule(name); 169 this.descriptor = builder.build(); 170 } 171 172 @Override 173 public ModuleDescriptor descriptor() { 174 return descriptor; 175 } 176 } 177 178 }