author | tschatzl |
Fri, 22 Nov 2019 10:03:38 +0100 | |
changeset 59220 | 72e15d757e6c |
parent 52993 | 2626982cf4f7 |
permissions | -rw-r--r-- |
36511 | 1 |
/* |
52993 | 2 |
* Copyright (c) 2014, 2018, Oracle and/or its affiliates. All rights reserved. |
36511 | 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 |
||
26 |
package build.tools.jigsaw; |
|
27 |
||
28 |
import java.io.IOException; |
|
29 |
import java.io.PrintStream; |
|
30 |
import java.lang.module.Configuration; |
|
31 |
import java.lang.module.ModuleDescriptor; |
|
32 |
import java.lang.module.ModuleFinder; |
|
33 |
import java.lang.module.ModuleReference; |
|
34 |
import java.lang.module.ResolvedModule; |
|
35 |
import java.nio.file.Files; |
|
36 |
import java.nio.file.Path; |
|
37 |
import java.nio.file.Paths; |
|
38 |
import java.util.Arrays; |
|
52993 | 39 |
import java.util.ArrayList; |
36511 | 40 |
import java.util.Comparator; |
41 |
import java.util.Date; |
|
42 |
import java.util.Enumeration; |
|
43 |
import java.util.HashMap; |
|
44 |
import java.util.HashSet; |
|
52993 | 45 |
import java.util.List; |
36511 | 46 |
import java.util.Map; |
47 |
import java.util.Set; |
|
48 |
import java.util.stream.Collectors; |
|
49 |
import java.util.zip.ZipEntry; |
|
50 |
import java.util.zip.ZipFile; |
|
51 |
import static java.lang.module.ModuleDescriptor.*; |
|
52 |
import static build.tools.jigsaw.ModuleSummary.HtmlDocument.Selector.*; |
|
53 |
import static build.tools.jigsaw.ModuleSummary.HtmlDocument.Division.*; |
|
54 |
||
55 |
public class ModuleSummary { |
|
40261
86a49ba76f52
8136930: Simplify use of module-system options by custom launchers
mchung
parents:
38457
diff
changeset
|
56 |
private static final String USAGE = "Usage: ModuleSummary --module-path <dir> -o <outfile> [--root mn]*"; |
36511 | 57 |
|
58 |
public static void main(String[] args) throws Exception { |
|
59 |
int i=0; |
|
60 |
Path modpath = null; |
|
61 |
Path outfile = null; |
|
62 |
Set<String> roots = new HashSet<>(); |
|
63 |
while (i < args.length && args[i].startsWith("-")) { |
|
64 |
String arg = args[i++]; |
|
65 |
switch (arg) { |
|
40261
86a49ba76f52
8136930: Simplify use of module-system options by custom launchers
mchung
parents:
38457
diff
changeset
|
66 |
case "--module-path": |
36511 | 67 |
modpath = Paths.get(args[i++]); |
68 |
break; |
|
69 |
case "-o": |
|
70 |
outfile = Paths.get(args[i++]); |
|
71 |
break; |
|
40261
86a49ba76f52
8136930: Simplify use of module-system options by custom launchers
mchung
parents:
38457
diff
changeset
|
72 |
case "--root": |
36511 | 73 |
roots.add(args[i++]); |
74 |
default: |
|
75 |
System.err.println(USAGE); |
|
76 |
System.exit(-1); |
|
77 |
} |
|
78 |
} |
|
79 |
if (outfile == null || modpath == null) { |
|
80 |
System.err.println(USAGE); |
|
81 |
System.exit(1); |
|
82 |
} |
|
83 |
Path dir = outfile.getParent() != null ? outfile.getParent() : Paths.get("."); |
|
84 |
Files.createDirectories(dir); |
|
85 |
||
86 |
Map<String, ModuleSummary> modules = new HashMap<>(); |
|
87 |
Set<ModuleReference> mrefs = ModuleFinder.ofSystem().findAll(); |
|
88 |
for (ModuleReference mref : mrefs) { |
|
89 |
String mn = mref.descriptor().name(); |
|
90 |
Path jmod = modpath.resolve(mn + ".jmod"); |
|
91 |
modules.put(mn, new ModuleSummary(mref, jmod)); |
|
92 |
} |
|
93 |
||
94 |
if (roots.isEmpty()) { |
|
95 |
roots.addAll(modules.keySet()); |
|
96 |
} |
|
97 |
genReport(outfile, modules, roots, "JDK Module Summary"); |
|
98 |
} |
|
99 |
||
100 |
static void genReport(Path outfile, Map<String, ModuleSummary> modules, Set<String> roots, String title) |
|
101 |
throws IOException |
|
102 |
{ |
|
103 |
Configuration cf = resolve(roots); |
|
104 |
try (PrintStream out = new PrintStream(Files.newOutputStream(outfile))) { |
|
105 |
HtmlDocument doc = new HtmlDocument(title, modules); |
|
106 |
Set<ModuleDescriptor> descriptors = cf.modules().stream() |
|
107 |
.map(ResolvedModule::reference) |
|
108 |
.map(ModuleReference::descriptor) |
|
109 |
.collect(Collectors.toSet()); |
|
110 |
doc.writeTo(out, descriptors); |
|
111 |
} |
|
112 |
} |
|
113 |
||
114 |
private final String name; |
|
115 |
private final ModuleDescriptor descriptor; |
|
116 |
private final JmodInfo jmodInfo; |
|
117 |
ModuleSummary(ModuleReference mref, Path jmod) throws IOException { |
|
118 |
this.name = mref.descriptor().name(); |
|
119 |
this.descriptor = mref.descriptor(); |
|
120 |
this.jmodInfo = new JmodInfo(jmod); |
|
121 |
} |
|
122 |
||
123 |
String name() { |
|
124 |
return name; |
|
125 |
} |
|
126 |
||
127 |
long uncompressedSize() { |
|
128 |
return jmodInfo.size; |
|
129 |
} |
|
130 |
||
131 |
long jmodFileSize() { |
|
132 |
return jmodInfo.filesize; // estimated compressed size |
|
133 |
} |
|
134 |
||
135 |
ModuleDescriptor descriptor() { |
|
136 |
return descriptor; |
|
137 |
} |
|
138 |
||
139 |
int numClasses() { |
|
140 |
return jmodInfo.classCount; |
|
141 |
} |
|
142 |
||
143 |
long classBytes() { |
|
144 |
return jmodInfo.classBytes; |
|
145 |
} |
|
146 |
||
147 |
int numResources() { |
|
148 |
return jmodInfo.resourceCount; |
|
149 |
} |
|
150 |
||
151 |
long resourceBytes() { |
|
152 |
return jmodInfo.resourceBytes; |
|
153 |
} |
|
154 |
||
155 |
int numConfigs() { |
|
156 |
return jmodInfo.configCount; |
|
157 |
} |
|
158 |
long configBytes() { |
|
159 |
return jmodInfo.configBytes; |
|
160 |
} |
|
161 |
int numCommands() { |
|
162 |
return jmodInfo.nativeCmds.size(); |
|
163 |
} |
|
164 |
||
165 |
long commandBytes() { |
|
166 |
return jmodInfo.nativeCmds.values().stream() |
|
167 |
.mapToLong(l -> l.longValue()).sum() - jmodInfo.debugInfoCmdBytes; |
|
168 |
} |
|
169 |
int numCommandsDebug() { |
|
170 |
return jmodInfo.debugInfoCmdCount; |
|
171 |
} |
|
172 |
long commandDebugBytes() { |
|
173 |
return jmodInfo.debugInfoCmdBytes; |
|
174 |
} |
|
175 |
int numNativeLibraries() { |
|
176 |
return jmodInfo.nativeLibs.size(); |
|
177 |
} |
|
178 |
||
179 |
long nativeLibrariesBytes() { |
|
180 |
return jmodInfo.nativeLibs.values().stream() |
|
181 |
.mapToLong(l -> l.longValue()).sum() - jmodInfo.debugInfoLibBytes; |
|
182 |
} |
|
183 |
int numNativeLibrariesDebug() { |
|
184 |
return jmodInfo.debugInfoLibCount; |
|
185 |
} |
|
186 |
||
187 |
long nativeLibrariesDebugBytes() { |
|
188 |
return jmodInfo.debugInfoLibBytes; |
|
189 |
} |
|
190 |
||
191 |
Map<String,Long> commands() { |
|
192 |
return jmodInfo.nativeCmds; |
|
193 |
} |
|
194 |
||
195 |
Map<String,Long> nativeLibs() { |
|
196 |
return jmodInfo.nativeLibs; |
|
197 |
} |
|
198 |
||
199 |
Map<String,Long> configFiles() { |
|
200 |
return jmodInfo.configFiles; |
|
201 |
} |
|
202 |
||
203 |
||
204 |
static class JmodInfo { |
|
205 |
final long size; |
|
206 |
final long filesize; |
|
207 |
final int classCount; |
|
208 |
final long classBytes; |
|
209 |
final int resourceCount; |
|
210 |
final long resourceBytes; |
|
211 |
final int configCount; |
|
212 |
final long configBytes; |
|
213 |
final int debugInfoLibCount; |
|
214 |
final long debugInfoLibBytes; |
|
215 |
final int debugInfoCmdCount; |
|
216 |
final long debugInfoCmdBytes; |
|
217 |
final Map<String,Long> configFiles = new HashMap<>(); |
|
218 |
final Map<String,Long> nativeCmds = new HashMap<>(); |
|
219 |
final Map<String,Long> nativeLibs = new HashMap<>(); |
|
220 |
||
221 |
JmodInfo(Path jmod) throws IOException { |
|
222 |
long total = 0; |
|
223 |
long cBytes = 0, rBytes = 0, cfBytes = 0, dizLibBytes = 0, dizCmdBytes = 0; |
|
224 |
int cCount = 0, rCount = 0, cfCount = 0, dizLibCount = 0, dizCmdCount = 0; |
|
225 |
try (ZipFile zf = new ZipFile(jmod.toFile())) { |
|
226 |
for (Enumeration<? extends ZipEntry> e = zf.entries(); e.hasMoreElements(); ) { |
|
227 |
ZipEntry ze = e.nextElement(); |
|
228 |
String fn = ze.getName(); |
|
229 |
int pos = fn.indexOf('/'); |
|
230 |
String dir = fn.substring(0, pos); |
|
231 |
String filename = fn.substring(fn.lastIndexOf('/') + 1); |
|
232 |
// name shown in the column |
|
233 |
String name = filename; |
|
234 |
||
235 |
long len = ze.getSize(); |
|
236 |
total += len; |
|
237 |
switch (dir) { |
|
238 |
case NATIVE_LIBS: |
|
239 |
nativeLibs.put(name, len); |
|
240 |
if (filename.endsWith(".diz")) { |
|
241 |
dizLibCount++; |
|
242 |
dizLibBytes += len; |
|
243 |
} |
|
244 |
break; |
|
245 |
case NATIVE_CMDS: |
|
246 |
nativeCmds.put(name, len); |
|
247 |
if (filename.endsWith(".diz")) { |
|
248 |
dizCmdCount++; |
|
249 |
dizCmdBytes += len; |
|
250 |
} |
|
251 |
break; |
|
252 |
case CLASSES: |
|
253 |
if (filename.endsWith(".class")) { |
|
254 |
cCount++; |
|
255 |
cBytes += len; |
|
256 |
} else { |
|
257 |
rCount++; |
|
258 |
rBytes += len; |
|
259 |
} |
|
260 |
break; |
|
261 |
case CONFIG: |
|
262 |
configFiles.put(name, len); |
|
263 |
cfCount++; |
|
264 |
cfBytes += len; |
|
265 |
break; |
|
266 |
default: |
|
267 |
break; |
|
268 |
} |
|
269 |
} |
|
270 |
this.filesize = jmod.toFile().length(); |
|
271 |
this.classCount = cCount; |
|
272 |
this.classBytes = cBytes; |
|
273 |
this.resourceCount = rCount; |
|
274 |
this.resourceBytes = rBytes; |
|
275 |
this.configCount = cfCount; |
|
276 |
this.configBytes = cfBytes; |
|
277 |
this.size = total; |
|
278 |
this.debugInfoLibCount = dizLibCount; |
|
279 |
this.debugInfoLibBytes = dizLibBytes; |
|
280 |
this.debugInfoCmdCount = dizCmdCount; |
|
281 |
this.debugInfoCmdBytes = dizCmdBytes; |
|
282 |
} |
|
283 |
} |
|
284 |
||
285 |
static final String NATIVE_LIBS = "native"; |
|
286 |
static final String NATIVE_CMDS = "bin"; |
|
287 |
static final String CLASSES = "classes"; |
|
288 |
static final String CONFIG = "conf"; |
|
289 |
||
290 |
static final String MODULE_ID = "module/id"; |
|
291 |
static final String MODULE_MAIN_CLASS = "module/main-class"; |
|
292 |
} |
|
293 |
||
294 |
static Configuration resolve(Set<String> roots) { |
|
295 |
return Configuration.empty() |
|
43712
5dfd0950317c
8173393: Module system implementation refresh (2/2017)
alanb
parents:
42338
diff
changeset
|
296 |
.resolve(ModuleFinder.ofSystem(), |
5dfd0950317c
8173393: Module system implementation refresh (2/2017)
alanb
parents:
42338
diff
changeset
|
297 |
ModuleFinder.of(), |
5dfd0950317c
8173393: Module system implementation refresh (2/2017)
alanb
parents:
42338
diff
changeset
|
298 |
roots); |
36511 | 299 |
} |
300 |
||
301 |
static class HtmlDocument { |
|
302 |
final String title; |
|
303 |
final Map<String, ModuleSummary> modules; |
|
42338
a60f280f803c
8169069: Module system implementation refresh (11/2016)
alanb
parents:
40261
diff
changeset
|
304 |
boolean requiresTransitiveNote = false; |
36511 | 305 |
boolean aggregatorNote = false; |
306 |
boolean totalBytesNote = false; |
|
307 |
HtmlDocument(String title, Map<String, ModuleSummary> modules) { |
|
308 |
this.title = title; |
|
309 |
this.modules = modules; |
|
310 |
} |
|
311 |
||
312 |
void writeTo(PrintStream out, Set<ModuleDescriptor> selectedModules) { |
|
313 |
out.format("<html><head>%n"); |
|
314 |
out.format("<title>%s</title>%n", title); |
|
315 |
// stylesheet |
|
316 |
Arrays.stream(HtmlDocument.STYLES).forEach(out::println); |
|
317 |
out.format("</head>%n"); |
|
318 |
||
319 |
// body begins |
|
320 |
out.format("<body>%n"); |
|
321 |
||
322 |
// title and date |
|
323 |
out.println(DOCTITLE.toString(title)); |
|
324 |
out.println(VERSION.toString(String.format("%tc", new Date()))); |
|
325 |
||
326 |
// total modules and sizes |
|
327 |
long totalBytes = selectedModules.stream() |
|
328 |
.map(ModuleDescriptor::name) |
|
329 |
.map(modules::get) |
|
330 |
.mapToLong(ModuleSummary::uncompressedSize) |
|
331 |
.sum(); |
|
332 |
String[] sections = new String[] { |
|
333 |
String.format("%s: %d", "Total modules", selectedModules.size()), |
|
334 |
String.format("%s: %,d bytes (%s %s)", "Total size", |
|
335 |
totalBytes, |
|
336 |
System.getProperty("os.name"), |
|
337 |
System.getProperty("os.arch")) |
|
338 |
}; |
|
339 |
out.println(SECTION.toString(sections)); |
|
340 |
||
341 |
// write table and header |
|
342 |
out.println(String.format("<table class=\"%s\">", MODULES)); |
|
343 |
out.println(header("Module", "Requires", "Exports", |
|
344 |
"Services", "Commands/Native Libraries/Configs")); |
|
345 |
||
346 |
// write contents - one row per module |
|
347 |
selectedModules.stream() |
|
348 |
.sorted(Comparator.comparing(ModuleDescriptor::name)) |
|
349 |
.map(m -> modules.get(m.name())) |
|
350 |
.map(ModuleTableRow::new) |
|
351 |
.forEach(table -> table.writeTo(out)); |
|
352 |
||
353 |
out.format("</table>"); // end table |
|
354 |
out.format("</body>"); |
|
355 |
out.println("</html>"); |
|
356 |
} |
|
357 |
||
358 |
String header(String... columns) { |
|
359 |
StringBuilder sb = new StringBuilder(); |
|
360 |
sb.append("<tr>"); |
|
361 |
Arrays.stream(columns) |
|
362 |
.forEach(cn -> sb.append(" <th>").append(cn).append("</th>").append("\n")); |
|
363 |
sb.append("</tr>"); |
|
364 |
return sb.toString(); |
|
365 |
} |
|
366 |
||
367 |
static enum Selector { |
|
368 |
MODULES("modules"), |
|
369 |
MODULE("module"), |
|
370 |
MODULE_DEF("code name def"), |
|
371 |
AGGREGATOR("code name def agg"), |
|
372 |
REQUIRES("code"), |
|
373 |
REQUIRES_PUBLIC("code reexp"), |
|
374 |
BR("br"), |
|
375 |
CODE("code"), |
|
376 |
NUMBER("number"),; |
|
377 |
final String name; |
|
378 |
Selector(String name) { |
|
379 |
this.name = name; |
|
380 |
} |
|
381 |
@Override |
|
382 |
public String toString() { |
|
383 |
return name; |
|
384 |
} |
|
385 |
} |
|
386 |
||
387 |
static enum Division { |
|
388 |
DOCTITLE("doctitle"), |
|
389 |
VERSION("versions"), |
|
390 |
SECTION("section"); |
|
391 |
final String name; |
|
392 |
||
393 |
Division(String name) { |
|
394 |
this.name = name; |
|
395 |
} |
|
396 |
||
397 |
public String toString(String... lines) { |
|
398 |
String value = Arrays.stream(lines).collect(Collectors.joining("<br>\n")); |
|
399 |
return "<div class=\"" + name + "\">" + value + "</div>"; |
|
400 |
} |
|
401 |
} |
|
402 |
||
403 |
class ModuleTableRow { |
|
404 |
private final ModuleSummary ms; |
|
405 |
private final Set<ModuleDescriptor> deps; |
|
406 |
private final int maxRows; |
|
407 |
private final boolean aggregator; |
|
408 |
ModuleTableRow(ModuleSummary ms) { |
|
409 |
this.ms = ms; |
|
410 |
Configuration cf = resolve(Set.of(ms.name())); |
|
411 |
this.deps = cf.modules().stream() |
|
412 |
.map(ResolvedModule::reference) |
|
413 |
.map(ModuleReference::descriptor) |
|
414 |
.collect(Collectors.toSet()); |
|
415 |
int count = (ms.numClasses() > 0 ? 1 : 0) + |
|
416 |
(ms.numResources() > 0 ? 1 : 0) + |
|
417 |
(ms.numConfigs() > 0 ? 1 : 0) + |
|
418 |
(ms.numNativeLibraries() > 0 ? 1 : 0) + |
|
419 |
(ms.numNativeLibrariesDebug() > 0 ? 1 : 0) + |
|
420 |
(ms.numCommands() > 0 ? 1 : 0) + |
|
421 |
(ms.numCommandsDebug() > 0 ? 1 : 0); |
|
422 |
this.aggregator = ms.numClasses() == 1 && count == 1; // only module-info.class |
|
423 |
||
424 |
// 5 fixed rows (name + 2 transitive count/size + 2 blank rows) |
|
425 |
this.maxRows = 5 + count + (aggregator && !aggregatorNote ? 2 : 0); |
|
426 |
} |
|
427 |
||
428 |
public void writeTo(PrintStream out) { |
|
429 |
out.println(String.format("<tr id=\"%s\" class=\"%s\">", ms.name(), MODULE)); |
|
430 |
out.println(moduleColumn()); |
|
431 |
out.println(requiresColumn()); |
|
432 |
out.println(exportsColumn()); |
|
433 |
out.println(servicesColumn()); |
|
434 |
out.println(otherSectionColumn()); |
|
435 |
out.println("</td>"); |
|
436 |
out.println("</tr>"); |
|
437 |
} |
|
438 |
||
439 |
public String moduleColumn() { |
|
440 |
// module name |
|
441 |
StringBuilder sb = new StringBuilder(" "); |
|
442 |
sb.append("<td>"); |
|
443 |
sb.append(String.format("<table class=\"%s\">", MODULE)).append("\n"); |
|
444 |
sb.append(moduleName(ms.name())); |
|
445 |
sb.append(blankRow()); |
|
446 |
// metadata |
|
447 |
sb.append(toTableRow("class", "classes", ms.numClasses(), ms.classBytes())); |
|
448 |
sb.append(toTableRow("resource", "resources", ms.numResources(), ms.resourceBytes())); |
|
449 |
sb.append(toTableRow("config", "configs", ms.numConfigs(), ms.configBytes())); |
|
450 |
sb.append(toTableRow("native library", "native libraries", |
|
451 |
ms.numNativeLibraries(), ms.nativeLibrariesBytes())); |
|
452 |
sb.append(toTableRow("native library debug", "native libraries debug", |
|
453 |
ms.numNativeLibrariesDebug(), ms.nativeLibrariesDebugBytes())); |
|
454 |
sb.append(toTableRow("command", "commands", ms.numCommands(), ms.commandBytes())); |
|
455 |
sb.append(toTableRow("command debug", "commands debug", |
|
456 |
ms.numCommandsDebug(), ms.commandDebugBytes())); |
|
457 |
sb.append(blankRow()); |
|
458 |
||
459 |
// transitive dependencies |
|
460 |
long reqBytes = deps.stream() |
|
461 |
.filter(d -> !d.name().equals(ms.name())) |
|
462 |
.mapToLong(d -> modules.get(d.name()).uncompressedSize()) |
|
463 |
.sum(); |
|
464 |
long reqJmodFileSize = deps.stream() |
|
465 |
.mapToLong(d -> modules.get(d.name()).jmodFileSize()) |
|
466 |
.sum(); |
|
467 |
// size |
|
468 |
if (totalBytesNote) { |
|
469 |
sb.append(toTableRow("Total bytes", ms.uncompressedSize())); |
|
470 |
sb.append(toTableRow("Total bytes of dependencies", reqBytes)); |
|
471 |
} else { |
|
472 |
// print footnote |
|
473 |
sb.append(toTableRow("Total bytes<sup>1</sup>", ms.uncompressedSize())); |
|
474 |
sb.append(toTableRow("Total bytes of dependencies<sup>2</sup>", reqBytes)); |
|
475 |
} |
|
476 |
String files = deps.size() == 1 ? "file" : "files"; |
|
477 |
sb.append(toTableRow(String.format("Total jmod bytes (%d %s)", deps.size(), files), reqJmodFileSize)); |
|
478 |
||
479 |
if (aggregator && !aggregatorNote) { |
|
480 |
aggregatorNote = true; |
|
481 |
sb.append(blankRow()); |
|
482 |
sb.append(toTableRow("<i>* aggregator is a module with module-info.class only</i>", BR)); |
|
483 |
} |
|
484 |
if (!totalBytesNote) { |
|
485 |
totalBytesNote = true; |
|
486 |
sb.append(blankRow()); |
|
487 |
sb.append(toTableRow("<i><sup>1</sup>sum of all files including debug files</i>", BR)); |
|
488 |
sb.append(toTableRow("<i><sup>2</sup>sum of direct and indirect dependencies</i>", BR)); |
|
489 |
} |
|
490 |
sb.append("</table>").append("</td>"); |
|
491 |
return sb.toString(); |
|
492 |
} |
|
493 |
||
494 |
private String moduleName(String mn) { |
|
495 |
if (aggregator) { |
|
496 |
StringBuilder sb = new StringBuilder(); |
|
497 |
sb.append(String.format("<tr><td colspan=\"2\"><span class=\"%s\">", AGGREGATOR)) |
|
498 |
.append(mn) |
|
499 |
.append("</span>").append(" "); |
|
500 |
if (!aggregatorNote) { |
|
501 |
sb.append("(aggregator<sup>*</sup>)"); |
|
502 |
} else { |
|
503 |
sb.append("(aggregator)"); |
|
504 |
} |
|
505 |
sb.append("</td></tr>"); |
|
506 |
return sb.toString(); |
|
507 |
} else { |
|
508 |
return toTableRow(mn, MODULE_DEF); |
|
509 |
} |
|
510 |
} |
|
511 |
||
512 |
public String requiresColumn() { |
|
513 |
StringBuilder sb = new StringBuilder(); |
|
514 |
sb.append(String.format("<td>")); |
|
42338
a60f280f803c
8169069: Module system implementation refresh (11/2016)
alanb
parents:
40261
diff
changeset
|
515 |
boolean footnote = requiresTransitiveNote; |
36511 | 516 |
ms.descriptor().requires().stream() |
517 |
.sorted(Comparator.comparing(Requires::name)) |
|
518 |
.forEach(r -> { |
|
42338
a60f280f803c
8169069: Module system implementation refresh (11/2016)
alanb
parents:
40261
diff
changeset
|
519 |
boolean requiresTransitive = r.modifiers().contains(Requires.Modifier.TRANSITIVE); |
a60f280f803c
8169069: Module system implementation refresh (11/2016)
alanb
parents:
40261
diff
changeset
|
520 |
Selector sel = requiresTransitive ? REQUIRES_PUBLIC : REQUIRES; |
36511 | 521 |
String req = String.format("<a class=\"%s\" href=\"#%s\">%s</a>", |
522 |
sel, r.name(), r.name()); |
|
42338
a60f280f803c
8169069: Module system implementation refresh (11/2016)
alanb
parents:
40261
diff
changeset
|
523 |
if (!requiresTransitiveNote && requiresTransitive) { |
a60f280f803c
8169069: Module system implementation refresh (11/2016)
alanb
parents:
40261
diff
changeset
|
524 |
requiresTransitiveNote = true; |
36511 | 525 |
req += "<sup>*</sup>"; |
526 |
} |
|
527 |
sb.append(req).append("\n").append("<br>"); |
|
528 |
}); |
|
529 |
||
530 |
if (!ms.name().equals("java.base")) { |
|
531 |
int directDeps = ms.descriptor().requires().size(); |
|
532 |
int indirectDeps = deps.size()-directDeps-1; |
|
533 |
for (int i=directDeps; i< (maxRows-1); i++) { |
|
534 |
sb.append("<br>"); |
|
535 |
} |
|
536 |
sb.append("<br>"); |
|
537 |
sb.append("<i>+").append(indirectDeps).append(" transitive dependencies</i>"); |
|
538 |
} |
|
42338
a60f280f803c
8169069: Module system implementation refresh (11/2016)
alanb
parents:
40261
diff
changeset
|
539 |
if (footnote != requiresTransitiveNote) { |
a60f280f803c
8169069: Module system implementation refresh (11/2016)
alanb
parents:
40261
diff
changeset
|
540 |
sb.append("<br><br>").append("<i>* bold denotes requires transitive</i>"); |
36511 | 541 |
} |
542 |
sb.append("</td>"); |
|
543 |
return sb.toString(); |
|
544 |
} |
|
545 |
||
546 |
public String exportsColumn() { |
|
547 |
StringBuilder sb = new StringBuilder(); |
|
548 |
sb.append(String.format(" <td class=\"%s\">", CODE)); |
|
549 |
ms.descriptor().exports().stream() |
|
550 |
.sorted(Comparator.comparing(Exports::source)) |
|
551 |
.filter(e -> !e.isQualified()) |
|
552 |
.forEach(e -> sb.append(e.source()).append("<br>").append("\n")); |
|
553 |
sb.append("</td>"); |
|
554 |
return sb.toString(); |
|
555 |
} |
|
556 |
||
52993 | 557 |
private String providesEntry(Provides p) { |
558 |
StringBuilder sb = new StringBuilder(); |
|
559 |
sb.append(String.format("provides %s<br>\n", p.service())); |
|
560 |
List<String> pvs = new ArrayList<>(p.providers()); |
|
561 |
pvs.sort(Comparator.naturalOrder()); |
|
562 |
for (int i = 0; i < pvs.size(); i++) { // My kingdom for Stream::zip ... |
|
563 |
String fmt = ((i == 0) |
|
564 |
? " with %s" |
|
565 |
: ",<br> %s"); |
|
566 |
sb.append(String.format(fmt, pvs.get(i))); |
|
567 |
} |
|
568 |
sb.append("\n"); |
|
569 |
return sb.toString(); |
|
570 |
} |
|
571 |
||
36511 | 572 |
public String servicesColumn() { |
573 |
StringBuilder sb = new StringBuilder(); |
|
574 |
sb.append(String.format(" <td class=\"%s\">", CODE)); |
|
575 |
ms.descriptor().uses().stream() |
|
576 |
.sorted() |
|
577 |
.forEach(s -> sb.append("uses ").append(s).append("<br>").append("\n")); |
|
42338
a60f280f803c
8169069: Module system implementation refresh (11/2016)
alanb
parents:
40261
diff
changeset
|
578 |
ms.descriptor().provides().stream() |
a60f280f803c
8169069: Module system implementation refresh (11/2016)
alanb
parents:
40261
diff
changeset
|
579 |
.sorted(Comparator.comparing(Provides::service)) |
52993 | 580 |
.map(this::providesEntry) |
36511 | 581 |
.forEach(p -> sb.append(p).append("<br>").append("\n")); |
582 |
sb.append("</td>"); |
|
583 |
return sb.toString(); |
|
584 |
} |
|
585 |
||
586 |
public String otherSectionColumn() { |
|
587 |
StringBuilder sb = new StringBuilder(); |
|
588 |
sb.append("<td>"); |
|
589 |
sb.append(String.format("<table class=\"%s\">", MODULE)).append("\n"); |
|
590 |
// commands |
|
591 |
if (ms.numCommands() > 0) { |
|
592 |
sb.append(toTableRow("bin/", CODE)); |
|
593 |
ms.commands().entrySet().stream() |
|
594 |
.sorted(Map.Entry.comparingByKey()) |
|
595 |
.forEach(e -> sb.append(toTableRow(e.getKey(), e.getValue(), CODE))); |
|
596 |
sb.append(blankRow()); |
|
597 |
} |
|
598 |
||
599 |
// native libraries |
|
600 |
if (ms.numNativeLibraries() > 0) { |
|
601 |
sb.append(toTableRow("lib/", CODE)); |
|
602 |
ms.nativeLibs().entrySet().stream() |
|
603 |
.sorted(Map.Entry.comparingByKey()) |
|
604 |
.forEach(e -> sb.append(toTableRow(e.getKey(), e.getValue(), CODE))); |
|
605 |
sb.append(blankRow()); |
|
606 |
} |
|
607 |
||
608 |
// config files |
|
609 |
if (ms.numConfigs() > 0) { |
|
610 |
sb.append(toTableRow("conf/", CODE)); |
|
611 |
ms.configFiles().entrySet().stream() |
|
612 |
.sorted(Map.Entry.comparingByKey()) |
|
613 |
.forEach(e -> sb.append(toTableRow(e.getKey(), e.getValue(), CODE))); |
|
614 |
} |
|
615 |
// totals |
|
616 |
sb.append("</table>").append("</td>"); |
|
617 |
return sb.toString(); |
|
618 |
} |
|
619 |
||
620 |
private String blankRow() { |
|
621 |
return toTableRow(" ", BR); |
|
622 |
} |
|
623 |
||
624 |
private String toTableRow(String col, Selector selector) { |
|
625 |
TableDataBuilder builder = new TableDataBuilder(); |
|
626 |
builder.colspan(selector, 2, col); |
|
627 |
return builder.build(); |
|
628 |
} |
|
629 |
||
630 |
private String toTableRow(String col1, long col2) { |
|
631 |
return toTableRow(col1, col2, BR); |
|
632 |
} |
|
633 |
||
634 |
private String toTableRow(String col1, long col2, Selector selector) { |
|
635 |
TableDataBuilder builder = new TableDataBuilder(); |
|
636 |
builder.data(selector, col1); |
|
637 |
builder.data(col2); |
|
638 |
return builder.build(); |
|
639 |
||
640 |
} |
|
641 |
||
642 |
private String toTableRow(String singular, String plural, int count, long bytes) { |
|
643 |
if (count == 0) { |
|
644 |
return ""; |
|
645 |
} |
|
646 |
TableDataBuilder builder = new TableDataBuilder(); |
|
647 |
if (count == 1) { |
|
648 |
builder.data(count + " " + singular); |
|
649 |
} else { |
|
650 |
builder.data(count + " " + plural); |
|
651 |
} |
|
652 |
builder.data(bytes); |
|
653 |
return builder.build(); |
|
654 |
} |
|
655 |
||
656 |
class TableDataBuilder { |
|
657 |
private final StringBuilder sb; |
|
658 |
TableDataBuilder() { |
|
659 |
this.sb = new StringBuilder("<tr>"); |
|
660 |
} |
|
661 |
TableDataBuilder data(String s) { |
|
662 |
data(BR, s); |
|
663 |
return this; |
|
664 |
} |
|
665 |
TableDataBuilder data(long num) { |
|
666 |
data(NUMBER, String.format("%,d", num)); |
|
667 |
return this; |
|
668 |
} |
|
669 |
TableDataBuilder colspan(Selector selector, int columns, String data) { |
|
670 |
sb.append("<td colspan=\"").append(columns).append("\">"); |
|
671 |
sb.append("<span class=\"").append(selector).append("\">"); |
|
672 |
sb.append(data).append("</span></td>"); |
|
673 |
return this; |
|
674 |
} |
|
675 |
||
676 |
TableDataBuilder data(Selector selector, String data) { |
|
677 |
sb.append("<td class=\"").append(selector).append("\">"); |
|
678 |
sb.append(data).append("</td>"); |
|
679 |
return this; |
|
680 |
} |
|
681 |
String build() { |
|
682 |
sb.append("</tr>"); |
|
683 |
return sb.toString(); |
|
684 |
} |
|
685 |
} |
|
686 |
} |
|
687 |
||
688 |
private static final String[] STYLES = new String[]{ |
|
689 |
"<link rel=\"stylesheet\" type=\"text/css\" href=\"/.fonts/dejavu.css\"/>", |
|
690 |
"<style type=\"text/css\">", |
|
691 |
" HTML, BODY, DIV, SPAN, APPLET, OBJECT, IFRAME, H1, H2, H3, H4, H5, H6, P,", |
|
692 |
" BLOCKQUOTE, PRE, A, ABBR, ACRONYM, ADDRESS, BIG, CITE, CODE, DEL, DFN, EM,", |
|
693 |
" IMG, INS, KBD, Q, S, SAMP, SMALL, STRIKE, STRONG, SUB, SUP, TT, VAR, B, U,", |
|
694 |
" I, CENTER, DL, DT, DD, OL, UL, LI, FIELDSET, FORM, LABEL, LEGEND, TABLE,", |
|
695 |
" CAPTION, TBODY, TFOOT, THEAD, TR, TH, TD, ARTICLE, ASIDE, CANVAS, DETAILS,", |
|
696 |
" EMBED, FIGURE, FIGCAPTION, FOOTER, HEADER, HGROUP, MENU, NAV, OUTPUT, RUBY,", |
|
697 |
" SECTION, SUMMARY, TIME, MARK, AUDIO, VIDEO {", |
|
698 |
" margin: 0; padding: 0; border: 0; font-size: 100%; font: inherit;", |
|
699 |
" vertical-align: baseline; }", |
|
700 |
" ARTICLE, ASIDE, DETAILS, FIGCAPTION, FIGURE, ", |
|
701 |
" FOOTER, HEADER, HGROUP, MENU, NAV, SECTION { display: block; }", |
|
702 |
" BLOCKQUOTE, Q { quotes: none; }", |
|
703 |
" BLOCKQUOTE:before, BLOCKQUOTE:after, Q:before, Q:after {", |
|
704 |
" content: ''; content: none; }", |
|
705 |
" TABLE { border-collapse: collapse; border-spacing: 0; }", |
|
706 |
" A { text-decoration: none; }", |
|
707 |
" A:link { color: #437291; }", |
|
708 |
" A:visited { color: #666666; }", |
|
709 |
" A.anchor:link, A.anchor:visited { color: black; }", |
|
710 |
" A[href]:hover { color: #e76f00; }", |
|
711 |
" A IMG { border-width: 0px; }", |
|
712 |
" HTML { font-size: 20px; } /* baseline grid */", |
|
713 |
" HTML > BODY { font-size: 14px; }", |
|
714 |
" BODY {", |
|
715 |
" background: white;", |
|
716 |
" margin: 40px;", |
|
717 |
" margin-bottom: 150%;", |
|
718 |
" line-height: 20px;", |
|
719 |
" -webkit-text-size-adjust: 100%; /* iOS */", |
|
720 |
" color: #222;", |
|
721 |
" }", |
|
722 |
" BODY { font-family: \"DejaVu Serif\", \"Lucida Bright\", \"Bookman Old Style\",", |
|
723 |
" Georgia, serif; }", |
|
724 |
" CODE, TT, .jref, DIV.spec .open, TABLE.profiles {", |
|
725 |
" font-family: \"DejaVu Sans\", \"Lucida Sans\", Helvetica, sans-serif; }", |
|
726 |
" PRE, .code { font-family: \"DejaVu Sans Mono\", \"Bitstream Vera Sans Mono\",", |
|
727 |
" Monaco, \"Courier New\", monospace; }", |
|
728 |
" H1, H2, H3, H4 { color: green; font-weight: bold; }", |
|
729 |
" I { font-style: italic; }", |
|
730 |
" TH { font-weight: bold; }", |
|
731 |
" P { text-indent: 40px; }", |
|
732 |
" P:first-child, UL + P, OL + P, BLOCKQUOTE + P, TABLE + P, P.subsection,", |
|
733 |
" P.break, DIV.profiles-table + P { text-indent: 0; }", |
|
734 |
" P.break { margin-top: 10px; }", |
|
735 |
" P.subsection { margin-top: 20px; }", |
|
736 |
" P.subsection SPAN.title { font-weight: bold; padding-right: 20px; }", |
|
737 |
" UL, OL { margin: 10px 0; padding-left: 40px; }", |
|
738 |
" LI { margin-bottom: 10px; }", |
|
739 |
" UL.compact LI { margin-bottom: 0; }", |
|
740 |
" PRE { padding: 0; margin: 10px 0 10px 20px; background: #eee; width: 45em; }", |
|
741 |
" BLOCKQUOTE { margin: 10px 0; margin-left: 20px; }", |
|
742 |
" LI BLOCKQUOTE { margin-left: 0; }", |
|
743 |
" UL LI { list-style-type: square; }", |
|
744 |
" .todo { color: darkred; text-align: right; }", |
|
745 |
" .error { color: red; font-weight: bold; }", |
|
746 |
" .warn { color: #ee0000; font-weight: bold; }", |
|
747 |
" DIV.doctitle { margin-top: -13px;", |
|
748 |
" font-size: 22px; line-height: 40px; font-weight: bold; }", |
|
749 |
" DIV.twarn { color: #cc0000; font-weight: bold; margin-bottom: 9px; }", |
|
750 |
" DIV.subtitle { margin-top: 2px; font-size: 18px; font-weight: bold; }", |
|
751 |
" DIV.authors { margin-top: 10px; margin-bottom: 10px; font-size: 16px; }", |
|
752 |
" DIV.author A { font-style: italic; }", |
|
753 |
" DIV.version { margin-top: 10px; font-size: 12px; }", |
|
754 |
" DIV.version, DIV.legal-notice { font-size: 12px; line-height: 15px; }", |
|
755 |
" SPAN.hash { font-size: 9px; }", |
|
756 |
" DIV.version SPAN.modified { color: green; font-weight: bold; }", |
|
757 |
" DIV.head { margin-bottom: 20px; }", |
|
758 |
" DIV.section > DIV.title, DIV.section DIV.number SPAN {", |
|
759 |
" font-size: 15px; font-weight: bold; }", |
|
760 |
" TABLE { border-collapse: collapse; border: none; }", |
|
761 |
" TD.number { text-align: right; }", |
|
762 |
" TD, TH { text-align: left; white-space: nowrap; }", |
|
763 |
" TD.name, SPAN.name { font-weight: bold; }", |
|
764 |
" ", |
|
765 |
" TABLE.module { width: 100%; }", |
|
766 |
" TABLE.module TD:first-child { padding-right: 10px; }", |
|
767 |
" TR.module > TD { padding: 10px 0; border-top: 1px solid black; }", |
|
768 |
" TR > TH { padding-bottom: 10px; }", |
|
769 |
" TR.br TD { padding-top: 20px; }", |
|
770 |
" TABLE.modules { margin-top: 20px; }", |
|
771 |
" TABLE.modules > TBODY > TR > TD:nth-child(even) { background: #eee; }", |
|
772 |
" TABLE.modules > TBODY > TR > TD, TABLE.modules > TBODY > TR > TH {", |
|
773 |
" padding-left: 10px; padding-right: 10px; }", |
|
774 |
" .reexp, .def { font-weight: bold; }", |
|
775 |
" .agg { font-style: italic; }", |
|
776 |
" SUP { height: 0; line-height: 1; position: relative;", |
|
777 |
" vertical-align: baseline; bottom: 1ex; font-size: 11px; }", |
|
778 |
"</style>", |
|
779 |
}; |
|
780 |
} |
|
781 |
} |