340 }
341 task.command = task.listModuleDeps(CommandOption.LIST_DEPS);
342 }
343 },
344 new Option(false, CommandOption.LIST_REDUCED_DEPS) {
345 void process(JdepsTask task, String opt, String arg) throws BadArgs {
346 if (task.command != null) {
347 throw new BadArgs("err.command.set", task.command, opt);
348 }
349 task.command = task.listModuleDeps(CommandOption.LIST_REDUCED_DEPS);
350 }
351 },
352 new Option(false, CommandOption.PRINT_MODULE_DEPS) {
353 void process(JdepsTask task, String opt, String arg) throws BadArgs {
354 if (task.command != null) {
355 throw new BadArgs("err.command.set", task.command, opt);
356 }
357 task.command = task.listModuleDeps(CommandOption.PRINT_MODULE_DEPS);
358 }
359 },
360
361 // ---- Target filtering options ----
362 new Option(true, "-p", "-package", "--package") {
363 void process(JdepsTask task, String opt, String arg) {
364 task.options.packageNames.add(arg);
365 }
366 },
367 new Option(true, "-e", "-regex", "--regex") {
368 void process(JdepsTask task, String opt, String arg) {
369 task.options.regex = Pattern.compile(arg);
370 }
371 },
372 new Option(true, "--require") {
373 void process(JdepsTask task, String opt, String arg) {
374 task.options.requires.add(arg);
375 task.options.addmods.add(arg);
376 }
377 },
378 new Option(true, "-f", "-filter") {
379 void process(JdepsTask task, String opt, String arg) {
384 "-filter:archive", "-filter:module",
385 "-filter:none") {
386 void process(JdepsTask task, String opt, String arg) {
387 switch (opt) {
388 case "-filter:package":
389 task.options.filterSamePackage = true;
390 task.options.filterSameArchive = false;
391 break;
392 case "-filter:archive":
393 case "-filter:module":
394 task.options.filterSameArchive = true;
395 task.options.filterSamePackage = false;
396 break;
397 case "-filter:none":
398 task.options.filterSameArchive = false;
399 task.options.filterSamePackage = false;
400 break;
401 }
402 }
403 },
404
405 // ---- Source filtering options ----
406 new Option(true, "-include") {
407 void process(JdepsTask task, String opt, String arg) throws BadArgs {
408 task.options.includePattern = Pattern.compile(arg);
409 }
410 },
411
412 new Option(false, "-P", "-profile") {
413 void process(JdepsTask task, String opt, String arg) throws BadArgs {
414 task.options.showProfile = true;
415 }
416 },
417
418 new Option(false, "-R", "-recursive") {
419 void process(JdepsTask task, String opt, String arg) {
420 task.options.depth = 0;
421 // turn off filtering
422 task.options.filterSameArchive = false;
423 task.options.filterSamePackage = false;
594 private AnalyzeDeps analyzeDeps() throws BadArgs {
595 return options.inverse ? new InverseAnalyzeDeps()
596 : new AnalyzeDeps();
597 }
598
599 private GenDotFile genDotFile(Path dir) throws BadArgs {
600 if (Files.exists(dir) && (!Files.isDirectory(dir) || !Files.isWritable(dir))) {
601 throw new BadArgs("err.invalid.path", dir.toString());
602 }
603 return new GenDotFile(dir);
604 }
605
606 private GenModuleInfo genModuleInfo(Path dir, boolean openModule) throws BadArgs {
607 if (Files.exists(dir) && (!Files.isDirectory(dir) || !Files.isWritable(dir))) {
608 throw new BadArgs("err.invalid.path", dir.toString());
609 }
610 return new GenModuleInfo(dir, openModule);
611 }
612
613 private ListModuleDeps listModuleDeps(CommandOption option) throws BadArgs {
614 switch (option) {
615 case LIST_DEPS:
616 return new ListModuleDeps(option, true, false);
617 case LIST_REDUCED_DEPS:
618 return new ListModuleDeps(option, true, true);
619 case PRINT_MODULE_DEPS:
620 return new ListModuleDeps(option, false, true, ",");
621 default:
622 throw new IllegalArgumentException(option.toString());
623 }
624 }
625
626 private CheckModuleDeps checkModuleDeps(Set<String> mods) throws BadArgs {
627 return new CheckModuleDeps(mods);
628 }
629
630 abstract class Command {
631 final CommandOption option;
632 protected Command(CommandOption option) {
633 this.option = option;
660 return option.toString();
661 }
662 }
663
664
665 /**
666 * Analyze dependences
667 */
668 class AnalyzeDeps extends Command {
669 JdepsWriter writer;
670 AnalyzeDeps() {
671 this(CommandOption.ANALYZE_DEPS);
672 }
673
674 AnalyzeDeps(CommandOption option) {
675 super(option);
676 }
677
678 @Override
679 boolean checkOptions() {
680 if (options.findJDKInternals) {
681 // cannot set any filter, -verbose and -summary option
682 if (options.showSummary || options.verbose != null) {
683 reportError("err.invalid.options", "-summary or -verbose",
684 "-jdkinternals");
685 return false;
686 }
687 if (options.hasFilter()) {
688 reportError("err.invalid.options", "--package, --regex, --require",
689 "-jdkinternals");
690 return false;
691 }
692 }
693 if (options.showSummary) {
694 // -summary cannot use with -verbose option
695 if (options.verbose != null) {
696 reportError("err.invalid.options", "-v, -verbose", "-s, -summary");
697 return false;
698 }
699 }
700
701 if (!inputArgs.isEmpty() && !options.rootModules.isEmpty()) {
702 reportError("err.invalid.arg.for.option", "-m");
703 }
704 if (inputArgs.isEmpty() && !options.hasSourcePath()) {
705 showHelp();
706 return false;
707 }
708 return true;
709 }
710
711 /*
712 * Default is to show package-level dependencies
713 */
714 Type getAnalyzerType() {
715 if (options.showSummary)
716 return Type.SUMMARY;
717
718 if (options.findJDKInternals)
719 return Type.CLASS;
720
721 // default to package-level verbose
722 return options.verbose != null ? options.verbose : PACKAGE;
723 }
724
725 @Override
726 boolean run(JdepsConfiguration config) throws IOException {
727 Type type = getAnalyzerType();
728 // default to package-level verbose
729 JdepsWriter writer = new SimpleWriter(log,
730 type,
731 options.showProfile,
732 options.showModule);
733
734 return run(config, writer, type);
735 }
736
737 boolean run(JdepsConfiguration config, JdepsWriter writer, Type type)
738 throws IOException
908 .filter(cn -> toPackageName(cn).isEmpty())
909 .findFirst();
910
911 if (classInUnnamedPackage.isPresent()) {
912 if (classInUnnamedPackage.get().equals("module-info.class")) {
913 reportError("err.genmoduleinfo.not.jarfile", arg);
914 } else {
915 reportError("err.genmoduleinfo.unnamed.package", arg);
916 }
917 return false;
918 }
919 }
920 }
921
922 ModuleInfoBuilder builder
923 = new ModuleInfoBuilder(config, inputArgs, dir, openModule);
924 boolean ok = builder.run();
925
926 if (!ok && !options.nowarning) {
927 reportError("err.missing.dependences");
928 builder.visitMissingDeps(
929 (origin, originArchive, target, targetArchive) -> {
930 if (builder.notFound(targetArchive))
931 log.format(" %-50s -> %-50s %s%n",
932 origin, target, targetArchive.getName());
933 });
934 }
935 return ok;
936 }
937
938 private String toPackageName(String name) {
939 int i = name.lastIndexOf('/');
940 return i > 0 ? name.replace('/', '.').substring(0, i) : "";
941 }
942 }
943
944 class CheckModuleDeps extends Command {
945 final Set<String> modules;
946 CheckModuleDeps(Set<String> mods) {
947 super(CommandOption.CHECK_MODULES);
948 this.modules = mods;
949 }
950
951 @Override
952 boolean checkOptions() {
953 if (!inputArgs.isEmpty()) {
976 }
977 }
978
979 class ListModuleDeps extends Command {
980 final boolean jdkinternals;
981 final boolean reduced;
982 final String separator;
983 ListModuleDeps(CommandOption option, boolean jdkinternals, boolean reduced) {
984 this(option, jdkinternals, reduced, System.getProperty("line.separator"));
985 }
986 ListModuleDeps(CommandOption option, boolean jdkinternals, boolean reduced, String sep) {
987 super(option);
988 this.jdkinternals = jdkinternals;
989 this.reduced = reduced;
990 this.separator = sep;
991 }
992
993 @Override
994 boolean checkOptions() {
995 if (options.showSummary || options.verbose != null) {
996 reportError("err.invalid.options", "-summary or -verbose",
997 option);
998 return false;
999 }
1000 if (options.findJDKInternals) {
1001 reportError("err.invalid.options", "-jdkinternals",
1002 option);
1003 return false;
1004 }
1005
1006 if (!inputArgs.isEmpty() && !options.rootModules.isEmpty()) {
1007 reportError("err.invalid.arg.for.option", "-m");
1008 }
1009 if (inputArgs.isEmpty() && !options.hasSourcePath()) {
1010 showHelp();
1011 return false;
1012 }
1013 return true;
1014 }
1015
1016 @Override
1017 boolean run(JdepsConfiguration config) throws IOException {
1018 return new ModuleExportsAnalyzer(config,
1019 dependencyFilter(config),
1020 jdkinternals,
1021 reduced,
1022 log,
1023 separator).run();
1024 }
1025 }
1026
1027
1028 class GenDotFile extends AnalyzeDeps {
1029 final Path dotOutputDir;
1030 GenDotFile(Path dotOutputDir) {
1031 super(CommandOption.GENERATE_DOT_FILE);
1032
1033 this.dotOutputDir = dotOutputDir;
1034 }
1035
1036 @Override
1037 boolean run(JdepsConfiguration config) throws IOException {
1038 if ((options.showSummary || options.verbose == MODULE) &&
1039 !options.addmods.isEmpty() && inputArgs.isEmpty()) {
1040 // generate dot graph from the resolved graph from module
1041 // resolution. No class dependency analysis is performed.
1042 return new ModuleDotGraph(config, options.apiOnly)
1043 .genDotFiles(dotOutputDir);
1044 }
1045
1046 Type type = getAnalyzerType();
1047 JdepsWriter writer = new DotFileWriter(dotOutputDir,
1048 type,
1049 options.showProfile,
1050 options.showModule,
1051 options.showLabel);
1052 return run(config, writer, type);
1053 }
1054 }
1055
1056 /**
1057 * Returns a filter used during dependency analysis
1058 */
1059 private JdepsFilter dependencyFilter(JdepsConfiguration config) {
1060 // Filter specified by -filter, -package, -regex, and --require options
1061 JdepsFilter.Builder builder = new JdepsFilter.Builder();
1062
1063 // source filters
1064 builder.includePattern(options.includePattern);
1065
1066 // target filters
1067 builder.filter(options.filterSamePackage, options.filterSameArchive);
1068 builder.findJDKInternals(options.findJDKInternals);
1069
1070 // --require
1071 if (!options.requires.isEmpty()) {
1072 options.requires.stream()
1073 .forEach(mn -> {
1074 Module m = config.findModule(mn).get();
1075 builder.requires(mn, m.packages());
1076 });
1077 }
1078 // -regex
1079 if (options.regex != null)
1080 builder.regex(options.regex);
1081 // -package
1082 if (!options.packageNames.isEmpty())
1083 builder.packages(options.packageNames);
1084 // -filter
1085 if (options.filterRegex != null)
1086 builder.filter(options.filterRegex);
1087
1088 return builder.build();
1141
1142 private void showHelp() {
1143 log.println(getMessage("main.usage", PROGNAME));
1144 for (Option o : recognizedOptions) {
1145 String name = o.aliases[0].substring(1); // there must always be at least one name
1146 name = name.charAt(0) == '-' ? name.substring(1) : name;
1147 if (o.isHidden() || name.startsWith("filter:")) {
1148 continue;
1149 }
1150 log.println(getMessage("main.opt." + name));
1151 }
1152 }
1153
1154 private void showVersion(boolean full) {
1155 log.println(version(full ? "full" : "release"));
1156 }
1157
1158 private String version(String key) {
1159 // key=version: mm.nn.oo[-milestone]
1160 // key=full: mm.mm.oo[-milestone]-build
1161 if (ResourceBundleHelper.versionRB == null) {
1162 return System.getProperty("java.version");
1163 }
1164 try {
1165 return ResourceBundleHelper.versionRB.getString(key);
1166 } catch (MissingResourceException e) {
1167 return getMessage("version.unknown", System.getProperty("java.version"));
1168 }
1169 }
1170
1171 static String getMessage(String key, Object... args) {
1172 try {
1173 return MessageFormat.format(ResourceBundleHelper.bundle.getString(key), args);
1174 } catch (MissingResourceException e) {
1175 throw new InternalError("Missing message: " + key);
1176 }
1177 }
1178
1179 private static class Options {
1180 boolean help;
1181 boolean version;
1182 boolean fullVersion;
1183 boolean showProfile;
1184 boolean showModule = true;
1185 boolean showSummary;
1186 boolean apiOnly;
1187 boolean showLabel;
1188 boolean findJDKInternals;
1189 boolean nowarning = false;
1190 Analyzer.Type verbose;
1191 // default filter references from same package
1192 boolean filterSamePackage = true;
1193 boolean filterSameArchive = false;
1194 Pattern filterRegex;
1195 String classpath;
1196 int depth = 1;
1197 Set<String> requires = new HashSet<>();
1198 Set<String> packageNames = new HashSet<>();
1199 Pattern regex; // apply to the dependences
1200 Pattern includePattern;
1201 boolean inverse = false;
1202 boolean compileTimeView = false;
1203 String systemModulePath = System.getProperty("java.home");
1204 String upgradeModulePath;
1205 String modulePath;
1206 Set<String> rootModules = new HashSet<>();
1207 Set<String> addmods = new HashSet<>();
1208 Runtime.Version multiRelease;
1209
1210 boolean hasSourcePath() {
1211 return !addmods.isEmpty() || includePattern != null;
1212 }
1213
1214 boolean hasFilter() {
1215 return numFilters() > 0;
1216 }
1217
1218 int numFilters() {
1219 int count = 0;
1220 if (requires.size() > 0) count++;
1221 if (regex != null) count++;
1222 if (packageNames.size() > 0) count++;
1223 return count;
1224 }
1225 }
1226
1227 private static class ResourceBundleHelper {
1228 static final ResourceBundle versionRB;
1229 static final ResourceBundle bundle;
1230 static final ResourceBundle jdkinternals;
1231
1232 static {
1233 Locale locale = Locale.getDefault();
1234 try {
1235 bundle = ResourceBundle.getBundle("com.sun.tools.jdeps.resources.jdeps", locale);
1236 } catch (MissingResourceException e) {
1237 throw new InternalError("Cannot find jdeps resource bundle for locale " + locale);
1238 }
1239 try {
1240 versionRB = ResourceBundle.getBundle("com.sun.tools.jdeps.resources.version");
1241 } catch (MissingResourceException e) {
1242 throw new InternalError("version.resource.missing");
1243 }
1244 try {
1245 jdkinternals = ResourceBundle.getBundle("com.sun.tools.jdeps.resources.jdkinternals");
1246 } catch (MissingResourceException e) {
1247 throw new InternalError("Cannot find jdkinternals resource bundle");
1248 }
1249 }
1250 }
1251
1252 /**
1253 * Returns the recommended replacement API for the given classname;
1254 * or return null if replacement API is not known.
1255 */
1256 private Optional<String> replacementFor(String cn) {
1257 String name = cn;
1258 String value = null;
1259 while (value == null && name != null) {
1260 try {
1261 value = ResourceBundleHelper.jdkinternals.getString(name);
1262 } catch (MissingResourceException e) {
1263 // go up one subpackage level
1264 int i = name.lastIndexOf('.');
1265 name = i > 0 ? name.substring(0, i) : null;
1266 }
1267 }
1268 return Optional.ofNullable(value);
1269 }
1270 }
|
340 }
341 task.command = task.listModuleDeps(CommandOption.LIST_DEPS);
342 }
343 },
344 new Option(false, CommandOption.LIST_REDUCED_DEPS) {
345 void process(JdepsTask task, String opt, String arg) throws BadArgs {
346 if (task.command != null) {
347 throw new BadArgs("err.command.set", task.command, opt);
348 }
349 task.command = task.listModuleDeps(CommandOption.LIST_REDUCED_DEPS);
350 }
351 },
352 new Option(false, CommandOption.PRINT_MODULE_DEPS) {
353 void process(JdepsTask task, String opt, String arg) throws BadArgs {
354 if (task.command != null) {
355 throw new BadArgs("err.command.set", task.command, opt);
356 }
357 task.command = task.listModuleDeps(CommandOption.PRINT_MODULE_DEPS);
358 }
359 },
360 new Option(false, "--ignore-missing-deps") {
361 void process(JdepsTask task, String opt, String arg) {
362 task.options.ignoreMissingDeps = true;
363 }
364 },
365
366 // ---- Target filtering options ----
367 new Option(true, "-p", "-package", "--package") {
368 void process(JdepsTask task, String opt, String arg) {
369 task.options.packageNames.add(arg);
370 }
371 },
372 new Option(true, "-e", "-regex", "--regex") {
373 void process(JdepsTask task, String opt, String arg) {
374 task.options.regex = Pattern.compile(arg);
375 }
376 },
377 new Option(true, "--require") {
378 void process(JdepsTask task, String opt, String arg) {
379 task.options.requires.add(arg);
380 task.options.addmods.add(arg);
381 }
382 },
383 new Option(true, "-f", "-filter") {
384 void process(JdepsTask task, String opt, String arg) {
389 "-filter:archive", "-filter:module",
390 "-filter:none") {
391 void process(JdepsTask task, String opt, String arg) {
392 switch (opt) {
393 case "-filter:package":
394 task.options.filterSamePackage = true;
395 task.options.filterSameArchive = false;
396 break;
397 case "-filter:archive":
398 case "-filter:module":
399 task.options.filterSameArchive = true;
400 task.options.filterSamePackage = false;
401 break;
402 case "-filter:none":
403 task.options.filterSameArchive = false;
404 task.options.filterSamePackage = false;
405 break;
406 }
407 }
408 },
409 new Option(false, "--missing-deps") {
410 void process(JdepsTask task, String opt, String arg) {
411 task.options.findMissingDeps = true;
412 }
413 },
414
415 // ---- Source filtering options ----
416 new Option(true, "-include") {
417 void process(JdepsTask task, String opt, String arg) throws BadArgs {
418 task.options.includePattern = Pattern.compile(arg);
419 }
420 },
421
422 new Option(false, "-P", "-profile") {
423 void process(JdepsTask task, String opt, String arg) throws BadArgs {
424 task.options.showProfile = true;
425 }
426 },
427
428 new Option(false, "-R", "-recursive") {
429 void process(JdepsTask task, String opt, String arg) {
430 task.options.depth = 0;
431 // turn off filtering
432 task.options.filterSameArchive = false;
433 task.options.filterSamePackage = false;
604 private AnalyzeDeps analyzeDeps() throws BadArgs {
605 return options.inverse ? new InverseAnalyzeDeps()
606 : new AnalyzeDeps();
607 }
608
609 private GenDotFile genDotFile(Path dir) throws BadArgs {
610 if (Files.exists(dir) && (!Files.isDirectory(dir) || !Files.isWritable(dir))) {
611 throw new BadArgs("err.invalid.path", dir.toString());
612 }
613 return new GenDotFile(dir);
614 }
615
616 private GenModuleInfo genModuleInfo(Path dir, boolean openModule) throws BadArgs {
617 if (Files.exists(dir) && (!Files.isDirectory(dir) || !Files.isWritable(dir))) {
618 throw new BadArgs("err.invalid.path", dir.toString());
619 }
620 return new GenModuleInfo(dir, openModule);
621 }
622
623 private ListModuleDeps listModuleDeps(CommandOption option) throws BadArgs {
624 // no need to record the dependences on the same archive or same package
625 options.filterSameArchive = true;
626 options.filterSamePackage = true;
627 // do transitive dependence analysis
628 options.depth = 0;
629 switch (option) {
630 case LIST_DEPS:
631 return new ListModuleDeps(option, true, false);
632 case LIST_REDUCED_DEPS:
633 return new ListModuleDeps(option, true, true);
634 case PRINT_MODULE_DEPS:
635 return new ListModuleDeps(option, false, true, ",");
636 default:
637 throw new IllegalArgumentException(option.toString());
638 }
639 }
640
641 private CheckModuleDeps checkModuleDeps(Set<String> mods) throws BadArgs {
642 return new CheckModuleDeps(mods);
643 }
644
645 abstract class Command {
646 final CommandOption option;
647 protected Command(CommandOption option) {
648 this.option = option;
675 return option.toString();
676 }
677 }
678
679
680 /**
681 * Analyze dependences
682 */
683 class AnalyzeDeps extends Command {
684 JdepsWriter writer;
685 AnalyzeDeps() {
686 this(CommandOption.ANALYZE_DEPS);
687 }
688
689 AnalyzeDeps(CommandOption option) {
690 super(option);
691 }
692
693 @Override
694 boolean checkOptions() {
695 if (options.findJDKInternals || options.findMissingDeps) {
696 // cannot set any filter, -verbose and -summary option
697 if (options.showSummary || options.verbose != null) {
698 reportError("err.invalid.options", "-summary or -verbose",
699 options.findJDKInternals ? "-jdkinternals" : "--missing-deps");
700 return false;
701 }
702 if (options.hasFilter()) {
703 reportError("err.invalid.options", "--package, --regex, --require",
704 options.findJDKInternals ? "-jdkinternals" : "--missing-deps");
705 return false;
706 }
707 }
708 if (options.showSummary) {
709 // -summary cannot use with -verbose option
710 if (options.verbose != null) {
711 reportError("err.invalid.options", "-v, -verbose", "-s, -summary");
712 return false;
713 }
714 }
715
716 if (!inputArgs.isEmpty() && !options.rootModules.isEmpty()) {
717 reportError("err.invalid.arg.for.option", "-m");
718 }
719 if (inputArgs.isEmpty() && !options.hasSourcePath()) {
720 showHelp();
721 return false;
722 }
723 return true;
724 }
725
726 /*
727 * Default is to show package-level dependencies
728 */
729 Type getAnalyzerType() {
730 if (options.showSummary)
731 return Type.SUMMARY;
732
733 if (options.findJDKInternals || options.findMissingDeps)
734 return Type.CLASS;
735
736 // default to package-level verbose
737 return options.verbose != null ? options.verbose : PACKAGE;
738 }
739
740 @Override
741 boolean run(JdepsConfiguration config) throws IOException {
742 Type type = getAnalyzerType();
743 // default to package-level verbose
744 JdepsWriter writer = new SimpleWriter(log,
745 type,
746 options.showProfile,
747 options.showModule);
748
749 return run(config, writer, type);
750 }
751
752 boolean run(JdepsConfiguration config, JdepsWriter writer, Type type)
753 throws IOException
923 .filter(cn -> toPackageName(cn).isEmpty())
924 .findFirst();
925
926 if (classInUnnamedPackage.isPresent()) {
927 if (classInUnnamedPackage.get().equals("module-info.class")) {
928 reportError("err.genmoduleinfo.not.jarfile", arg);
929 } else {
930 reportError("err.genmoduleinfo.unnamed.package", arg);
931 }
932 return false;
933 }
934 }
935 }
936
937 ModuleInfoBuilder builder
938 = new ModuleInfoBuilder(config, inputArgs, dir, openModule);
939 boolean ok = builder.run();
940
941 if (!ok && !options.nowarning) {
942 reportError("err.missing.dependences");
943 builder.visitMissingDeps(new SimpleDepVisitor());
944 }
945 return ok;
946 }
947
948 private String toPackageName(String name) {
949 int i = name.lastIndexOf('/');
950 return i > 0 ? name.replace('/', '.').substring(0, i) : "";
951 }
952 }
953
954 class CheckModuleDeps extends Command {
955 final Set<String> modules;
956 CheckModuleDeps(Set<String> mods) {
957 super(CommandOption.CHECK_MODULES);
958 this.modules = mods;
959 }
960
961 @Override
962 boolean checkOptions() {
963 if (!inputArgs.isEmpty()) {
986 }
987 }
988
989 class ListModuleDeps extends Command {
990 final boolean jdkinternals;
991 final boolean reduced;
992 final String separator;
993 ListModuleDeps(CommandOption option, boolean jdkinternals, boolean reduced) {
994 this(option, jdkinternals, reduced, System.getProperty("line.separator"));
995 }
996 ListModuleDeps(CommandOption option, boolean jdkinternals, boolean reduced, String sep) {
997 super(option);
998 this.jdkinternals = jdkinternals;
999 this.reduced = reduced;
1000 this.separator = sep;
1001 }
1002
1003 @Override
1004 boolean checkOptions() {
1005 if (options.showSummary || options.verbose != null) {
1006 reportError("err.invalid.options", "-summary or -verbose", option);
1007 return false;
1008 }
1009 if (options.findJDKInternals) {
1010 reportError("err.invalid.options", "-jdkinternals", option);
1011 return false;
1012 }
1013 if (options.findMissingDeps) {
1014 reportError("err.invalid.options", "--missing-deps", option);
1015 return false;
1016 }
1017
1018 if (!inputArgs.isEmpty() && !options.rootModules.isEmpty()) {
1019 reportError("err.invalid.arg.for.option", "-m");
1020 }
1021 if (inputArgs.isEmpty() && !options.hasSourcePath()) {
1022 showHelp();
1023 return false;
1024 }
1025 return true;
1026 }
1027
1028 @Override
1029 boolean run(JdepsConfiguration config) throws IOException {
1030 ModuleExportsAnalyzer analyzer = new ModuleExportsAnalyzer(config,
1031 dependencyFilter(config),
1032 jdkinternals,
1033 reduced,
1034 log,
1035 separator);
1036 boolean ok = analyzer.run(options.depth, options.ignoreMissingDeps);
1037 if (!ok) {
1038 reportError("err.cant.list.module.deps");
1039 log.println();
1040 analyzer.visitMissingDeps(new SimpleDepVisitor());
1041 }
1042 return ok;
1043 }
1044 }
1045
1046 class GenDotFile extends AnalyzeDeps {
1047 final Path dotOutputDir;
1048 GenDotFile(Path dotOutputDir) {
1049 super(CommandOption.GENERATE_DOT_FILE);
1050
1051 this.dotOutputDir = dotOutputDir;
1052 }
1053
1054 @Override
1055 boolean run(JdepsConfiguration config) throws IOException {
1056 if ((options.showSummary || options.verbose == MODULE) &&
1057 !options.addmods.isEmpty() && inputArgs.isEmpty()) {
1058 // generate dot graph from the resolved graph from module
1059 // resolution. No class dependency analysis is performed.
1060 return new ModuleDotGraph(config, options.apiOnly)
1061 .genDotFiles(dotOutputDir);
1062 }
1063
1064 Type type = getAnalyzerType();
1065 JdepsWriter writer = new DotFileWriter(dotOutputDir,
1066 type,
1067 options.showProfile,
1068 options.showModule,
1069 options.showLabel);
1070 return run(config, writer, type);
1071 }
1072 }
1073
1074 class SimpleDepVisitor implements Analyzer.Visitor {
1075 private Archive source;
1076 @Override
1077 public void visitDependence(String origin, Archive originArchive, String target, Archive targetArchive) {
1078 if (source != originArchive) {
1079 source = originArchive;
1080 log.format("%s%n", originArchive);
1081 }
1082 log.format(" %-50s -> %-50s %s%n", origin, target, targetArchive.getName());
1083 }
1084 }
1085
1086 /**
1087 * Returns a filter used during dependency analysis
1088 */
1089 private JdepsFilter dependencyFilter(JdepsConfiguration config) {
1090 // Filter specified by -filter, -package, -regex, and --require options
1091 JdepsFilter.Builder builder = new JdepsFilter.Builder();
1092
1093 // source filters
1094 builder.includePattern(options.includePattern);
1095
1096 // target filters
1097 builder.filter(options.filterSamePackage, options.filterSameArchive);
1098 builder.findJDKInternals(options.findJDKInternals);
1099 builder.findMissingDeps(options.findMissingDeps);
1100
1101 // --require
1102 if (!options.requires.isEmpty()) {
1103 options.requires.stream()
1104 .forEach(mn -> {
1105 Module m = config.findModule(mn).get();
1106 builder.requires(mn, m.packages());
1107 });
1108 }
1109 // -regex
1110 if (options.regex != null)
1111 builder.regex(options.regex);
1112 // -package
1113 if (!options.packageNames.isEmpty())
1114 builder.packages(options.packageNames);
1115 // -filter
1116 if (options.filterRegex != null)
1117 builder.filter(options.filterRegex);
1118
1119 return builder.build();
1172
1173 private void showHelp() {
1174 log.println(getMessage("main.usage", PROGNAME));
1175 for (Option o : recognizedOptions) {
1176 String name = o.aliases[0].substring(1); // there must always be at least one name
1177 name = name.charAt(0) == '-' ? name.substring(1) : name;
1178 if (o.isHidden() || name.startsWith("filter:")) {
1179 continue;
1180 }
1181 log.println(getMessage("main.opt." + name));
1182 }
1183 }
1184
1185 private void showVersion(boolean full) {
1186 log.println(version(full ? "full" : "release"));
1187 }
1188
1189 private String version(String key) {
1190 // key=version: mm.nn.oo[-milestone]
1191 // key=full: mm.mm.oo[-milestone]-build
1192 try {
1193 return ResourceBundleHelper.getVersion(key);
1194 } catch (MissingResourceException e) {
1195 return getMessage("version.unknown", System.getProperty("java.version"));
1196 }
1197 }
1198
1199 static String getMessage(String key, Object... args) {
1200 try {
1201 return MessageFormat.format(ResourceBundleHelper.getMessage(key), args);
1202 } catch (MissingResourceException e) {
1203 throw new InternalError("Missing message: " + key);
1204 }
1205 }
1206
1207 private static class Options {
1208 boolean help;
1209 boolean version;
1210 boolean fullVersion;
1211 boolean showProfile;
1212 boolean showModule = true;
1213 boolean showSummary;
1214 boolean apiOnly;
1215 boolean showLabel;
1216 boolean findJDKInternals;
1217 boolean findMissingDeps;
1218 boolean ignoreMissingDeps;
1219 boolean nowarning = false;
1220 Analyzer.Type verbose;
1221 // default filter references from same package
1222 boolean filterSamePackage = true;
1223 boolean filterSameArchive = false;
1224 Pattern filterRegex;
1225 String classpath;
1226 int depth = 1;
1227 Set<String> requires = new HashSet<>();
1228 Set<String> packageNames = new HashSet<>();
1229 Pattern regex; // apply to the dependences
1230 Pattern includePattern;
1231 boolean inverse = false;
1232 boolean compileTimeView = false;
1233 String systemModulePath = System.getProperty("java.home");
1234 String upgradeModulePath;
1235 String modulePath;
1236 Set<String> rootModules = new HashSet<>();
1237 Set<String> addmods = new HashSet<>();
1238 Runtime.Version multiRelease;
1239
1240 boolean hasSourcePath() {
1241 return !addmods.isEmpty() || includePattern != null;
1242 }
1243
1244 boolean hasFilter() {
1245 return numFilters() > 0;
1246 }
1247
1248 int numFilters() {
1249 int count = 0;
1250 if (requires.size() > 0) count++;
1251 if (regex != null) count++;
1252 if (packageNames.size() > 0) count++;
1253 return count;
1254 }
1255 }
1256
1257 private static class ResourceBundleHelper {
1258 static final String LS = System.lineSeparator();
1259 static final ResourceBundle versionRB;
1260 static final ResourceBundle bundle;
1261 static final ResourceBundle jdkinternals;
1262
1263 static {
1264 Locale locale = Locale.getDefault();
1265 try {
1266 bundle = ResourceBundle.getBundle("com.sun.tools.jdeps.resources.jdeps", locale);
1267 } catch (MissingResourceException e) {
1268 throw new InternalError("Cannot find jdeps resource bundle for locale " + locale);
1269 }
1270 try {
1271 versionRB = ResourceBundle.getBundle("com.sun.tools.jdeps.resources.version");
1272 } catch (MissingResourceException e) {
1273 throw new InternalError("version.resource.missing");
1274 }
1275 try {
1276 jdkinternals = ResourceBundle.getBundle("com.sun.tools.jdeps.resources.jdkinternals");
1277 } catch (MissingResourceException e) {
1278 throw new InternalError("Cannot find jdkinternals resource bundle");
1279 }
1280 }
1281
1282 static String getMessage(String key) {
1283 return bundle.getString(key).replace("\n", LS);
1284 }
1285
1286 static String getVersion(String key) {
1287 if (ResourceBundleHelper.versionRB == null) {
1288 return System.getProperty("java.version");
1289 }
1290 return versionRB.getString(key).replace("\n", LS);
1291 }
1292
1293 static String getSuggestedReplacement(String key) {
1294 return ResourceBundleHelper.jdkinternals.getString(key).replace("\n", LS);
1295 }
1296 }
1297
1298 /**
1299 * Returns the recommended replacement API for the given classname;
1300 * or return null if replacement API is not known.
1301 */
1302 private Optional<String> replacementFor(String cn) {
1303 String name = cn;
1304 String value = null;
1305 while (value == null && name != null) {
1306 try {
1307 value = ResourceBundleHelper.getSuggestedReplacement(name);
1308 } catch (MissingResourceException e) {
1309 // go up one subpackage level
1310 int i = name.lastIndexOf('.');
1311 name = i > 0 ? name.substring(0, i) : null;
1312 }
1313 }
1314 return Optional.ofNullable(value);
1315 }
1316 }
|