ModuleExportsAnalyzer.java revision 3792:d516975e8110
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 = new Analyzer.Visitor() {
73            @Override
74            public void visitDependence(String origin, Archive originArchive,
75                                        String target, Archive targetArchive)
76            {
77                Set<String> jdkInternals =
78                    deps.computeIfAbsent(originArchive, _k -> new HashMap<>())
79                        .computeIfAbsent(targetArchive, _k -> new HashSet<>());
80
81                Module module = targetArchive.getModule();
82                if (originArchive.getModule() != module &&
83                        module.isJDK() && !module.isExported(target)) {
84                    // use of JDK internal APIs
85                    jdkInternals.add(target);
86                }
87            }
88        };
89
90        // visit the dependences
91        archives.stream()
92            .filter(analyzer::hasDependences)
93            .sorted(Comparator.comparing(Archive::getName))
94            .forEach(archive -> analyzer.visitDependences(archive, visitor));
95
96
97        // print the dependences on named modules
98        printDependences();
99
100        // print the dependences on unnamed module
101        deps.values().stream()
102            .flatMap(map -> map.keySet().stream())
103            .filter(archive -> !archive.getModule().isNamed())
104            .map(archive -> archive != NOT_FOUND
105                                ? "unnamed module: " + archive.getPathName()
106                                : archive.getPathName())
107            .distinct()
108            .sorted()
109            .forEach(archive -> writer.format("   %s%n", archive));
110
111        return rc;
112    }
113
114    private void printDependences() {
115        // find use of JDK internals
116        Map<Module, Set<String>> jdkinternals = new HashMap<>();
117        deps.keySet().stream()
118            .filter(source -> !source.getModule().isNamed())
119            .map(deps::get)
120            .flatMap(map -> map.entrySet().stream())
121            .filter(e -> e.getValue().size() > 0)
122            .forEach(e -> jdkinternals.computeIfAbsent(e.getKey().getModule(),
123                                                       _k -> new HashSet<>())
124                                      .addAll(e.getValue()));
125
126
127        // build module graph
128        ModuleGraphBuilder builder = new ModuleGraphBuilder(configuration);
129        Module root = new RootModule("root");
130        builder.addModule(root);
131        // find named module dependences
132        deps.keySet().stream()
133            .filter(source -> !source.getModule().isNamed())
134            .map(deps::get)
135            .flatMap(map -> map.keySet().stream())
136            .filter(m -> m.getModule().isNamed())
137            .map(Archive::getModule)
138            .forEach(m -> builder.addEdge(root, m));
139
140        // module dependences
141        Set<Module> modules = builder.build().adjacentNodes(root);
142
143        // if reduced is set, apply transition reduction
144        Set<Module> reducedSet;
145        if (reduced) {
146            Set<Module> nodes = builder.reduced().adjacentNodes(root);
147            if (nodes.size() == 1) {
148                // java.base only
149                reducedSet = nodes;
150            } else {
151                // java.base is mandated and can be excluded from the reduced graph
152                reducedSet = nodes.stream()
153                    .filter(m -> !"java.base".equals(m.name()) ||
154                                    jdkinternals.containsKey("java.base"))
155                    .collect(Collectors.toSet());
156            }
157        } else {
158            reducedSet = modules;
159        }
160
161        modules.stream()
162               .sorted(Comparator.comparing(Module::name))
163               .forEach(m -> {
164                    if (jdkinternals.containsKey(m)) {
165                        jdkinternals.get(m).stream()
166                            .sorted()
167                            .forEach(pn -> writer.format("   %s/%s%n", m, pn));
168                    } else if (reducedSet.contains(m)){
169                        // if the transition reduction is applied, show the reduced graph
170                        writer.format("   %s%n", m);
171                    }
172            });
173    }
174
175
176    private class RootModule extends Module {
177        final ModuleDescriptor descriptor;
178        RootModule(String name) {
179            super(name);
180
181            ModuleDescriptor.Builder builder = ModuleDescriptor.module(name);
182            this.descriptor = builder.build();
183        }
184
185        @Override
186        public ModuleDescriptor descriptor() {
187            return descriptor;
188        }
189    }
190
191}
192