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/ModuleDotGraph.java
41161 views
1
/*
2
* Copyright (c) 2017, 2018, 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 java.lang.module.ModuleDescriptor.Requires.Modifier.*;
28
import static java.util.stream.Collectors.*;
29
30
import java.io.BufferedWriter;
31
import java.io.IOException;
32
import java.io.PrintWriter;
33
import java.lang.module.Configuration;
34
import java.lang.module.ModuleDescriptor;
35
import java.lang.module.ModuleDescriptor.*;
36
import java.lang.module.ModuleFinder;
37
import java.lang.module.ModuleReference;
38
import java.lang.module.ResolvedModule;
39
import java.nio.file.Files;
40
import java.nio.file.Path;
41
import java.util.ArrayDeque;
42
import java.util.ArrayList;
43
import java.util.Collections;
44
import java.util.Deque;
45
import java.util.HashSet;
46
import java.util.List;
47
import java.util.Locale;
48
import java.util.Map;
49
import java.util.Objects;
50
import java.util.Optional;
51
import java.util.Set;
52
import java.util.TreeSet;
53
import java.util.function.Function;
54
import java.util.stream.Collectors;
55
import java.util.stream.Stream;
56
57
/**
58
* Generate dot graph for modules
59
*/
60
public class ModuleDotGraph {
61
private final JdepsConfiguration config;
62
private final Map<String, Configuration> configurations;
63
private final boolean apiOnly;
64
public ModuleDotGraph(JdepsConfiguration config, boolean apiOnly) {
65
this(config,
66
config.rootModules().stream()
67
.map(Module::name)
68
.sorted()
69
.collect(toMap(Function.identity(), mn -> config.resolve(Set.of(mn)))),
70
apiOnly);
71
}
72
73
public ModuleDotGraph(Map<String, Configuration> configurations, boolean apiOnly) {
74
this(null, configurations, apiOnly);
75
}
76
77
private ModuleDotGraph(JdepsConfiguration config,
78
Map<String, Configuration> configurations,
79
boolean apiOnly) {
80
this.configurations = configurations;
81
this.apiOnly = apiOnly;
82
this.config = config;
83
}
84
85
/**
86
* Generate dotfile for all modules
87
*
88
* @param dir output directory
89
*/
90
public boolean genDotFiles(Path dir) throws IOException {
91
return genDotFiles(dir, DotGraphAttributes.DEFAULT);
92
}
93
94
public boolean genDotFiles(Path dir, Attributes attributes)
95
throws IOException
96
{
97
Files.createDirectories(dir);
98
for (String mn : configurations.keySet()) {
99
Path path = dir.resolve(toDotFileBaseName(mn) + ".dot");
100
genDotFile(path, mn, configurations.get(mn), attributes);
101
}
102
return true;
103
}
104
105
private String toDotFileBaseName(String mn) {
106
if (config == null)
107
return mn;
108
109
Optional<Path> path = config.findModule(mn).flatMap(Module::path);
110
if (path.isPresent())
111
return path.get().getFileName().toString();
112
else
113
return mn;
114
}
115
/**
116
* Generate dotfile of the given path
117
*/
118
public void genDotFile(Path path, String name,
119
Configuration configuration,
120
Attributes attributes)
121
throws IOException
122
{
123
// transitive reduction
124
Graph<String> graph = apiOnly
125
? requiresTransitiveGraph(configuration, Set.of(name))
126
: gengraph(configuration);
127
128
DotGraphBuilder builder = new DotGraphBuilder(name, graph, attributes);
129
builder.subgraph("se", "java", attributes.javaSubgraphColor(),
130
DotGraphBuilder.JAVA_SE_SUBGRAPH)
131
.subgraph("jdk", "jdk", attributes.jdkSubgraphColor(),
132
DotGraphBuilder.JDK_SUBGRAPH)
133
.modules(graph.nodes().stream()
134
.map(mn -> configuration.findModule(mn).get()
135
.reference().descriptor()));
136
// build dot file
137
builder.build(path);
138
}
139
140
/**
141
* Returns a Graph of the given Configuration after transitive reduction.
142
*
143
* Transitive reduction of requires transitive edge and requires edge have
144
* to be applied separately to prevent the requires transitive edges
145
* (e.g. U -> V) from being reduced by a path (U -> X -> Y -> V)
146
* in which V would not be re-exported from U.
147
*/
148
private Graph<String> gengraph(Configuration cf) {
149
Graph.Builder<String> builder = new Graph.Builder<>();
150
cf.modules().stream()
151
.forEach(rm -> {
152
String mn = rm.name();
153
builder.addNode(mn);
154
rm.reads().stream()
155
.map(ResolvedModule::name)
156
.forEach(target -> builder.addEdge(mn, target));
157
});
158
159
Graph<String> rpg = requiresTransitiveGraph(cf, builder.nodes);
160
return builder.build().reduce(rpg);
161
}
162
163
164
/**
165
* Returns a Graph containing only requires transitive edges
166
* with transitive reduction.
167
*/
168
public Graph<String> requiresTransitiveGraph(Configuration cf,
169
Set<String> roots)
170
{
171
Deque<String> deque = new ArrayDeque<>(roots);
172
Set<String> visited = new HashSet<>();
173
Graph.Builder<String> builder = new Graph.Builder<>();
174
175
while (deque.peek() != null) {
176
String mn = deque.pop();
177
if (visited.contains(mn))
178
continue;
179
180
visited.add(mn);
181
builder.addNode(mn);
182
cf.findModule(mn).get()
183
.reference().descriptor().requires().stream()
184
.filter(d -> d.modifiers().contains(TRANSITIVE)
185
|| d.name().equals("java.base"))
186
.map(Requires::name)
187
.forEach(d -> {
188
deque.add(d);
189
builder.addEdge(mn, d);
190
});
191
}
192
193
return builder.build().reduce();
194
}
195
196
public interface Attributes {
197
static final String ORANGE = "#e76f00";
198
static final String BLUE = "#437291";
199
static final String BLACK = "#000000";
200
static final String DARK_GRAY = "#999999";
201
static final String LIGHT_GRAY = "#dddddd";
202
203
int fontSize();
204
String fontName();
205
String fontColor();
206
207
int arrowSize();
208
int arrowWidth();
209
String arrowColor();
210
211
default double rankSep() {
212
return 1;
213
}
214
215
default List<Set<String>> ranks() {
216
return Collections.emptyList();
217
}
218
219
default int weightOf(String s, String t) {
220
return 1;
221
}
222
223
default String requiresMandatedColor() {
224
return LIGHT_GRAY;
225
}
226
227
default String javaSubgraphColor() {
228
return ORANGE;
229
}
230
231
default String jdkSubgraphColor() {
232
return BLUE;
233
}
234
}
235
236
static class DotGraphAttributes implements Attributes {
237
static final DotGraphAttributes DEFAULT = new DotGraphAttributes();
238
239
static final String FONT_NAME = "DejaVuSans";
240
static final int FONT_SIZE = 12;
241
static final int ARROW_SIZE = 1;
242
static final int ARROW_WIDTH = 2;
243
244
@Override
245
public int fontSize() {
246
return FONT_SIZE;
247
}
248
249
@Override
250
public String fontName() {
251
return FONT_NAME;
252
}
253
254
@Override
255
public String fontColor() {
256
return BLACK;
257
}
258
259
@Override
260
public int arrowSize() {
261
return ARROW_SIZE;
262
}
263
264
@Override
265
public int arrowWidth() {
266
return ARROW_WIDTH;
267
}
268
269
@Override
270
public String arrowColor() {
271
return DARK_GRAY;
272
}
273
}
274
275
private static class DotGraphBuilder {
276
static final String REEXPORTS = "";
277
static final String REQUIRES = "style=\"dashed\"";
278
279
static final Set<String> JAVA_SE_SUBGRAPH = javaSE();
280
static final Set<String> JDK_SUBGRAPH = jdk();
281
282
private static Set<String> javaSE() {
283
String root = "java.se";
284
ModuleFinder system = ModuleFinder.ofSystem();
285
if (system.find(root).isPresent()) {
286
return Stream.concat(Stream.of(root),
287
Configuration.empty().resolve(system,
288
ModuleFinder.of(),
289
Set.of(root))
290
.findModule(root).get()
291
.reads().stream()
292
.map(ResolvedModule::name))
293
.collect(toSet());
294
} else {
295
// approximation
296
return system.findAll().stream()
297
.map(ModuleReference::descriptor)
298
.map(ModuleDescriptor::name)
299
.filter(name -> name.startsWith("java.") &&
300
!name.equals("java.smartcardio"))
301
.collect(Collectors.toSet());
302
}
303
}
304
305
private static Set<String> jdk() {
306
return ModuleFinder.ofSystem().findAll().stream()
307
.map(ModuleReference::descriptor)
308
.map(ModuleDescriptor::name)
309
.filter(name -> !JAVA_SE_SUBGRAPH.contains(name) &&
310
(name.startsWith("java.") || name.startsWith("jdk.")))
311
.collect(Collectors.toSet());
312
}
313
314
static class SubGraph {
315
final String name;
316
final String group;
317
final String color;
318
final Set<String> nodes;
319
SubGraph(String name, String group, String color, Set<String> nodes) {
320
this.name = Objects.requireNonNull(name);
321
this.group = Objects.requireNonNull(group);
322
this.color = Objects.requireNonNull(color);
323
this.nodes = Objects.requireNonNull(nodes);
324
}
325
}
326
327
private final String name;
328
private final Graph<String> graph;
329
private final Set<ModuleDescriptor> descriptors = new TreeSet<>();
330
private final List<SubGraph> subgraphs = new ArrayList<>();
331
private final Attributes attributes;
332
public DotGraphBuilder(String name,
333
Graph<String> graph,
334
Attributes attributes) {
335
this.name = name;
336
this.graph = graph;
337
this.attributes = attributes;
338
}
339
340
public DotGraphBuilder modules(Stream<ModuleDescriptor> descriptors) {
341
descriptors.forEach(this.descriptors::add);
342
return this;
343
}
344
345
public void build(Path filename) throws IOException {
346
try (BufferedWriter writer = Files.newBufferedWriter(filename);
347
PrintWriter out = new PrintWriter(writer)) {
348
349
out.format("digraph \"%s\" {%n", name);
350
out.format(" nodesep=.5;%n");
351
out.format((Locale)null, " ranksep=%f;%n", attributes.rankSep());
352
out.format(" pencolor=transparent;%n");
353
out.format(" node [shape=plaintext, fontcolor=\"%s\", fontname=\"%s\","
354
+ " fontsize=%d, margin=\".2,.2\"];%n",
355
attributes.fontColor(),
356
attributes.fontName(),
357
attributes.fontSize());
358
out.format(" edge [penwidth=%d, color=\"%s\", arrowhead=open, arrowsize=%d];%n",
359
attributes.arrowWidth(),
360
attributes.arrowColor(),
361
attributes.arrowSize());
362
363
// same RANKS
364
attributes.ranks().stream()
365
.map(nodes -> descriptors.stream()
366
.map(ModuleDescriptor::name)
367
.filter(nodes::contains)
368
.map(mn -> "\"" + mn + "\"")
369
.collect(joining(",")))
370
.filter(group -> group.length() > 0)
371
.forEach(group -> out.format(" {rank=same %s}%n", group));
372
373
subgraphs.forEach(subgraph -> {
374
out.format(" subgraph %s {%n", subgraph.name);
375
descriptors.stream()
376
.map(ModuleDescriptor::name)
377
.filter(subgraph.nodes::contains)
378
.forEach(mn -> printNode(out, mn, subgraph.color, subgraph.group));
379
out.format(" }%n");
380
});
381
382
descriptors.stream()
383
.filter(md -> graph.contains(md.name()) &&
384
!graph.adjacentNodes(md.name()).isEmpty())
385
.forEach(md -> printNode(out, md, graph.adjacentNodes(md.name())));
386
387
out.println("}");
388
}
389
}
390
391
public DotGraphBuilder subgraph(String name, String group, String color,
392
Set<String> nodes) {
393
subgraphs.add(new SubGraph(name, group, color, nodes));
394
return this;
395
}
396
397
public void printNode(PrintWriter out, String node, String color, String group) {
398
out.format(" \"%s\" [fontcolor=\"%s\", group=%s];%n",
399
node, color, group);
400
}
401
402
public void printNode(PrintWriter out, ModuleDescriptor md, Set<String> edges) {
403
Set<String> requiresTransitive = md.requires().stream()
404
.filter(d -> d.modifiers().contains(TRANSITIVE))
405
.map(d -> d.name())
406
.collect(toSet());
407
408
String mn = md.name();
409
edges.stream().forEach(dn -> {
410
String attr;
411
if (dn.equals("java.base")) {
412
attr = "color=\"" + attributes.requiresMandatedColor() + "\"";
413
} else {
414
attr = (requiresTransitive.contains(dn) ? REEXPORTS : REQUIRES);
415
}
416
417
int w = attributes.weightOf(mn, dn);
418
if (w > 1) {
419
if (!attr.isEmpty())
420
attr += ", ";
421
422
attr += "weight=" + w;
423
}
424
out.format(" \"%s\" -> \"%s\" [%s];%n", mn, dn, attr);
425
});
426
}
427
428
}
429
}
430
431