ModuleExportsAnalyzer.java revision 3827:44bdefe64114
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.IOException;
29import java.io.PrintWriter;
30import java.lang.module.ModuleDescriptor;
31import java.util.Comparator;
32import java.util.HashMap;
33import java.util.HashSet;
34import java.util.Map;
35import java.util.Set;
36import java.util.stream.Collectors;
37
38import static com.sun.tools.jdeps.Analyzer.NOT_FOUND;
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 */
49public 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 reduced;
53    private final PrintWriter writer;
54    public ModuleExportsAnalyzer(JdepsConfiguration config,
55                                 JdepsFilter filter,
56                                 boolean reduced,
57                                 PrintWriter writer) {
58        super(config, filter, null,
59              Analyzer.Type.PACKAGE,
60              false /* all classes */);
61        this.reduced = reduced;
62        this.writer = writer;
63    }
64
65    @Override
66    public boolean run() throws IOException {
67        // analyze dependences
68        boolean rc = super.run();
69
70        // A visitor to record the module-level dependences as well as
71        // use of JDK internal APIs
72        Analyzer.Visitor visitor = (origin, originArchive, target, targetArchive) -> {
73            Set<String> jdkInternals =
74                deps.computeIfAbsent(originArchive, _k -> new HashMap<>())
75                    .computeIfAbsent(targetArchive, _k -> new HashSet<>());
76
77            Module module = targetArchive.getModule();
78            if (originArchive.getModule() != module &&
79                    module.isJDK() && !module.isExported(target)) {
80                // use of JDK internal APIs
81                jdkInternals.add(target);
82            }
83        };
84
85        // visit the dependences
86        archives.stream()
87            .filter(analyzer::hasDependences)
88            .sorted(Comparator.comparing(Archive::getName))
89            .forEach(archive -> analyzer.visitDependences(archive, visitor));
90
91
92        // print the dependences on named modules
93        printDependences();
94
95        // print the dependences on unnamed module
96        deps.values().stream()
97            .flatMap(map -> map.keySet().stream())
98            .filter(archive -> !archive.getModule().isNamed())
99            .map(archive -> archive != NOT_FOUND
100                                ? "unnamed module: " + archive.getPathName()
101                                : archive.getPathName())
102            .distinct()
103            .sorted()
104            .forEach(archive -> writer.format("   %s%n", archive));
105
106        return rc;
107    }
108
109    private void printDependences() {
110        // find use of JDK internals
111        Map<Module, Set<String>> jdkinternals = new HashMap<>();
112        deps.keySet().stream()
113            .filter(source -> !source.getModule().isNamed())
114            .map(deps::get)
115            .flatMap(map -> map.entrySet().stream())
116            .filter(e -> e.getValue().size() > 0)
117            .forEach(e -> jdkinternals.computeIfAbsent(e.getKey().getModule(),
118                                                       _k -> new HashSet<>())
119                                      .addAll(e.getValue()));
120
121
122        // build module graph
123        ModuleGraphBuilder builder = new ModuleGraphBuilder(configuration);
124        Module root = new RootModule("root");
125        builder.addModule(root);
126        // find named module dependences
127        deps.keySet().stream()
128            .filter(source -> !source.getModule().isNamed())
129            .map(deps::get)
130            .flatMap(map -> map.keySet().stream())
131            .filter(m -> m.getModule().isNamed())
132            .map(Archive::getModule)
133            .forEach(m -> builder.addEdge(root, m));
134
135        // module dependences
136        Set<Module> modules = builder.build().adjacentNodes(root);
137
138        // if reduced is set, apply transition reduction
139        Set<Module> reducedSet;
140        if (reduced) {
141            Set<Module> nodes = builder.reduced().adjacentNodes(root);
142            if (nodes.size() == 1) {
143                // java.base only
144                reducedSet = nodes;
145            } else {
146                // java.base is mandated and can be excluded from the reduced graph
147                reducedSet = nodes.stream()
148                    .filter(m -> !"java.base".equals(m.name()) ||
149                                    jdkinternals.containsKey("java.base"))
150                    .collect(Collectors.toSet());
151            }
152        } else {
153            reducedSet = modules;
154        }
155
156        modules.stream()
157               .sorted(Comparator.comparing(Module::name))
158               .forEach(m -> {
159                    if (jdkinternals.containsKey(m)) {
160                        jdkinternals.get(m).stream()
161                            .sorted()
162                            .forEach(pn -> writer.format("   %s/%s%n", m, pn));
163                    } else if (reducedSet.contains(m)){
164                        // if the transition reduction is applied, show the reduced graph
165                        writer.format("   %s%n", m);
166                    }
167            });
168    }
169
170
171    private class RootModule extends Module {
172        final ModuleDescriptor descriptor;
173        RootModule(String name) {
174            super(name);
175
176            ModuleDescriptor.Builder builder = ModuleDescriptor.module(name);
177            this.descriptor = builder.build();
178        }
179
180        @Override
181        public ModuleDescriptor descriptor() {
182            return descriptor;
183        }
184    }
185
186}
187