Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
PojavLauncherTeam
GitHub Repository: PojavLauncherTeam/mobile
Path: blob/master/src/jdk.jdeps/share/classes/com/sun/tools/jdeps/JdepsWriter.java
41161 views
1
/*
2
* Copyright (c) 2015, 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
package com.sun.tools.jdeps;
26
27
import static com.sun.tools.jdeps.Analyzer.Type.*;
28
29
import java.io.IOException;
30
import java.io.PrintWriter;
31
import java.io.UncheckedIOException;
32
import java.lang.module.ModuleDescriptor.Requires;
33
import java.nio.file.Files;
34
import java.nio.file.Path;
35
import java.nio.file.Paths;
36
import java.util.Collection;
37
import java.util.Comparator;
38
import java.util.HashMap;
39
import java.util.Map;
40
import java.util.Optional;
41
42
public abstract class JdepsWriter {
43
public static JdepsWriter newDotWriter(Path outputdir, Analyzer.Type type) {
44
return new DotFileWriter(outputdir, type, false, true, false);
45
}
46
47
public static JdepsWriter newSimpleWriter(PrintWriter writer, Analyzer.Type type) {
48
return new SimpleWriter(writer, type, false, true);
49
}
50
51
final Analyzer.Type type;
52
final boolean showProfile;
53
final boolean showModule;
54
55
JdepsWriter(Analyzer.Type type, boolean showProfile, boolean showModule) {
56
this.type = type;
57
this.showProfile = showProfile;
58
this.showModule = showModule;
59
}
60
61
abstract void generateOutput(Collection<Archive> archives, Analyzer analyzer) throws IOException;
62
63
static class DotFileWriter extends JdepsWriter {
64
final boolean showLabel;
65
final Path outputDir;
66
DotFileWriter(Path dir, Analyzer.Type type,
67
boolean showProfile, boolean showModule, boolean showLabel) {
68
super(type, showProfile, showModule);
69
this.showLabel = showLabel;
70
this.outputDir = dir;
71
}
72
73
@Override
74
void generateOutput(Collection<Archive> archives, Analyzer analyzer)
75
throws IOException
76
{
77
Files.createDirectories(outputDir);
78
79
// output individual .dot file for each archive
80
if (type != SUMMARY && type != MODULE) {
81
archives.stream()
82
.filter(analyzer::hasDependences)
83
.forEach(archive -> {
84
// use the filename if path is present; otherwise
85
// use the module name e.g. from jrt file system
86
Path path = archive.path().orElse(Paths.get(archive.getName()));
87
Path dotfile = outputDir.resolve(path.getFileName().toString() + ".dot");
88
try (PrintWriter pw = new PrintWriter(Files.newOutputStream(dotfile));
89
DotFileFormatter formatter = new DotFileFormatter(pw, archive)) {
90
analyzer.visitDependences(archive, formatter);
91
} catch (IOException e) {
92
throw new UncheckedIOException(e);
93
}
94
});
95
}
96
// generate summary dot file
97
generateSummaryDotFile(archives, analyzer);
98
}
99
100
101
private void generateSummaryDotFile(Collection<Archive> archives, Analyzer analyzer)
102
throws IOException
103
{
104
// If verbose mode (-v or -verbose option),
105
// the summary.dot file shows package-level dependencies.
106
boolean isSummary = type == PACKAGE || type == SUMMARY || type == MODULE;
107
Analyzer.Type summaryType = isSummary ? SUMMARY : PACKAGE;
108
Path summary = outputDir.resolve("summary.dot");
109
try (PrintWriter sw = new PrintWriter(Files.newOutputStream(summary));
110
SummaryDotFile dotfile = new SummaryDotFile(sw, summaryType)) {
111
for (Archive archive : archives) {
112
if (isSummary) {
113
if (showLabel) {
114
// build labels listing package-level dependencies
115
analyzer.visitDependences(archive, dotfile.labelBuilder(), PACKAGE);
116
}
117
}
118
analyzer.visitDependences(archive, dotfile, summaryType);
119
}
120
}
121
}
122
123
class DotFileFormatter implements Analyzer.Visitor, AutoCloseable {
124
private final PrintWriter writer;
125
private final String name;
126
DotFileFormatter(PrintWriter writer, Archive archive) {
127
this.writer = writer;
128
this.name = archive.getName();
129
writer.format("digraph \"%s\" {%n", name);
130
writer.format(" // Path: %s%n", archive.getPathName());
131
}
132
133
@Override
134
public void close() {
135
writer.println("}");
136
}
137
138
@Override
139
public void visitDependence(String origin, Archive originArchive,
140
String target, Archive targetArchive) {
141
String tag = toTag(originArchive, target, targetArchive);
142
writer.format(" %-50s -> \"%s\";%n",
143
String.format("\"%s\"", origin),
144
tag.isEmpty() ? target
145
: String.format("%s (%s)", target, tag));
146
}
147
}
148
149
class SummaryDotFile implements Analyzer.Visitor, AutoCloseable {
150
private final PrintWriter writer;
151
private final Analyzer.Type type;
152
private final Map<Archive, Map<Archive,StringBuilder>> edges = new HashMap<>();
153
SummaryDotFile(PrintWriter writer, Analyzer.Type type) {
154
this.writer = writer;
155
this.type = type;
156
writer.format("digraph \"summary\" {%n");
157
}
158
159
@Override
160
public void close() {
161
writer.println("}");
162
}
163
164
@Override
165
public void visitDependence(String origin, Archive originArchive,
166
String target, Archive targetArchive) {
167
168
String targetName = type == PACKAGE ? target : targetArchive.getName();
169
if (targetArchive.getModule().isJDK()) {
170
Module m = (Module)targetArchive;
171
String n = showProfileOrModule(m);
172
if (!n.isEmpty()) {
173
targetName += " (" + n + ")";
174
}
175
} else if (type == PACKAGE) {
176
targetName += " (" + targetArchive.getName() + ")";
177
}
178
String label = getLabel(originArchive, targetArchive);
179
writer.format(" %-50s -> \"%s\"%s;%n",
180
String.format("\"%s\"", origin), targetName, label);
181
}
182
183
String getLabel(Archive origin, Archive target) {
184
if (edges.isEmpty())
185
return "";
186
187
StringBuilder label = edges.get(origin).get(target);
188
return label == null ? "" : String.format(" [label=\"%s\",fontsize=9]", label.toString());
189
}
190
191
Analyzer.Visitor labelBuilder() {
192
// show the package-level dependencies as labels in the dot graph
193
return new Analyzer.Visitor() {
194
@Override
195
public void visitDependence(String origin, Archive originArchive,
196
String target, Archive targetArchive)
197
{
198
edges.putIfAbsent(originArchive, new HashMap<>());
199
edges.get(originArchive).putIfAbsent(targetArchive, new StringBuilder());
200
StringBuilder sb = edges.get(originArchive).get(targetArchive);
201
String tag = toTag(originArchive, target, targetArchive);
202
addLabel(sb, origin, target, tag);
203
}
204
205
void addLabel(StringBuilder label, String origin, String target, String tag) {
206
label.append(origin).append(" -> ").append(target);
207
if (!tag.isEmpty()) {
208
label.append(" (" + tag + ")");
209
}
210
label.append("\\n");
211
}
212
};
213
}
214
}
215
}
216
217
static class SimpleWriter extends JdepsWriter {
218
final PrintWriter writer;
219
SimpleWriter(PrintWriter writer, Analyzer.Type type,
220
boolean showProfile, boolean showModule) {
221
super(type, showProfile, showModule);
222
this.writer = writer;
223
}
224
225
@Override
226
void generateOutput(Collection<Archive> archives, Analyzer analyzer) {
227
RawOutputFormatter depFormatter = new RawOutputFormatter(writer);
228
RawSummaryFormatter summaryFormatter = new RawSummaryFormatter(writer);
229
archives.stream()
230
.filter(analyzer::hasDependences)
231
.sorted(Comparator.comparing(Archive::getName))
232
.forEach(archive -> {
233
if (showModule && archive.getModule().isNamed() && type != SUMMARY) {
234
// print module-info except -summary
235
summaryFormatter.printModuleDescriptor(archive.getModule());
236
}
237
// print summary
238
analyzer.visitDependences(archive, summaryFormatter, SUMMARY);
239
240
if (analyzer.hasDependences(archive) && type != SUMMARY) {
241
// print the class-level or package-level dependences
242
analyzer.visitDependences(archive, depFormatter);
243
}
244
});
245
}
246
247
class RawOutputFormatter implements Analyzer.Visitor {
248
private final PrintWriter writer;
249
private String pkg = "";
250
251
RawOutputFormatter(PrintWriter writer) {
252
this.writer = writer;
253
}
254
255
@Override
256
public void visitDependence(String origin, Archive originArchive,
257
String target, Archive targetArchive) {
258
String tag = toTag(originArchive, target, targetArchive);
259
if (showModule || type == VERBOSE) {
260
writer.format(" %-50s -> %-50s %s%n", origin, target, tag);
261
} else {
262
if (!origin.equals(pkg)) {
263
pkg = origin;
264
writer.format(" %s (%s)%n", origin, originArchive.getName());
265
}
266
writer.format(" -> %-50s %s%n", target, tag);
267
}
268
}
269
}
270
271
class RawSummaryFormatter implements Analyzer.Visitor {
272
private final PrintWriter writer;
273
274
RawSummaryFormatter(PrintWriter writer) {
275
this.writer = writer;
276
}
277
278
@Override
279
public void visitDependence(String origin, Archive originArchive,
280
String target, Archive targetArchive) {
281
282
String targetName = targetArchive.getPathName();
283
if (targetArchive.getModule().isNamed()) {
284
targetName = targetArchive.getModule().name();
285
}
286
writer.format("%s -> %s", originArchive.getName(), targetName);
287
if (showProfile && targetArchive.getModule().isJDK()) {
288
writer.format(" (%s)", target);
289
}
290
writer.format("%n");
291
}
292
293
public void printModuleDescriptor(Module module) {
294
if (!module.isNamed())
295
return;
296
297
writer.format("%s%s%n", module.name(), module.isAutomatic() ? " automatic" : "");
298
writer.format(" [%s]%n", module.location());
299
module.descriptor().requires()
300
.stream()
301
.sorted(Comparator.comparing(Requires::name))
302
.forEach(req -> writer.format(" requires %s%n", req));
303
}
304
}
305
}
306
307
/**
308
* If the given archive is JDK archive, this method returns the profile name
309
* only if -profile option is specified; it accesses a private JDK API and
310
* the returned value will have "JDK internal API" prefix
311
*
312
* For non-JDK archives, this method returns the file name of the archive.
313
*/
314
String toTag(Archive source, String name, Archive target) {
315
if (source == target || !target.getModule().isNamed() || Analyzer.notFound(target)) {
316
return target.getName();
317
}
318
319
Module module = target.getModule();
320
String pn = name;
321
if ((type == CLASS || type == VERBOSE)) {
322
int i = name.lastIndexOf('.');
323
pn = i > 0 ? name.substring(0, i) : "";
324
}
325
326
// exported API
327
if (module.isExported(pn) && !module.isJDKUnsupported()) {
328
return showProfileOrModule(module);
329
}
330
331
// JDK internal API
332
if (!source.getModule().isJDK() && module.isJDK()){
333
return "JDK internal API (" + module.name() + ")";
334
}
335
336
// qualified exports or inaccessible
337
boolean isExported = module.isExported(pn, source.getModule().name());
338
return module.name() + (isExported ? " (qualified)" : " (internal)");
339
}
340
341
String showProfileOrModule(Module m) {
342
String tag = "";
343
if (showProfile) {
344
Profile p = Profile.getProfile(m);
345
if (p != null) {
346
tag = p.profileName();
347
}
348
} else if (showModule) {
349
tag = m.name();
350
}
351
return tag;
352
}
353
354
Profile getProfile(String name) {
355
String pn = name;
356
if (type == CLASS || type == VERBOSE) {
357
int i = name.lastIndexOf('.');
358
pn = i > 0 ? name.substring(0, i) : "";
359
}
360
return Profile.getProfile(pn);
361
}
362
363
}
364
365