src/share/classes/com/sun/tools/jdeps/JdepsTask.java

Thu, 27 Apr 2017 16:18:18 -0700

author
bchristi
date
Thu, 27 Apr 2017 16:18:18 -0700
changeset 3389
e2abef6f10b9
parent 3368
f206126308bc
child 3446
e468915bad3a
permissions
-rw-r--r--

8176329: jdeps to detect MR jar file and output a warning
Reviewed-by: mchung

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

mercurial