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 } | 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 Map<String, Set<String>> missingDeps = new HashMap<>(); 53 private final boolean showInternals; 54 private final boolean reduced; 55 private final PrintWriter writer; 56 private final String separator; 57 public ModuleExportsAnalyzer(JdepsConfiguration config, 58 JdepsFilter filter, 59 boolean showInternals, 60 boolean reduced, 61 PrintWriter writer, 62 String separator) { 63 super(config, filter, null, 64 Analyzer.Type.PACKAGE, 65 false /* all classes */); 66 this.showInternals = showInternals; 67 this.reduced = reduced; 68 this.writer = writer; 69 this.separator = separator; 70 } 71 72 public boolean run(int maxDepth, boolean ignoreMissingDeps) throws IOException { 73 // use compile time view so that the entire archive on classpath is analyzed 74 boolean rc = super.run(true, maxDepth); 75 76 // A visitor to record the module-level dependences as well as 77 // use of internal APIs 78 Analyzer.Visitor visitor = (origin, originArchive, target, targetArchive) -> { 79 Set<String> internals = 80 deps.computeIfAbsent(originArchive, _k -> new HashMap<>()) 81 .computeIfAbsent(targetArchive, _k -> new HashSet<>()); 82 83 Module module = targetArchive.getModule(); 84 if (showInternals && originArchive.getModule() != module && 85 module.isNamed() && !module.isExported(target, module.name())) { 86 // use of internal APIs 87 internals.add(target); 88 } 89 if (!ignoreMissingDeps && Analyzer.notFound(targetArchive)) { 90 Set<String> notFound = 91 missingDeps.computeIfAbsent(origin, _k -> new HashSet<>()); 92 notFound.add(target); 93 } 94 }; 95 96 // visit the dependences 97 archives.stream() 98 .filter(analyzer::hasDependences) 99 .sorted(Comparator.comparing(Archive::getName)) 100 .forEach(archive -> analyzer.visitDependences(archive, visitor)); 101 102 // error if any missing dependence 103 if (!rc || !missingDeps.isEmpty()) { 104 return false; 105 } 106 107 Map<Module, Set<String>> internalPkgs = internalPackages(); 108 Set<Module> modules = modules(); 109 if (showInternals) { 110 // print modules and JDK internal API dependences 111 Stream.concat(modules.stream(), internalPkgs.keySet().stream()) 112 .sorted(Comparator.comparing(Module::name)) 113 .distinct() 114 .forEach(m -> { 115 if (internalPkgs.containsKey(m)) { 116 internalPkgs.get(m).stream() 117 .forEach(pn -> writer.format(" %s/%s%s", m, pn, separator)); 118 } else { 119 writer.format(" %s%s", m, separator); 120 } 121 }); 122 } else { 123 // print module dependences 124 writer.println(modules.stream().map(Module::name).sorted() 125 .collect(Collectors.joining(separator))); 126 } 127 return rc; 128 } 129 130 /* 131 * Prints missing dependences 132 */ 133 void visitMissingDeps(Analyzer.Visitor visitor) { 134 archives.stream() 135 .filter(analyzer::hasDependences) 136 .sorted(Comparator.comparing(Archive::getName)) 137 .filter(m -> analyzer.requires(m).anyMatch(Analyzer::notFound)) 138 .forEach(m -> { 139 analyzer.visitDependences(m, visitor, Analyzer.Type.VERBOSE, Analyzer::notFound); 140 }); 141 } 142 143 private Set<Module> modules() { 144 // build module graph 145 ModuleGraphBuilder builder = new ModuleGraphBuilder(configuration); 146 Module root = new RootModule(); 147 builder.addModule(root); 148 // find named module dependences 149 dependenceStream() 150 .flatMap(map -> map.keySet().stream()) 151 .filter(m -> m.getModule().isNamed() && !configuration.rootModules().contains(m)) 152 .map(Archive::getModule) 153 .forEach(m -> builder.addEdge(root, m)); 154 155 // build module dependence graph 156 // if reduced is set, apply transition reduction 157 Graph<Module> g = reduced ? builder.reduced() : builder.build(); 158 return g.adjacentNodes(root); 159 } 160 161 private Map<Module, Set<String>> internalPackages() { 162 Map<Module, Set<String>> internalPkgs = new HashMap<>(); 163 dependenceStream() 164 .flatMap(map -> map.entrySet().stream()) 165 .filter(e -> e.getValue().size() > 0) 166 .forEach(e -> internalPkgs.computeIfAbsent(e.getKey().getModule(), 167 _k -> new TreeSet<>()) 168 .addAll(e.getValue())); 169 return internalPkgs; 170 } 171 172 /* 173 * Returns a stream of dependence map from an Archive to the set of JDK 174 * internal APIs being used. 175 */ 176 private Stream<Map<Archive, Set<String>>> dependenceStream() { 177 return deps.keySet().stream() 178 .filter(source -> !source.getModule().isNamed() 179 || configuration.rootModules().contains(source)) 180 .map(deps::get); 181 } 182 183 /* 184 * RootModule serves as the root node for building the module graph 185 */ 186 private static class RootModule extends Module { 187 static final String NAME = "root"; 188 RootModule() { 189 super(NAME, ModuleDescriptor.newModule(NAME).build(), false); 190 } 191 } 192 193 } |