Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
PojavLauncherTeam
GitHub Repository: PojavLauncherTeam/openjdk-multiarch-jdk8u
Path: blob/aarch64-shenandoah-jdk8u272-b10/langtools/src/share/classes/com/sun/tools/jdeps/JdepsTask.java
38899 views
1
/*
2
* Copyright (c) 2012, 2017, 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 com.sun.tools.classfile.AccessFlags;
28
import com.sun.tools.classfile.ClassFile;
29
import com.sun.tools.classfile.ConstantPoolException;
30
import com.sun.tools.classfile.Dependencies;
31
import com.sun.tools.classfile.Dependencies.ClassFileError;
32
import com.sun.tools.classfile.Dependency;
33
import com.sun.tools.classfile.Dependency.Location;
34
import com.sun.tools.jdeps.PlatformClassPath.JDKArchive;
35
import static com.sun.tools.jdeps.Analyzer.Type.*;
36
import java.io.*;
37
import java.nio.file.DirectoryStream;
38
import java.nio.file.Files;
39
import java.nio.file.Path;
40
import java.nio.file.Paths;
41
import java.text.MessageFormat;
42
import java.util.*;
43
import java.util.regex.Pattern;
44
45
/**
46
* Implementation for the jdeps tool for static class dependency analysis.
47
*/
48
class JdepsTask {
49
static class BadArgs extends Exception {
50
static final long serialVersionUID = 8765093759964640721L;
51
BadArgs(String key, Object... args) {
52
super(JdepsTask.getMessage(key, args));
53
this.key = key;
54
this.args = args;
55
}
56
57
BadArgs showUsage(boolean b) {
58
showUsage = b;
59
return this;
60
}
61
final String key;
62
final Object[] args;
63
boolean showUsage;
64
}
65
66
static abstract class Option {
67
Option(boolean hasArg, String... aliases) {
68
this.hasArg = hasArg;
69
this.aliases = aliases;
70
}
71
72
boolean isHidden() {
73
return false;
74
}
75
76
boolean matches(String opt) {
77
for (String a : aliases) {
78
if (a.equals(opt))
79
return true;
80
if (hasArg && opt.startsWith(a + "="))
81
return true;
82
}
83
return false;
84
}
85
86
boolean ignoreRest() {
87
return false;
88
}
89
90
abstract void process(JdepsTask task, String opt, String arg) throws BadArgs;
91
final boolean hasArg;
92
final String[] aliases;
93
}
94
95
static abstract class HiddenOption extends Option {
96
HiddenOption(boolean hasArg, String... aliases) {
97
super(hasArg, aliases);
98
}
99
100
boolean isHidden() {
101
return true;
102
}
103
}
104
105
static Option[] recognizedOptions = {
106
new Option(false, "-h", "-?", "-help") {
107
void process(JdepsTask task, String opt, String arg) {
108
task.options.help = true;
109
}
110
},
111
new Option(true, "-dotoutput") {
112
void process(JdepsTask task, String opt, String arg) throws BadArgs {
113
Path p = Paths.get(arg);
114
if (Files.exists(p) && (!Files.isDirectory(p) || !Files.isWritable(p))) {
115
throw new BadArgs("err.invalid.path", arg);
116
}
117
task.options.dotOutputDir = arg;
118
}
119
},
120
new Option(false, "-s", "-summary") {
121
void process(JdepsTask task, String opt, String arg) {
122
task.options.showSummary = true;
123
task.options.verbose = SUMMARY;
124
}
125
},
126
new Option(false, "-v", "-verbose",
127
"-verbose:package",
128
"-verbose:class") {
129
void process(JdepsTask task, String opt, String arg) throws BadArgs {
130
switch (opt) {
131
case "-v":
132
case "-verbose":
133
task.options.verbose = VERBOSE;
134
task.options.filterSameArchive = false;
135
task.options.filterSamePackage = false;
136
break;
137
case "-verbose:package":
138
task.options.verbose = PACKAGE;
139
break;
140
case "-verbose:class":
141
task.options.verbose = CLASS;
142
break;
143
default:
144
throw new BadArgs("err.invalid.arg.for.option", opt);
145
}
146
}
147
},
148
new Option(true, "-cp", "-classpath") {
149
void process(JdepsTask task, String opt, String arg) {
150
task.options.classpath = arg;
151
}
152
},
153
new Option(true, "-p", "-package") {
154
void process(JdepsTask task, String opt, String arg) {
155
task.options.packageNames.add(arg);
156
}
157
},
158
new Option(true, "-e", "-regex") {
159
void process(JdepsTask task, String opt, String arg) {
160
task.options.regex = arg;
161
}
162
},
163
164
new Option(true, "-f", "-filter") {
165
void process(JdepsTask task, String opt, String arg) {
166
task.options.filterRegex = arg;
167
}
168
},
169
new Option(false, "-filter:package",
170
"-filter:archive",
171
"-filter:none") {
172
void process(JdepsTask task, String opt, String arg) {
173
switch (opt) {
174
case "-filter:package":
175
task.options.filterSamePackage = true;
176
task.options.filterSameArchive = false;
177
break;
178
case "-filter:archive":
179
task.options.filterSameArchive = true;
180
task.options.filterSamePackage = false;
181
break;
182
case "-filter:none":
183
task.options.filterSameArchive = false;
184
task.options.filterSamePackage = false;
185
break;
186
}
187
}
188
},
189
new Option(true, "-include") {
190
void process(JdepsTask task, String opt, String arg) throws BadArgs {
191
task.options.includePattern = Pattern.compile(arg);
192
}
193
},
194
new Option(false, "-P", "-profile") {
195
void process(JdepsTask task, String opt, String arg) throws BadArgs {
196
task.options.showProfile = true;
197
if (Profile.getProfileCount() == 0) {
198
throw new BadArgs("err.option.unsupported", opt, getMessage("err.profiles.msg"));
199
}
200
}
201
},
202
new Option(false, "-apionly") {
203
void process(JdepsTask task, String opt, String arg) {
204
task.options.apiOnly = true;
205
}
206
},
207
new Option(false, "-R", "-recursive") {
208
void process(JdepsTask task, String opt, String arg) {
209
task.options.depth = 0;
210
// turn off filtering
211
task.options.filterSameArchive = false;
212
task.options.filterSamePackage = false;
213
}
214
},
215
new Option(false, "-jdkinternals") {
216
void process(JdepsTask task, String opt, String arg) {
217
task.options.findJDKInternals = true;
218
task.options.verbose = CLASS;
219
if (task.options.includePattern == null) {
220
task.options.includePattern = Pattern.compile(".*");
221
}
222
}
223
},
224
new Option(false, "-version") {
225
void process(JdepsTask task, String opt, String arg) {
226
task.options.version = true;
227
}
228
},
229
new HiddenOption(false, "-fullversion") {
230
void process(JdepsTask task, String opt, String arg) {
231
task.options.fullVersion = true;
232
}
233
},
234
new HiddenOption(false, "-showlabel") {
235
void process(JdepsTask task, String opt, String arg) {
236
task.options.showLabel = true;
237
}
238
},
239
new HiddenOption(false, "-q", "-quiet") {
240
void process(JdepsTask task, String opt, String arg) {
241
task.options.nowarning = true;
242
}
243
},
244
new HiddenOption(true, "-depth") {
245
void process(JdepsTask task, String opt, String arg) throws BadArgs {
246
try {
247
task.options.depth = Integer.parseInt(arg);
248
} catch (NumberFormatException e) {
249
throw new BadArgs("err.invalid.arg.for.option", opt);
250
}
251
}
252
},
253
};
254
255
private static final String PROGNAME = "jdeps";
256
private final Options options = new Options();
257
private final List<String> classes = new ArrayList<>();
258
259
private PrintWriter log;
260
void setLog(PrintWriter out) {
261
log = out;
262
}
263
264
/**
265
* Result codes.
266
*/
267
static final int EXIT_OK = 0, // Completed with no errors.
268
EXIT_ERROR = 1, // Completed but reported errors.
269
EXIT_CMDERR = 2, // Bad command-line arguments
270
EXIT_SYSERR = 3, // System error or resource exhaustion.
271
EXIT_ABNORMAL = 4;// terminated abnormally
272
273
int run(String[] args) {
274
if (log == null) {
275
log = new PrintWriter(System.out);
276
}
277
try {
278
handleOptions(args);
279
if (options.help) {
280
showHelp();
281
}
282
if (options.version || options.fullVersion) {
283
showVersion(options.fullVersion);
284
}
285
if (classes.isEmpty() && options.includePattern == null) {
286
if (options.help || options.version || options.fullVersion) {
287
return EXIT_OK;
288
} else {
289
showHelp();
290
return EXIT_CMDERR;
291
}
292
}
293
if (options.regex != null && options.packageNames.size() > 0) {
294
showHelp();
295
return EXIT_CMDERR;
296
}
297
if (options.findJDKInternals &&
298
(options.regex != null || options.packageNames.size() > 0 || options.showSummary)) {
299
showHelp();
300
return EXIT_CMDERR;
301
}
302
if (options.showSummary && options.verbose != SUMMARY) {
303
showHelp();
304
return EXIT_CMDERR;
305
}
306
boolean ok = run();
307
return ok ? EXIT_OK : EXIT_ERROR;
308
} catch (BadArgs e) {
309
reportError(e.key, e.args);
310
if (e.showUsage) {
311
log.println(getMessage("main.usage.summary", PROGNAME));
312
}
313
return EXIT_CMDERR;
314
} catch (IOException e) {
315
return EXIT_ABNORMAL;
316
} finally {
317
log.flush();
318
}
319
}
320
321
private final List<Archive> sourceLocations = new ArrayList<>();
322
private boolean run() throws IOException {
323
// parse classfiles and find all dependencies
324
findDependencies();
325
326
Analyzer analyzer = new Analyzer(options.verbose, new Analyzer.Filter() {
327
@Override
328
public boolean accepts(Location origin, Archive originArchive,
329
Location target, Archive targetArchive)
330
{
331
if (options.findJDKInternals) {
332
// accepts target that is JDK class but not exported
333
return isJDKArchive(targetArchive) &&
334
!((JDKArchive) targetArchive).isExported(target.getClassName());
335
} else if (options.filterSameArchive) {
336
// accepts origin and target that from different archive
337
return originArchive != targetArchive;
338
}
339
return true;
340
}
341
});
342
343
// analyze the dependencies
344
analyzer.run(sourceLocations);
345
346
// output result
347
if (options.dotOutputDir != null) {
348
Path dir = Paths.get(options.dotOutputDir);
349
Files.createDirectories(dir);
350
generateDotFiles(dir, analyzer);
351
} else {
352
printRawOutput(log, analyzer);
353
}
354
355
if (options.findJDKInternals && !options.nowarning) {
356
showReplacements(analyzer);
357
}
358
return true;
359
}
360
361
private void generateSummaryDotFile(Path dir, Analyzer analyzer) throws IOException {
362
// If verbose mode (-v or -verbose option),
363
// the summary.dot file shows package-level dependencies.
364
Analyzer.Type summaryType =
365
(options.verbose == PACKAGE || options.verbose == SUMMARY) ? SUMMARY : PACKAGE;
366
Path summary = dir.resolve("summary.dot");
367
try (PrintWriter sw = new PrintWriter(Files.newOutputStream(summary));
368
SummaryDotFile dotfile = new SummaryDotFile(sw, summaryType)) {
369
for (Archive archive : sourceLocations) {
370
if (!archive.isEmpty()) {
371
if (options.verbose == PACKAGE || options.verbose == SUMMARY) {
372
if (options.showLabel) {
373
// build labels listing package-level dependencies
374
analyzer.visitDependences(archive, dotfile.labelBuilder(), PACKAGE);
375
}
376
}
377
analyzer.visitDependences(archive, dotfile, summaryType);
378
}
379
}
380
}
381
}
382
383
private void generateDotFiles(Path dir, Analyzer analyzer) throws IOException {
384
// output individual .dot file for each archive
385
if (options.verbose != SUMMARY) {
386
for (Archive archive : sourceLocations) {
387
if (analyzer.hasDependences(archive)) {
388
Path dotfile = dir.resolve(archive.getName() + ".dot");
389
try (PrintWriter pw = new PrintWriter(Files.newOutputStream(dotfile));
390
DotFileFormatter formatter = new DotFileFormatter(pw, archive)) {
391
analyzer.visitDependences(archive, formatter);
392
}
393
}
394
}
395
}
396
// generate summary dot file
397
generateSummaryDotFile(dir, analyzer);
398
}
399
400
private void printRawOutput(PrintWriter writer, Analyzer analyzer) {
401
RawOutputFormatter depFormatter = new RawOutputFormatter(writer);
402
RawSummaryFormatter summaryFormatter = new RawSummaryFormatter(writer);
403
for (Archive archive : sourceLocations) {
404
if (!archive.isEmpty()) {
405
analyzer.visitDependences(archive, summaryFormatter, SUMMARY);
406
if (analyzer.hasDependences(archive) && options.verbose != SUMMARY) {
407
analyzer.visitDependences(archive, depFormatter);
408
}
409
}
410
}
411
}
412
413
private boolean isValidClassName(String name) {
414
if (!Character.isJavaIdentifierStart(name.charAt(0))) {
415
return false;
416
}
417
for (int i=1; i < name.length(); i++) {
418
char c = name.charAt(i);
419
if (c != '.' && !Character.isJavaIdentifierPart(c)) {
420
return false;
421
}
422
}
423
return true;
424
}
425
426
/*
427
* Dep Filter configured based on the input jdeps option
428
* 1. -p and -regex to match target dependencies
429
* 2. -filter:package to filter out same-package dependencies
430
*
431
* This filter is applied when jdeps parses the class files
432
* and filtered dependencies are not stored in the Analyzer.
433
*
434
* -filter:archive is applied later in the Analyzer as the
435
* containing archive of a target class may not be known until
436
* the entire archive
437
*/
438
class DependencyFilter implements Dependency.Filter {
439
final Dependency.Filter filter;
440
final Pattern filterPattern;
441
DependencyFilter() {
442
if (options.regex != null) {
443
this.filter = Dependencies.getRegexFilter(Pattern.compile(options.regex));
444
} else if (options.packageNames.size() > 0) {
445
this.filter = Dependencies.getPackageFilter(options.packageNames, false);
446
} else {
447
this.filter = null;
448
}
449
450
this.filterPattern =
451
options.filterRegex != null ? Pattern.compile(options.filterRegex) : null;
452
}
453
@Override
454
public boolean accepts(Dependency d) {
455
if (d.getOrigin().equals(d.getTarget())) {
456
return false;
457
}
458
String pn = d.getTarget().getPackageName();
459
if (options.filterSamePackage && d.getOrigin().getPackageName().equals(pn)) {
460
return false;
461
}
462
463
if (filterPattern != null && filterPattern.matcher(pn).matches()) {
464
return false;
465
}
466
return filter != null ? filter.accepts(d) : true;
467
}
468
}
469
470
/**
471
* Tests if the given class matches the pattern given in the -include option
472
* or if it's a public class if -apionly option is specified
473
*/
474
private boolean matches(String classname, AccessFlags flags) {
475
if (options.apiOnly && !flags.is(AccessFlags.ACC_PUBLIC)) {
476
return false;
477
} else if (options.includePattern != null) {
478
return options.includePattern.matcher(classname.replace('/', '.')).matches();
479
} else {
480
return true;
481
}
482
}
483
484
private void findDependencies() throws IOException {
485
Dependency.Finder finder =
486
options.apiOnly ? Dependencies.getAPIFinder(AccessFlags.ACC_PROTECTED)
487
: Dependencies.getClassDependencyFinder();
488
Dependency.Filter filter = new DependencyFilter();
489
490
List<Archive> archives = new ArrayList<>();
491
Deque<String> roots = new LinkedList<>();
492
List<Path> paths = new ArrayList<>();
493
for (String s : classes) {
494
Path p = Paths.get(s);
495
if (Files.exists(p)) {
496
paths.add(p);
497
archives.add(Archive.getInstance(p));
498
} else {
499
if (isValidClassName(s)) {
500
roots.add(s);
501
} else {
502
warning("warn.invalid.arg", s);
503
}
504
}
505
}
506
sourceLocations.addAll(archives);
507
508
List<Archive> classpaths = new ArrayList<>(); // for class file lookup
509
classpaths.addAll(getClassPathArchives(options.classpath, paths));
510
if (options.includePattern != null) {
511
archives.addAll(classpaths);
512
}
513
classpaths.addAll(PlatformClassPath.getArchives());
514
515
// add all classpath archives to the source locations for reporting
516
sourceLocations.addAll(classpaths);
517
518
// warn about Multi-Release jars
519
for (Archive a : sourceLocations) {
520
if (a.reader().isMultiReleaseJar()) {
521
warning("warn.mrjar.usejdk9", a.getPathName());
522
}
523
}
524
525
// Work queue of names of classfiles to be searched.
526
// Entries will be unique, and for classes that do not yet have
527
// dependencies in the results map.
528
Deque<String> deque = new LinkedList<>();
529
Set<String> doneClasses = new HashSet<>();
530
531
// get the immediate dependencies of the input files
532
for (Archive a : archives) {
533
for (ClassFile cf : a.reader().getClassFiles()) {
534
String classFileName;
535
try {
536
classFileName = cf.getName();
537
} catch (ConstantPoolException e) {
538
throw new ClassFileError(e);
539
}
540
541
// tests if this class matches the -include or -apiOnly option if specified
542
if (!matches(classFileName, cf.access_flags)) {
543
continue;
544
}
545
546
if (!doneClasses.contains(classFileName)) {
547
doneClasses.add(classFileName);
548
}
549
550
for (Dependency d : finder.findDependencies(cf)) {
551
if (filter.accepts(d)) {
552
String cn = d.getTarget().getName();
553
if (!doneClasses.contains(cn) && !deque.contains(cn)) {
554
deque.add(cn);
555
}
556
a.addClass(d.getOrigin(), d.getTarget());
557
} else {
558
// ensure that the parsed class is added the archive
559
a.addClass(d.getOrigin());
560
}
561
}
562
for (String name : a.reader().skippedEntries()) {
563
warning("warn.skipped.entry", name, a.getPathName());
564
}
565
}
566
}
567
568
// add Archive for looking up classes from the classpath
569
// for transitive dependency analysis
570
Deque<String> unresolved = roots;
571
int depth = options.depth > 0 ? options.depth : Integer.MAX_VALUE;
572
do {
573
String name;
574
while ((name = unresolved.poll()) != null) {
575
if (doneClasses.contains(name)) {
576
continue;
577
}
578
ClassFile cf = null;
579
for (Archive a : classpaths) {
580
cf = a.reader().getClassFile(name);
581
if (cf != null) {
582
String classFileName;
583
try {
584
classFileName = cf.getName();
585
} catch (ConstantPoolException e) {
586
throw new ClassFileError(e);
587
}
588
if (!doneClasses.contains(classFileName)) {
589
// if name is a fully-qualified class name specified
590
// from command-line, this class might already be parsed
591
doneClasses.add(classFileName);
592
// process @jdk.Exported for JDK classes
593
if (isJDKArchive(a)) {
594
((JDKArchive)a).processJdkExported(cf);
595
}
596
for (Dependency d : finder.findDependencies(cf)) {
597
if (depth == 0) {
598
// ignore the dependency
599
a.addClass(d.getOrigin());
600
break;
601
} else if (filter.accepts(d)) {
602
a.addClass(d.getOrigin(), d.getTarget());
603
String cn = d.getTarget().getName();
604
if (!doneClasses.contains(cn) && !deque.contains(cn)) {
605
deque.add(cn);
606
}
607
} else {
608
// ensure that the parsed class is added the archive
609
a.addClass(d.getOrigin());
610
}
611
}
612
}
613
break;
614
}
615
}
616
if (cf == null) {
617
doneClasses.add(name);
618
}
619
}
620
unresolved = deque;
621
deque = new LinkedList<>();
622
} while (!unresolved.isEmpty() && depth-- > 0);
623
}
624
625
public void handleOptions(String[] args) throws BadArgs {
626
// process options
627
for (int i=0; i < args.length; i++) {
628
if (args[i].charAt(0) == '-') {
629
String name = args[i];
630
Option option = getOption(name);
631
String param = null;
632
if (option.hasArg) {
633
if (name.startsWith("-") && name.indexOf('=') > 0) {
634
param = name.substring(name.indexOf('=') + 1, name.length());
635
} else if (i + 1 < args.length) {
636
param = args[++i];
637
}
638
if (param == null || param.isEmpty() || param.charAt(0) == '-') {
639
throw new BadArgs("err.missing.arg", name).showUsage(true);
640
}
641
}
642
option.process(this, name, param);
643
if (option.ignoreRest()) {
644
i = args.length;
645
}
646
} else {
647
// process rest of the input arguments
648
for (; i < args.length; i++) {
649
String name = args[i];
650
if (name.charAt(0) == '-') {
651
throw new BadArgs("err.option.after.class", name).showUsage(true);
652
}
653
classes.add(name);
654
}
655
}
656
}
657
}
658
659
private Option getOption(String name) throws BadArgs {
660
for (Option o : recognizedOptions) {
661
if (o.matches(name)) {
662
return o;
663
}
664
}
665
throw new BadArgs("err.unknown.option", name).showUsage(true);
666
}
667
668
private void reportError(String key, Object... args) {
669
log.println(getMessage("error.prefix") + " " + getMessage(key, args));
670
}
671
672
private void warning(String key, Object... args) {
673
log.println(getMessage("warn.prefix") + " " + getMessage(key, args));
674
}
675
676
private void showHelp() {
677
log.println(getMessage("main.usage", PROGNAME));
678
for (Option o : recognizedOptions) {
679
String name = o.aliases[0].substring(1); // there must always be at least one name
680
name = name.charAt(0) == '-' ? name.substring(1) : name;
681
if (o.isHidden() || name.equals("h") || name.startsWith("filter:")) {
682
continue;
683
}
684
log.println(getMessage("main.opt." + name));
685
}
686
}
687
688
private void showVersion(boolean full) {
689
log.println(version(full ? "full" : "release"));
690
}
691
692
private String version(String key) {
693
// key=version: mm.nn.oo[-milestone]
694
// key=full: mm.mm.oo[-milestone]-build
695
if (ResourceBundleHelper.versionRB == null) {
696
return System.getProperty("java.version");
697
}
698
try {
699
return ResourceBundleHelper.versionRB.getString(key);
700
} catch (MissingResourceException e) {
701
return getMessage("version.unknown", System.getProperty("java.version"));
702
}
703
}
704
705
static String getMessage(String key, Object... args) {
706
try {
707
return MessageFormat.format(ResourceBundleHelper.bundle.getString(key), args);
708
} catch (MissingResourceException e) {
709
throw new InternalError("Missing message: " + key);
710
}
711
}
712
713
private static class Options {
714
boolean help;
715
boolean version;
716
boolean fullVersion;
717
boolean showProfile;
718
boolean showSummary;
719
boolean apiOnly;
720
boolean showLabel;
721
boolean findJDKInternals;
722
boolean nowarning;
723
// default is to show package-level dependencies
724
// and filter references from same package
725
Analyzer.Type verbose = PACKAGE;
726
boolean filterSamePackage = true;
727
boolean filterSameArchive = false;
728
String filterRegex;
729
String dotOutputDir;
730
String classpath = "";
731
int depth = 1;
732
Set<String> packageNames = new HashSet<>();
733
String regex; // apply to the dependences
734
Pattern includePattern; // apply to classes
735
}
736
private static class ResourceBundleHelper {
737
static final ResourceBundle versionRB;
738
static final ResourceBundle bundle;
739
static final ResourceBundle jdkinternals;
740
741
static {
742
Locale locale = Locale.getDefault();
743
try {
744
bundle = ResourceBundle.getBundle("com.sun.tools.jdeps.resources.jdeps", locale);
745
} catch (MissingResourceException e) {
746
throw new InternalError("Cannot find jdeps resource bundle for locale " + locale);
747
}
748
try {
749
versionRB = ResourceBundle.getBundle("com.sun.tools.jdeps.resources.version");
750
} catch (MissingResourceException e) {
751
throw new InternalError("version.resource.missing");
752
}
753
try {
754
jdkinternals = ResourceBundle.getBundle("com.sun.tools.jdeps.resources.jdkinternals");
755
} catch (MissingResourceException e) {
756
throw new InternalError("Cannot find jdkinternals resource bundle");
757
}
758
}
759
}
760
761
/*
762
* Returns the list of Archive specified in cpaths and not included
763
* initialArchives
764
*/
765
private List<Archive> getClassPathArchives(String cpaths, List<Path> initialArchives)
766
throws IOException
767
{
768
List<Archive> result = new ArrayList<>();
769
if (cpaths.isEmpty()) {
770
return result;
771
}
772
773
List<Path> paths = new ArrayList<>();
774
for (String p : cpaths.split(File.pathSeparator)) {
775
if (p.length() > 0) {
776
// wildcard to parse all JAR files e.g. -classpath dir/*
777
int i = p.lastIndexOf(".*");
778
if (i > 0) {
779
Path dir = Paths.get(p.substring(0, i));
780
try (DirectoryStream<Path> stream = Files.newDirectoryStream(dir, "*.jar")) {
781
for (Path entry : stream) {
782
paths.add(entry);
783
}
784
}
785
} else {
786
paths.add(Paths.get(p));
787
}
788
}
789
}
790
for (Path p : paths) {
791
if (Files.exists(p) && !hasSameFile(initialArchives, p)) {
792
result.add(Archive.getInstance(p));
793
}
794
}
795
return result;
796
}
797
798
private boolean hasSameFile(List<Path> paths, Path p2) throws IOException {
799
for (Path p1 : paths) {
800
if (Files.isSameFile(p1, p2)) {
801
return true;
802
}
803
}
804
return false;
805
}
806
807
class RawOutputFormatter implements Analyzer.Visitor {
808
private final PrintWriter writer;
809
private String pkg = "";
810
RawOutputFormatter(PrintWriter writer) {
811
this.writer = writer;
812
}
813
@Override
814
public void visitDependence(String origin, Archive originArchive,
815
String target, Archive targetArchive) {
816
String tag = toTag(target, targetArchive);
817
if (options.verbose == VERBOSE) {
818
writer.format(" %-50s -> %-50s %s%n", origin, target, tag);
819
} else {
820
if (!origin.equals(pkg)) {
821
pkg = origin;
822
writer.format(" %s (%s)%n", origin, originArchive.getName());
823
}
824
writer.format(" -> %-50s %s%n", target, tag);
825
}
826
}
827
}
828
829
class RawSummaryFormatter implements Analyzer.Visitor {
830
private final PrintWriter writer;
831
RawSummaryFormatter(PrintWriter writer) {
832
this.writer = writer;
833
}
834
@Override
835
public void visitDependence(String origin, Archive originArchive,
836
String target, Archive targetArchive) {
837
writer.format("%s -> %s", originArchive.getName(), targetArchive.getPathName());
838
if (options.showProfile && JDKArchive.isProfileArchive(targetArchive)) {
839
writer.format(" (%s)", target);
840
}
841
writer.format("%n");
842
}
843
}
844
845
class DotFileFormatter implements Analyzer.Visitor, AutoCloseable {
846
private final PrintWriter writer;
847
private final String name;
848
DotFileFormatter(PrintWriter writer, Archive archive) {
849
this.writer = writer;
850
this.name = archive.getName();
851
writer.format("digraph \"%s\" {%n", name);
852
writer.format(" // Path: %s%n", archive.getPathName());
853
}
854
855
@Override
856
public void close() {
857
writer.println("}");
858
}
859
860
@Override
861
public void visitDependence(String origin, Archive originArchive,
862
String target, Archive targetArchive) {
863
String tag = toTag(target, targetArchive);
864
writer.format(" %-50s -> \"%s\";%n",
865
String.format("\"%s\"", origin),
866
tag.isEmpty() ? target
867
: String.format("%s (%s)", target, tag));
868
}
869
}
870
871
class SummaryDotFile implements Analyzer.Visitor, AutoCloseable {
872
private final PrintWriter writer;
873
private final Analyzer.Type type;
874
private final Map<Archive, Map<Archive,StringBuilder>> edges = new HashMap<>();
875
SummaryDotFile(PrintWriter writer, Analyzer.Type type) {
876
this.writer = writer;
877
this.type = type;
878
writer.format("digraph \"summary\" {%n");
879
}
880
881
@Override
882
public void close() {
883
writer.println("}");
884
}
885
886
@Override
887
public void visitDependence(String origin, Archive originArchive,
888
String target, Archive targetArchive) {
889
String targetName = type == PACKAGE ? target : targetArchive.getName();
890
if (type == PACKAGE) {
891
String tag = toTag(target, targetArchive, type);
892
if (!tag.isEmpty())
893
targetName += " (" + tag + ")";
894
} else if (options.showProfile && JDKArchive.isProfileArchive(targetArchive)) {
895
targetName += " (" + target + ")";
896
}
897
String label = getLabel(originArchive, targetArchive);
898
writer.format(" %-50s -> \"%s\"%s;%n",
899
String.format("\"%s\"", origin), targetName, label);
900
}
901
902
String getLabel(Archive origin, Archive target) {
903
if (edges.isEmpty())
904
return "";
905
906
StringBuilder label = edges.get(origin).get(target);
907
return label == null ? "" : String.format(" [label=\"%s\",fontsize=9]", label.toString());
908
}
909
910
Analyzer.Visitor labelBuilder() {
911
// show the package-level dependencies as labels in the dot graph
912
return new Analyzer.Visitor() {
913
@Override
914
public void visitDependence(String origin, Archive originArchive,
915
String target, Archive targetArchive)
916
{
917
Map<Archive,StringBuilder> labels = edges.get(originArchive);
918
if (!edges.containsKey(originArchive)) {
919
edges.put(originArchive, labels = new HashMap<>());
920
}
921
StringBuilder sb = labels.get(targetArchive);
922
if (sb == null) {
923
labels.put(targetArchive, sb = new StringBuilder());
924
}
925
String tag = toTag(target, targetArchive, PACKAGE);
926
addLabel(sb, origin, target, tag);
927
}
928
929
void addLabel(StringBuilder label, String origin, String target, String tag) {
930
label.append(origin).append(" -> ").append(target);
931
if (!tag.isEmpty()) {
932
label.append(" (" + tag + ")");
933
}
934
label.append("\\n");
935
}
936
};
937
}
938
}
939
940
/**
941
* Test if the given archive is part of the JDK
942
*/
943
private boolean isJDKArchive(Archive archive) {
944
return JDKArchive.class.isInstance(archive);
945
}
946
947
/**
948
* If the given archive is JDK archive, this method returns the profile name
949
* only if -profile option is specified; it accesses a private JDK API and
950
* the returned value will have "JDK internal API" prefix
951
*
952
* For non-JDK archives, this method returns the file name of the archive.
953
*/
954
private String toTag(String name, Archive source, Analyzer.Type type) {
955
if (!isJDKArchive(source)) {
956
return source.getName();
957
}
958
959
JDKArchive jdk = (JDKArchive)source;
960
boolean isExported = false;
961
if (type == CLASS || type == VERBOSE) {
962
isExported = jdk.isExported(name);
963
} else {
964
isExported = jdk.isExportedPackage(name);
965
}
966
Profile p = getProfile(name, type);
967
if (isExported) {
968
// exported API
969
return options.showProfile && p != null ? p.profileName() : "";
970
} else {
971
return "JDK internal API (" + source.getName() + ")";
972
}
973
}
974
975
private String toTag(String name, Archive source) {
976
return toTag(name, source, options.verbose);
977
}
978
979
private Profile getProfile(String name, Analyzer.Type type) {
980
String pn = name;
981
if (type == CLASS || type == VERBOSE) {
982
int i = name.lastIndexOf('.');
983
pn = i > 0 ? name.substring(0, i) : "";
984
}
985
return Profile.getProfile(pn);
986
}
987
988
/**
989
* Returns the recommended replacement API for the given classname;
990
* or return null if replacement API is not known.
991
*/
992
private String replacementFor(String cn) {
993
String name = cn;
994
String value = null;
995
while (value == null && name != null) {
996
try {
997
value = ResourceBundleHelper.jdkinternals.getString(name);
998
} catch (MissingResourceException e) {
999
// go up one subpackage level
1000
int i = name.lastIndexOf('.');
1001
name = i > 0 ? name.substring(0, i) : null;
1002
}
1003
}
1004
return value;
1005
};
1006
1007
private void showReplacements(Analyzer analyzer) {
1008
Map<String,String> jdkinternals = new TreeMap<>();
1009
boolean useInternals = false;
1010
for (Archive source : sourceLocations) {
1011
useInternals = useInternals || analyzer.hasDependences(source);
1012
for (String cn : analyzer.dependences(source)) {
1013
String repl = replacementFor(cn);
1014
if (repl != null && !jdkinternals.containsKey(cn)) {
1015
jdkinternals.put(cn, repl);
1016
}
1017
}
1018
}
1019
if (useInternals) {
1020
log.println();
1021
warning("warn.replace.useJDKInternals", getMessage("jdeps.wiki.url"));
1022
}
1023
if (!jdkinternals.isEmpty()) {
1024
log.println();
1025
log.format("%-40s %s%n", "JDK Internal API", "Suggested Replacement");
1026
log.format("%-40s %s%n", "----------------", "---------------------");
1027
for (Map.Entry<String,String> e : jdkinternals.entrySet()) {
1028
log.format("%-40s %s%n", e.getKey(), e.getValue());
1029
}
1030
}
1031
1032
}
1033
}
1034
1035