1 /* 2 * Copyright (c) 2012, 2014, 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 26 package com.sun.tools.jdeps; 27 28 import static com.sun.tools.jdeps.Analyzer.Type.*; 29 import static com.sun.tools.jdeps.JdepsWriter.*; 30 31 import java.io.IOException; 32 import java.io.PrintWriter; 33 import java.lang.module.ResolutionException; 34 import java.nio.file.Files; 35 import java.nio.file.Path; 36 import java.nio.file.Paths; 37 import java.text.MessageFormat; 38 import java.util.*; 39 import java.util.regex.Pattern; 40 import java.util.stream.Collectors; 41 import java.util.stream.Stream; 42 43 /** 44 * Implementation for the jdeps tool for static class dependency analysis. 45 */ 46 class JdepsTask { 47 static interface BadArguments { 48 String getKey(); 49 Object[] getArgs(); 50 boolean showUsage(); 51 } 52 static class BadArgs extends Exception implements BadArguments { 53 static final long serialVersionUID = 8765093759964640721L; 54 BadArgs(String key, Object... args) { 55 super(JdepsTask.getMessage(key, args)); 56 this.key = key; 57 this.args = args; 58 } 59 60 BadArgs showUsage(boolean b) { 61 showUsage = b; 62 return this; 63 } 64 final String key; 65 final Object[] args; 66 boolean showUsage; 67 68 @Override 69 public String getKey() { 70 return key; 71 } 72 73 @Override 74 public Object[] getArgs() { 75 return args; 76 } 77 78 @Override 79 public boolean showUsage() { 80 return showUsage; 81 } 82 } 83 84 static class UncheckedBadArgs extends RuntimeException implements BadArguments { 85 static final long serialVersionUID = -1L; 86 final BadArgs cause; 87 UncheckedBadArgs(BadArgs cause) { 88 super(cause); 89 this.cause = cause; 90 } 91 @Override 92 public String getKey() { 93 return cause.key; 94 } 95 96 @Override 97 public Object[] getArgs() { 98 return cause.args; 99 } 100 101 @Override 102 public boolean showUsage() { 103 return cause.showUsage; 104 } 105 } 106 107 static abstract class Option { 108 Option(boolean hasArg, String... aliases) { 109 this.hasArg = hasArg; 110 this.aliases = aliases; 111 } 112 113 boolean isHidden() { 114 return false; 115 } 116 117 boolean matches(String opt) { 118 for (String a : aliases) { 119 if (a.equals(opt)) 120 return true; 121 if (hasArg && opt.startsWith(a + "=")) 122 return true; 123 } 124 return false; 125 } 126 127 boolean ignoreRest() { 128 return false; 129 } 130 131 abstract void process(JdepsTask task, String opt, String arg) throws BadArgs; 132 final boolean hasArg; 133 final String[] aliases; 134 } 135 136 static abstract class HiddenOption extends Option { 137 HiddenOption(boolean hasArg, String... aliases) { 138 super(hasArg, aliases); 139 } 140 141 boolean isHidden() { 142 return true; 143 } 144 } 145 146 static Option[] recognizedOptions = { 147 new Option(false, "-h", "-?", "-help") { 148 void process(JdepsTask task, String opt, String arg) { 149 task.options.help = true; 150 } 151 }, 152 new Option(true, "-dotoutput") { 153 void process(JdepsTask task, String opt, String arg) throws BadArgs { 154 Path p = Paths.get(arg); 155 if (Files.exists(p) && (!Files.isDirectory(p) || !Files.isWritable(p))) { 156 throw new BadArgs("err.invalid.path", arg); 157 } 158 task.options.dotOutputDir = Paths.get(arg);; 159 } 160 }, 161 new Option(false, "-s", "-summary") { 162 void process(JdepsTask task, String opt, String arg) { 163 task.options.showSummary = true; 164 task.options.verbose = SUMMARY; 165 } 166 }, 167 new Option(false, "-v", "-verbose", 168 "-verbose:module", 169 "-verbose:package", 170 "-verbose:class") { 171 void process(JdepsTask task, String opt, String arg) throws BadArgs { 172 switch (opt) { 173 case "-v": 174 case "-verbose": 175 task.options.verbose = VERBOSE; 176 task.options.filterSameArchive = false; 177 task.options.filterSamePackage = false; 178 break; 179 case "-verbose:module": 180 task.options.verbose = MODULE; 181 break; 182 case "-verbose:package": 183 task.options.verbose = PACKAGE; 184 break; 185 case "-verbose:class": 186 task.options.verbose = CLASS; 187 break; 188 default: 189 throw new BadArgs("err.invalid.arg.for.option", opt); 190 } 191 } 192 }, 193 new Option(false, "-apionly") { 194 void process(JdepsTask task, String opt, String arg) { 195 task.options.apiOnly = true; 196 } 197 }, 198 new Option(true, "-check") { 199 void process(JdepsTask task, String opt, String arg) throws BadArgs { 200 Set<String> mods = Set.of(arg.split(",")); 201 task.options.checkModuleDeps = mods; 202 task.options.addmods.addAll(mods); 203 } 204 }, 205 new Option(true, "-genmoduleinfo") { 206 void process(JdepsTask task, String opt, String arg) throws BadArgs { 207 Path p = Paths.get(arg); 208 if (Files.exists(p) && (!Files.isDirectory(p) || !Files.isWritable(p))) { 209 throw new BadArgs("err.invalid.path", arg); 210 } 211 task.options.genModuleInfo = Paths.get(arg); 212 } 213 }, 214 new Option(false, "-jdkinternals") { 215 void process(JdepsTask task, String opt, String arg) { 216 task.options.findJDKInternals = true; 217 task.options.verbose = CLASS; 218 if (task.options.includePattern == null) { 219 task.options.includePattern = Pattern.compile(".*"); 220 } 221 } 222 }, 223 224 // ---- paths option ---- 225 new Option(true, "-cp", "-classpath") { 226 void process(JdepsTask task, String opt, String arg) { 227 task.options.classpath = arg; 228 } 229 }, 230 new Option(true, "-mp", "-modulepath") { 231 void process(JdepsTask task, String opt, String arg) throws BadArgs { 232 task.options.modulePath = arg; 233 } 234 }, 235 new Option(true, "-upgrademodulepath") { 236 void process(JdepsTask task, String opt, String arg) throws BadArgs { 237 task.options.upgradeModulePath = arg; 238 } 239 }, 240 new Option(true, "-system") { 241 void process(JdepsTask task, String opt, String arg) throws BadArgs { 242 if (arg.equals("none")) { 243 task.options.systemModulePath = null; 244 } else { 245 Path path = Paths.get(arg); 246 if (Files.isRegularFile(path.resolve("lib").resolve("modules"))) 247 task.options.systemModulePath = arg; 248 else 249 throw new BadArgs("err.invalid.path", arg); 250 } 251 } 252 }, 253 new Option(true, "-addmods") { 254 void process(JdepsTask task, String opt, String arg) throws BadArgs { 255 Set<String> mods = Set.of(arg.split(",")); 256 task.options.addmods.addAll(mods); 257 } 258 }, 259 new Option(true, "-m") { 260 void process(JdepsTask task, String opt, String arg) throws BadArgs { 261 task.options.rootModule = arg; 262 task.options.addmods.add(arg); 263 } 264 }, 265 266 // ---- Target filtering options ---- 267 new Option(true, "-p", "-package") { 268 void process(JdepsTask task, String opt, String arg) { 269 task.options.packageNames.add(arg); 270 } 271 }, 272 new Option(true, "-e", "-regex") { 273 void process(JdepsTask task, String opt, String arg) { 274 task.options.regex = Pattern.compile(arg); 275 } 276 }, 277 new Option(true, "-requires") { 278 void process(JdepsTask task, String opt, String arg) { 279 task.options.requires.add(arg); 280 } 281 }, 282 new Option(true, "-f", "-filter") { 283 void process(JdepsTask task, String opt, String arg) { 284 task.options.filterRegex = Pattern.compile(arg); 285 } 286 }, 287 new Option(false, "-filter:package", 288 "-filter:archive", "-filter:module", 289 "-filter:none") { 290 void process(JdepsTask task, String opt, String arg) { 291 switch (opt) { 292 case "-filter:package": 293 task.options.filterSamePackage = true; 294 task.options.filterSameArchive = false; 295 break; 296 case "-filter:archive": 297 case "-filter:module": 298 task.options.filterSameArchive = true; 299 task.options.filterSamePackage = false; 300 break; 301 case "-filter:none": 302 task.options.filterSameArchive = false; 303 task.options.filterSamePackage = false; 304 break; 305 } 306 } 307 }, 308 309 // ---- Source filtering options ---- 310 new Option(true, "-include") { 311 void process(JdepsTask task, String opt, String arg) throws BadArgs { 312 task.options.includePattern = Pattern.compile(arg); 313 } 314 }, 315 316 // Another alternative to list modules in -addmods option 317 new HiddenOption(true, "-include-system-modules") { 318 void process(JdepsTask task, String opt, String arg) throws BadArgs { 319 task.options.includeSystemModulePattern = Pattern.compile(arg); 320 } 321 }, 322 323 new Option(false, "-P", "-profile") { 324 void process(JdepsTask task, String opt, String arg) throws BadArgs { 325 task.options.showProfile = true; 326 } 327 }, 328 329 new Option(false, "-R", "-recursive") { 330 void process(JdepsTask task, String opt, String arg) { 331 task.options.depth = 0; 332 // turn off filtering 333 task.options.filterSameArchive = false; 334 task.options.filterSamePackage = false; 335 } 336 }, 337 338 new Option(false, "-I", "-inverse") { 339 void process(JdepsTask task, String opt, String arg) { 340 task.options.inverse = true; 341 // equivalent to the inverse of compile-time view analysis 342 task.options.compileTimeView = true; 343 task.options.filterSamePackage = true; 344 task.options.filterSameArchive = true; 345 } 346 }, 347 348 new Option(false, "-ct", "-compile-time") { 349 void process(JdepsTask task, String opt, String arg) { 350 task.options.compileTimeView = true; 351 task.options.filterSamePackage = true; 352 task.options.filterSameArchive = true; 353 task.options.depth = 0; 354 } 355 }, 356 357 new Option(false, "-q", "-quiet") { 358 void process(JdepsTask task, String opt, String arg) { 359 task.options.nowarning = true; 360 } 361 }, 362 363 new Option(false, "-version") { 364 void process(JdepsTask task, String opt, String arg) { 365 task.options.version = true; 366 } 367 }, 368 new HiddenOption(false, "-fullversion") { 369 void process(JdepsTask task, String opt, String arg) { 370 task.options.fullVersion = true; 371 } 372 }, 373 new HiddenOption(false, "-showlabel") { 374 void process(JdepsTask task, String opt, String arg) { 375 task.options.showLabel = true; 376 } 377 }, 378 new HiddenOption(false, "-hide-module") { 379 void process(JdepsTask task, String opt, String arg) { 380 task.options.showModule = false; 381 } 382 }, 383 new HiddenOption(true, "-depth") { 384 void process(JdepsTask task, String opt, String arg) throws BadArgs { 385 try { 386 task.options.depth = Integer.parseInt(arg); 387 } catch (NumberFormatException e) { 388 throw new BadArgs("err.invalid.arg.for.option", opt); 389 } 390 } 391 }, 392 }; 393 394 private static final String PROGNAME = "jdeps"; 395 private final Options options = new Options(); 396 private final List<String> inputArgs = new ArrayList<>(); 397 398 private PrintWriter log; 399 void setLog(PrintWriter out) { 400 log = out; 401 } 402 403 /** 404 * Result codes. 405 */ 406 static final int EXIT_OK = 0, // Completed with no errors. 407 EXIT_ERROR = 1, // Completed but reported errors. 408 EXIT_CMDERR = 2, // Bad command-line arguments 409 EXIT_SYSERR = 3, // System error or resource exhaustion. 410 EXIT_ABNORMAL = 4; // terminated abnormally 411 412 int run(String... args) { 413 if (log == null) { 414 log = new PrintWriter(System.out); 415 } 416 try { 417 handleOptions(args); 418 if (options.help) { 419 showHelp(); 420 } 421 if (options.version || options.fullVersion) { 422 showVersion(options.fullVersion); 423 } 424 if (!inputArgs.isEmpty() && options.rootModule != null) { 425 reportError("err.invalid.arg.for.option", "-m"); 426 } 427 if (inputArgs.isEmpty() && options.addmods.isEmpty() && options.includePattern == null 428 && options.includeSystemModulePattern == null && options.checkModuleDeps == null) { 429 if (options.help || options.version || options.fullVersion) { 430 return EXIT_OK; 431 } else { 432 showHelp(); 433 return EXIT_CMDERR; 434 } 435 } 436 if (options.genModuleInfo != null) { 437 if (options.dotOutputDir != null || options.classpath != null || options.hasFilter()) { 438 showHelp(); 439 return EXIT_CMDERR; 440 } 441 } 442 443 if (options.numFilters() > 1) { 444 reportError("err.invalid.filters"); 445 return EXIT_CMDERR; 446 } 447 448 if (options.inverse && options.depth != 1) { 449 reportError("err.invalid.inverse.option", "-R"); 450 return EXIT_CMDERR; 451 } 452 453 if (options.inverse && options.numFilters() == 0) { 454 reportError("err.invalid.filters"); 455 return EXIT_CMDERR; 456 } 457 458 if ((options.findJDKInternals) && (options.hasFilter() || options.showSummary)) { 459 showHelp(); 460 return EXIT_CMDERR; 461 } 462 if (options.showSummary && options.verbose != SUMMARY) { 463 showHelp(); 464 return EXIT_CMDERR; 465 } 466 if (options.checkModuleDeps != null && !inputArgs.isEmpty()) { 467 reportError("err.invalid.module.option", inputArgs, "-check"); 468 } 469 470 boolean ok = run(); 471 return ok ? EXIT_OK : EXIT_ERROR; 472 } catch (BadArgs|UncheckedBadArgs e) { 473 reportError(e.getKey(), e.getArgs()); 474 if (e.showUsage()) { 475 log.println(getMessage("main.usage.summary", PROGNAME)); 476 } 477 return EXIT_CMDERR; 478 } catch (ResolutionException e) { 479 reportError("err.exception.message", e.getMessage()); 480 return EXIT_CMDERR; 481 } catch (IOException e) { 482 e.printStackTrace(); 483 return EXIT_CMDERR; 484 } finally { 485 log.flush(); 486 } 487 } 488 489 boolean run() throws IOException { 490 try (JdepsConfiguration config = buildConfig()) { 491 492 // detect split packages 493 config.splitPackages().entrySet().stream() 494 .sorted(Map.Entry.comparingByKey()) 495 .forEach(e -> System.out.format("split package: %s %s%n", e.getKey(), 496 e.getValue().toString())); 497 498 // check if any module specified in -requires is missing 499 Stream.concat(options.addmods.stream(), options.requires.stream()) 500 .filter(mn -> !config.isValidToken(mn)) 501 .forEach(mn -> config.findModule(mn).orElseThrow(() -> 502 new UncheckedBadArgs(new BadArgs("err.module.not.found", mn)))); 503 504 // -genmoduleinfo 505 if (options.genModuleInfo != null) { 506 return genModuleInfo(config); 507 } 508 509 // -check 510 if (options.checkModuleDeps != null) { 511 return new ModuleAnalyzer(config, log, options.checkModuleDeps).run(); 512 } 513 514 if (options.dotOutputDir != null && 515 (options.verbose == SUMMARY || options.verbose == MODULE) && 516 !options.addmods.isEmpty() && inputArgs.isEmpty()) { 517 return new ModuleAnalyzer(config, log).genDotFiles(options.dotOutputDir); 518 } 519 520 if (options.inverse) { 521 return analyzeInverseDeps(config); 522 } else { 523 return analyzeDeps(config); 524 } 525 } 526 } 527 528 private JdepsConfiguration buildConfig() throws IOException { 529 JdepsConfiguration.Builder builder = 530 new JdepsConfiguration.Builder(options.systemModulePath); 531 532 builder.upgradeModulePath(options.upgradeModulePath) 533 .appModulePath(options.modulePath) 534 .addmods(options.addmods); 535 536 if (options.checkModuleDeps != null) { 537 // check all system modules in the image 538 builder.allModules(); 539 } 540 541 if (options.classpath != null) 542 builder.addClassPath(options.classpath); 543 544 // build the root set of archives to be analyzed 545 for (String s : inputArgs) { 546 Path p = Paths.get(s); 547 if (Files.exists(p)) { 548 builder.addRoot(p); 549 } 550 } 551 552 return builder.build(); 553 } 554 555 private boolean analyzeDeps(JdepsConfiguration config) throws IOException { 556 // output result 557 final JdepsWriter writer; 558 if (options.dotOutputDir != null) { 559 writer = new DotFileWriter(options.dotOutputDir, 560 options.verbose, 561 options.showProfile, 562 options.showModule, 563 options.showLabel); 564 } else { 565 writer = new SimpleWriter(log, 566 options.verbose, 567 options.showProfile, 568 options.showModule); 569 } 570 571 // analyze the dependencies 572 DepsAnalyzer analyzer = new DepsAnalyzer(config, 573 dependencyFilter(config), 574 writer, 575 options.verbose, 576 options.apiOnly); 577 578 boolean ok = analyzer.run(options.compileTimeView, options.depth); 579 580 // print skipped entries, if any 581 analyzer.archives() 582 .forEach(archive -> archive.reader() 583 .skippedEntries().stream() 584 .forEach(name -> warning("warn.skipped.entry", 585 name, archive.getPathName()))); 586 587 if (options.findJDKInternals && !options.nowarning) { 588 Map<String, String> jdkInternals = new TreeMap<>(); 589 Set<String> deps = analyzer.dependences(); 590 // find the ones with replacement 591 deps.forEach(cn -> replacementFor(cn).ifPresent( 592 repl -> jdkInternals.put(cn, repl)) 593 ); 594 595 if (!deps.isEmpty()) { 596 log.println(); 597 warning("warn.replace.useJDKInternals", getMessage("jdeps.wiki.url")); 598 } 599 600 if (!jdkInternals.isEmpty()) { 601 log.println(); 602 log.format("%-40s %s%n", "JDK Internal API", "Suggested Replacement"); 603 log.format("%-40s %s%n", "----------------", "---------------------"); 604 jdkInternals.entrySet().stream() 605 .forEach(e -> { 606 String key = e.getKey(); 607 String[] lines = e.getValue().split("\\n"); 608 for (String s : lines) { 609 log.format("%-40s %s%n", key, s); 610 key = ""; 611 } 612 }); 613 } 614 } 615 return ok; 616 } 617 618 private boolean analyzeInverseDeps(JdepsConfiguration config) throws IOException { 619 JdepsWriter writer = new SimpleWriter(log, 620 options.verbose, 621 options.showProfile, 622 options.showModule); 623 624 InverseDepsAnalyzer analyzer = new InverseDepsAnalyzer(config, 625 dependencyFilter(config), 626 writer, 627 options.verbose, 628 options.apiOnly); 629 boolean ok = analyzer.run(); 630 631 log.println(); 632 if (!options.requires.isEmpty()) 633 log.format("Inverse transitive dependences on %s%n", options.requires); 634 else 635 log.format("Inverse transitive dependences matching %s%n", 636 options.regex != null 637 ? options.regex.toString() 638 : "packages " + options.packageNames); 639 640 analyzer.inverseDependences().stream() 641 .sorted(Comparator.comparing(this::sortPath)) 642 .forEach(path -> log.println(path.stream() 643 .map(Archive::getName) 644 .collect(Collectors.joining(" <- ")))); 645 return ok; 646 } 647 648 private String sortPath(Deque<Archive> path) { 649 return path.peekFirst().getName(); 650 } 651 652 private boolean genModuleInfo(JdepsConfiguration config) throws IOException { 653 ModuleInfoBuilder builder 654 = new ModuleInfoBuilder(config, inputArgs, options.genModuleInfo); 655 boolean ok = builder.run(); 656 657 builder.modules().forEach(module -> { 658 if (module.packages().contains("")) { 659 reportError("ERROR: %s contains unnamed package. " + 660 "module-info.java not generated%n", module.getPathName()); 661 } 662 }); 663 664 if (!ok && !options.nowarning) { 665 log.println("ERROR: missing dependencies"); 666 builder.visitMissingDeps( 667 new Analyzer.Visitor() { 668 @Override 669 public void visitDependence(String origin, Archive originArchive, 670 String target, Archive targetArchive) { 671 if (builder.notFound(targetArchive)) 672 log.format(" %-50s -> %-50s %s%n", 673 origin, target, targetArchive.getName()); 674 } 675 }); 676 } 677 return ok; 678 } 679 680 /** 681 * Returns a filter used during dependency analysis 682 */ 683 private JdepsFilter dependencyFilter(JdepsConfiguration config) { 684 // Filter specified by -filter, -package, -regex, and -requires options 685 JdepsFilter.Builder builder = new JdepsFilter.Builder(); 686 687 // source filters 688 builder.includePattern(options.includePattern); 689 builder.includeSystemModules(options.includeSystemModulePattern); 690 691 builder.filter(options.filterSamePackage, options.filterSameArchive); 692 builder.findJDKInternals(options.findJDKInternals); 693 694 // -requires 695 if (!options.requires.isEmpty()) { 696 options.requires.stream() 697 .forEach(mn -> { 698 Module m = config.findModule(mn).get(); 699 builder.requires(mn, m.packages()); 700 }); 701 } 702 // -regex 703 if (options.regex != null) 704 builder.regex(options.regex); 705 // -package 706 if (!options.packageNames.isEmpty()) 707 builder.packages(options.packageNames); 708 // -filter 709 if (options.filterRegex != null) 710 builder.filter(options.filterRegex); 711 712 // check if system module is set 713 config.rootModules().stream() 714 .map(Module::name) 715 .forEach(builder::includeIfSystemModule); 716 717 return builder.build(); 718 } 719 720 public void handleOptions(String[] args) throws BadArgs { 721 // process options 722 for (int i=0; i < args.length; i++) { 723 if (args[i].charAt(0) == '-') { 724 String name = args[i]; 725 Option option = getOption(name); 726 String param = null; 727 if (option.hasArg) { 728 if (name.startsWith("-") && name.indexOf('=') > 0) { 729 param = name.substring(name.indexOf('=') + 1, name.length()); 730 } else if (i + 1 < args.length) { 731 param = args[++i]; 732 } 733 if (param == null || param.isEmpty() || param.charAt(0) == '-') { 734 throw new BadArgs("err.missing.arg", name).showUsage(true); 735 } 736 } 737 option.process(this, name, param); 738 if (option.ignoreRest()) { 739 i = args.length; 740 } 741 } else { 742 // process rest of the input arguments 743 for (; i < args.length; i++) { 744 String name = args[i]; 745 if (name.charAt(0) == '-') { 746 throw new BadArgs("err.option.after.class", name).showUsage(true); 747 } 748 inputArgs.add(name); 749 } 750 } 751 } 752 } 753 754 private Option getOption(String name) throws BadArgs { 755 for (Option o : recognizedOptions) { 756 if (o.matches(name)) { 757 return o; 758 } 759 } 760 throw new BadArgs("err.unknown.option", name).showUsage(true); 761 } 762 763 private void reportError(String key, Object... args) { 764 log.println(getMessage("error.prefix") + " " + getMessage(key, args)); 765 } 766 767 void warning(String key, Object... args) { 768 log.println(getMessage("warn.prefix") + " " + getMessage(key, args)); 769 } 770 771 private void showHelp() { 772 log.println(getMessage("main.usage", PROGNAME)); 773 for (Option o : recognizedOptions) { 774 String name = o.aliases[0].substring(1); // there must always be at least one name 775 name = name.charAt(0) == '-' ? name.substring(1) : name; 776 if (o.isHidden() || name.equals("h") || name.startsWith("filter:")) { 777 continue; 778 } 779 log.println(getMessage("main.opt." + name)); 780 } 781 } 782 783 private void showVersion(boolean full) { 784 log.println(version(full ? "full" : "release")); 785 } 786 787 private String version(String key) { 788 // key=version: mm.nn.oo[-milestone] 789 // key=full: mm.mm.oo[-milestone]-build 790 if (ResourceBundleHelper.versionRB == null) { 791 return System.getProperty("java.version"); 792 } 793 try { 794 return ResourceBundleHelper.versionRB.getString(key); 795 } catch (MissingResourceException e) { 796 return getMessage("version.unknown", System.getProperty("java.version")); 797 } 798 } 799 800 static String getMessage(String key, Object... args) { 801 try { 802 return MessageFormat.format(ResourceBundleHelper.bundle.getString(key), args); 803 } catch (MissingResourceException e) { 804 throw new InternalError("Missing message: " + key); 805 } 806 } 807 808 private static class Options { 809 boolean help; 810 boolean version; 811 boolean fullVersion; 812 boolean showProfile; 813 boolean showModule = true; 814 boolean showSummary; 815 boolean apiOnly; 816 boolean showLabel; 817 boolean findJDKInternals; 818 boolean nowarning = false; 819 // default is to show package-level dependencies 820 // and filter references from same package 821 Analyzer.Type verbose = PACKAGE; 822 boolean filterSamePackage = true; 823 boolean filterSameArchive = false; 824 Pattern filterRegex; 825 Path dotOutputDir; 826 Path genModuleInfo; 827 String classpath; 828 int depth = 1; 829 Set<String> requires = new HashSet<>(); 830 Set<String> packageNames = new HashSet<>(); 831 Pattern regex; // apply to the dependences 832 Pattern includePattern; 833 Pattern includeSystemModulePattern; 834 boolean inverse = false; 835 boolean compileTimeView = false; 836 Set<String> checkModuleDeps; 837 String systemModulePath = System.getProperty("java.home"); 838 String upgradeModulePath; 839 String modulePath; 840 String rootModule; 841 Set<String> addmods = new HashSet<>(); 842 843 boolean hasFilter() { 844 return numFilters() > 0; 845 } 846 847 int numFilters() { 848 int count = 0; 849 if (requires.size() > 0) count++; 850 if (regex != null) count++; 851 if (packageNames.size() > 0) count++; 852 return count; 853 } 854 } 855 856 private static class ResourceBundleHelper { 857 static final ResourceBundle versionRB; 858 static final ResourceBundle bundle; 859 static final ResourceBundle jdkinternals; 860 861 static { 862 Locale locale = Locale.getDefault(); 863 try { 864 bundle = ResourceBundle.getBundle("com.sun.tools.jdeps.resources.jdeps", locale); 865 } catch (MissingResourceException e) { 866 throw new InternalError("Cannot find jdeps resource bundle for locale " + locale); 867 } 868 try { 869 versionRB = ResourceBundle.getBundle("com.sun.tools.jdeps.resources.version"); 870 } catch (MissingResourceException e) { 871 throw new InternalError("version.resource.missing"); 872 } 873 try { 874 jdkinternals = ResourceBundle.getBundle("com.sun.tools.jdeps.resources.jdkinternals"); 875 } catch (MissingResourceException e) { 876 throw new InternalError("Cannot find jdkinternals resource bundle"); 877 } 878 } 879 } 880 881 /** 882 * Returns the recommended replacement API for the given classname; 883 * or return null if replacement API is not known. 884 */ 885 private Optional<String> replacementFor(String cn) { 886 String name = cn; 887 String value = null; 888 while (value == null && name != null) { 889 try { 890 value = ResourceBundleHelper.jdkinternals.getString(name); 891 } catch (MissingResourceException e) { 892 // go up one subpackage level 893 int i = name.lastIndexOf('.'); 894 name = i > 0 ? name.substring(0, i) : null; 895 } 896 } 897 return Optional.ofNullable(value); 898 } 899 }