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

Fri, 28 Dec 2012 22:25:21 -0800

author
mchung
date
Fri, 28 Dec 2012 22:25:21 -0800
changeset 1472
0c244701188e
child 1577
88286a36bb34
permissions
-rw-r--r--

8003562: Provide a CLI tool to analyze class dependencies
Reviewed-by: jjg, alanb, ulfzibis, erikj

mchung@1472 1 /*
mchung@1472 2 * Copyright (c) 2012, 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@1472 27 import com.sun.tools.classfile.ClassFile;
mchung@1472 28 import com.sun.tools.classfile.ConstantPoolException;
mchung@1472 29 import com.sun.tools.classfile.Dependencies;
mchung@1472 30 import com.sun.tools.classfile.Dependencies.ClassFileError;
mchung@1472 31 import com.sun.tools.classfile.Dependency;
mchung@1472 32 import com.sun.tools.classfile.Dependency.Location;
mchung@1472 33 import java.io.*;
mchung@1472 34 import java.text.MessageFormat;
mchung@1472 35 import java.util.*;
mchung@1472 36 import java.util.regex.Pattern;
mchung@1472 37
mchung@1472 38 /**
mchung@1472 39 * Implementation for the jdeps tool for static class dependency analysis.
mchung@1472 40 */
mchung@1472 41 class JdepsTask {
mchung@1472 42 class BadArgs extends Exception {
mchung@1472 43 static final long serialVersionUID = 8765093759964640721L;
mchung@1472 44 BadArgs(String key, Object... args) {
mchung@1472 45 super(JdepsTask.this.getMessage(key, args));
mchung@1472 46 this.key = key;
mchung@1472 47 this.args = args;
mchung@1472 48 }
mchung@1472 49
mchung@1472 50 BadArgs showUsage(boolean b) {
mchung@1472 51 showUsage = b;
mchung@1472 52 return this;
mchung@1472 53 }
mchung@1472 54 final String key;
mchung@1472 55 final Object[] args;
mchung@1472 56 boolean showUsage;
mchung@1472 57 }
mchung@1472 58
mchung@1472 59 static abstract class Option {
mchung@1472 60 Option(boolean hasArg, String... aliases) {
mchung@1472 61 this.hasArg = hasArg;
mchung@1472 62 this.aliases = aliases;
mchung@1472 63 }
mchung@1472 64
mchung@1472 65 boolean isHidden() {
mchung@1472 66 return false;
mchung@1472 67 }
mchung@1472 68
mchung@1472 69 boolean matches(String opt) {
mchung@1472 70 for (String a : aliases) {
mchung@1472 71 if (a.equals(opt)) {
mchung@1472 72 return true;
mchung@1472 73 } else if (opt.startsWith("--") && hasArg && opt.startsWith(a + "=")) {
mchung@1472 74 return true;
mchung@1472 75 }
mchung@1472 76 }
mchung@1472 77 return false;
mchung@1472 78 }
mchung@1472 79
mchung@1472 80 boolean ignoreRest() {
mchung@1472 81 return false;
mchung@1472 82 }
mchung@1472 83
mchung@1472 84 abstract void process(JdepsTask task, String opt, String arg) throws BadArgs;
mchung@1472 85 final boolean hasArg;
mchung@1472 86 final String[] aliases;
mchung@1472 87 }
mchung@1472 88
mchung@1472 89 static abstract class HiddenOption extends Option {
mchung@1472 90 HiddenOption(boolean hasArg, String... aliases) {
mchung@1472 91 super(hasArg, aliases);
mchung@1472 92 }
mchung@1472 93
mchung@1472 94 boolean isHidden() {
mchung@1472 95 return true;
mchung@1472 96 }
mchung@1472 97 }
mchung@1472 98
mchung@1472 99 static Option[] recognizedOptions = {
mchung@1472 100 new Option(false, "-h", "-?", "--help") {
mchung@1472 101 void process(JdepsTask task, String opt, String arg) {
mchung@1472 102 task.options.help = true;
mchung@1472 103 }
mchung@1472 104 },
mchung@1472 105 new Option(false, "-s", "--summary") {
mchung@1472 106 void process(JdepsTask task, String opt, String arg) {
mchung@1472 107 task.options.showSummary = true;
mchung@1472 108 task.options.verbose = Options.Verbose.SUMMARY;
mchung@1472 109 }
mchung@1472 110 },
mchung@1472 111 new Option(false, "-v", "--verbose") {
mchung@1472 112 void process(JdepsTask task, String opt, String arg) {
mchung@1472 113 task.options.verbose = Options.Verbose.VERBOSE;
mchung@1472 114 }
mchung@1472 115 },
mchung@1472 116 new Option(true, "-V", "--verbose-level") {
mchung@1472 117 void process(JdepsTask task, String opt, String arg) throws BadArgs {
mchung@1472 118 switch (arg) {
mchung@1472 119 case "package":
mchung@1472 120 task.options.verbose = Options.Verbose.PACKAGE;
mchung@1472 121 break;
mchung@1472 122 case "class":
mchung@1472 123 task.options.verbose = Options.Verbose.CLASS;
mchung@1472 124 break;
mchung@1472 125 default:
mchung@1472 126 throw task.new BadArgs("err.invalid.arg.for.option", opt);
mchung@1472 127 }
mchung@1472 128 }
mchung@1472 129 },
mchung@1472 130 new Option(true, "-c", "--classpath") {
mchung@1472 131 void process(JdepsTask task, String opt, String arg) {
mchung@1472 132 task.options.classpath = arg;
mchung@1472 133 }
mchung@1472 134 },
mchung@1472 135 new Option(true, "-p", "--package") {
mchung@1472 136 void process(JdepsTask task, String opt, String arg) {
mchung@1472 137 task.options.packageNames.add(arg);
mchung@1472 138 }
mchung@1472 139 },
mchung@1472 140 new Option(true, "-e", "--regex") {
mchung@1472 141 void process(JdepsTask task, String opt, String arg) {
mchung@1472 142 task.options.regex = arg;
mchung@1472 143 }
mchung@1472 144 },
mchung@1472 145 new Option(false, "-P", "--profile") {
mchung@1472 146 void process(JdepsTask task, String opt, String arg) {
mchung@1472 147 task.options.showProfile = true;
mchung@1472 148 }
mchung@1472 149 },
mchung@1472 150 new Option(false, "-R", "--recursive") {
mchung@1472 151 void process(JdepsTask task, String opt, String arg) {
mchung@1472 152 task.options.depth = 0;
mchung@1472 153 }
mchung@1472 154 },
mchung@1472 155 new HiddenOption(true, "-d", "--depth") {
mchung@1472 156 void process(JdepsTask task, String opt, String arg) throws BadArgs {
mchung@1472 157 try {
mchung@1472 158 task.options.depth = Integer.parseInt(arg);
mchung@1472 159 } catch (NumberFormatException e) {
mchung@1472 160 throw task.new BadArgs("err.invalid.arg.for.option", opt);
mchung@1472 161 }
mchung@1472 162 }
mchung@1472 163 },
mchung@1472 164 new Option(false, "--version") {
mchung@1472 165 void process(JdepsTask task, String opt, String arg) {
mchung@1472 166 task.options.version = true;
mchung@1472 167 }
mchung@1472 168 },
mchung@1472 169 new HiddenOption(false, "--fullversion") {
mchung@1472 170 void process(JdepsTask task, String opt, String arg) {
mchung@1472 171 task.options.fullVersion = true;
mchung@1472 172 }
mchung@1472 173 },
mchung@1472 174
mchung@1472 175 };
mchung@1472 176
mchung@1472 177 private static final String PROGNAME = "jdeps";
mchung@1472 178 private final Options options = new Options();
mchung@1472 179 private final List<String> classes = new ArrayList<String>();
mchung@1472 180
mchung@1472 181 private PrintWriter log;
mchung@1472 182 void setLog(PrintWriter out) {
mchung@1472 183 log = out;
mchung@1472 184 }
mchung@1472 185
mchung@1472 186 /**
mchung@1472 187 * Result codes.
mchung@1472 188 */
mchung@1472 189 static final int EXIT_OK = 0, // Completed with no errors.
mchung@1472 190 EXIT_ERROR = 1, // Completed but reported errors.
mchung@1472 191 EXIT_CMDERR = 2, // Bad command-line arguments
mchung@1472 192 EXIT_SYSERR = 3, // System error or resource exhaustion.
mchung@1472 193 EXIT_ABNORMAL = 4;// terminated abnormally
mchung@1472 194
mchung@1472 195 int run(String[] args) {
mchung@1472 196 if (log == null) {
mchung@1472 197 log = new PrintWriter(System.out);
mchung@1472 198 }
mchung@1472 199 try {
mchung@1472 200 handleOptions(args);
mchung@1472 201 if (options.help) {
mchung@1472 202 showHelp();
mchung@1472 203 }
mchung@1472 204 if (options.version || options.fullVersion) {
mchung@1472 205 showVersion(options.fullVersion);
mchung@1472 206 }
mchung@1472 207 if (classes.isEmpty() && !options.wildcard) {
mchung@1472 208 if (options.help || options.version || options.fullVersion) {
mchung@1472 209 return EXIT_OK;
mchung@1472 210 } else {
mchung@1472 211 showHelp();
mchung@1472 212 return EXIT_CMDERR;
mchung@1472 213 }
mchung@1472 214 }
mchung@1472 215 if (options.regex != null && options.packageNames.size() > 0) {
mchung@1472 216 showHelp();
mchung@1472 217 return EXIT_CMDERR;
mchung@1472 218 }
mchung@1472 219 if (options.showSummary && options.verbose != Options.Verbose.SUMMARY) {
mchung@1472 220 showHelp();
mchung@1472 221 return EXIT_CMDERR;
mchung@1472 222 }
mchung@1472 223 boolean ok = run();
mchung@1472 224 return ok ? EXIT_OK : EXIT_ERROR;
mchung@1472 225 } catch (BadArgs e) {
mchung@1472 226 reportError(e.key, e.args);
mchung@1472 227 if (e.showUsage) {
mchung@1472 228 log.println(getMessage("main.usage.summary", PROGNAME));
mchung@1472 229 }
mchung@1472 230 return EXIT_CMDERR;
mchung@1472 231 } catch (IOException e) {
mchung@1472 232 return EXIT_ABNORMAL;
mchung@1472 233 } finally {
mchung@1472 234 log.flush();
mchung@1472 235 }
mchung@1472 236 }
mchung@1472 237
mchung@1472 238 private final List<Archive> sourceLocations = new ArrayList<Archive>();
mchung@1472 239 private final Archive NOT_FOUND = new Archive(getMessage("artifact.not.found"));
mchung@1472 240 private boolean run() throws IOException {
mchung@1472 241 findDependencies();
mchung@1472 242 switch (options.verbose) {
mchung@1472 243 case VERBOSE:
mchung@1472 244 case CLASS:
mchung@1472 245 printClassDeps(log);
mchung@1472 246 break;
mchung@1472 247 case PACKAGE:
mchung@1472 248 printPackageDeps(log);
mchung@1472 249 break;
mchung@1472 250 case SUMMARY:
mchung@1472 251 for (Archive origin : sourceLocations) {
mchung@1472 252 for (Archive target : origin.getRequiredArchives()) {
mchung@1472 253 log.format("%-30s -> %s%n", origin, target);
mchung@1472 254 }
mchung@1472 255 }
mchung@1472 256 break;
mchung@1472 257 default:
mchung@1472 258 throw new InternalError("Should not reach here");
mchung@1472 259 }
mchung@1472 260 return true;
mchung@1472 261 }
mchung@1472 262
mchung@1472 263 private boolean isValidClassName(String name) {
mchung@1472 264 if (!Character.isJavaIdentifierStart(name.charAt(0))) {
mchung@1472 265 return false;
mchung@1472 266 }
mchung@1472 267 for (int i=1; i < name.length(); i++) {
mchung@1472 268 char c = name.charAt(i);
mchung@1472 269 if (c != '.' && !Character.isJavaIdentifierPart(c)) {
mchung@1472 270 return false;
mchung@1472 271 }
mchung@1472 272 }
mchung@1472 273 return true;
mchung@1472 274 }
mchung@1472 275
mchung@1472 276 private void findDependencies() throws IOException {
mchung@1472 277 Dependency.Finder finder = Dependencies.getClassDependencyFinder();
mchung@1472 278 Dependency.Filter filter;
mchung@1472 279 if (options.regex != null) {
mchung@1472 280 filter = Dependencies.getRegexFilter(Pattern.compile(options.regex));
mchung@1472 281 } else if (options.packageNames.size() > 0) {
mchung@1472 282 filter = Dependencies.getPackageFilter(options.packageNames, false);
mchung@1472 283 } else {
mchung@1472 284 filter = new Dependency.Filter() {
mchung@1472 285 public boolean accepts(Dependency dependency) {
mchung@1472 286 return !dependency.getOrigin().equals(dependency.getTarget());
mchung@1472 287 }
mchung@1472 288 };
mchung@1472 289 }
mchung@1472 290
mchung@1472 291 List<Archive> archives = new ArrayList<Archive>();
mchung@1472 292 Deque<String> roots = new LinkedList<String>();
mchung@1472 293 for (String s : classes) {
mchung@1472 294 File f = new File(s);
mchung@1472 295 if (f.exists()) {
mchung@1472 296 archives.add(new Archive(f, ClassFileReader.newInstance(f)));
mchung@1472 297 } else {
mchung@1472 298 if (isValidClassName(s)) {
mchung@1472 299 roots.add(s);
mchung@1472 300 } else {
mchung@1472 301 warning("warn.invalid.arg", s);
mchung@1472 302 }
mchung@1472 303 }
mchung@1472 304 }
mchung@1472 305
mchung@1472 306 List<Archive> classpaths = new ArrayList<Archive>(); // for class file lookup
mchung@1472 307 if (options.wildcard) {
mchung@1472 308 // include all archives from classpath to the initial list
mchung@1472 309 archives.addAll(getClassPathArchives(options.classpath));
mchung@1472 310 } else {
mchung@1472 311 classpaths.addAll(getClassPathArchives(options.classpath));
mchung@1472 312 }
mchung@1472 313 classpaths.addAll(PlatformClassPath.getArchives());
mchung@1472 314
mchung@1472 315 // add all archives to the source locations for reporting
mchung@1472 316 sourceLocations.addAll(archives);
mchung@1472 317 sourceLocations.addAll(classpaths);
mchung@1472 318
mchung@1472 319 // Work queue of names of classfiles to be searched.
mchung@1472 320 // Entries will be unique, and for classes that do not yet have
mchung@1472 321 // dependencies in the results map.
mchung@1472 322 Deque<String> deque = new LinkedList<String>();
mchung@1472 323 Set<String> doneClasses = new HashSet<String>();
mchung@1472 324
mchung@1472 325 // get the immediate dependencies of the input files
mchung@1472 326 for (Archive a : archives) {
mchung@1472 327 for (ClassFile cf : a.reader().getClassFiles()) {
mchung@1472 328 String classFileName;
mchung@1472 329 try {
mchung@1472 330 classFileName = cf.getName();
mchung@1472 331 } catch (ConstantPoolException e) {
mchung@1472 332 throw new ClassFileError(e);
mchung@1472 333 }
mchung@1472 334 a.addClass(classFileName);
mchung@1472 335 if (!doneClasses.contains(classFileName)) {
mchung@1472 336 doneClasses.add(classFileName);
mchung@1472 337 }
mchung@1472 338 for (Dependency d : finder.findDependencies(cf)) {
mchung@1472 339 if (filter.accepts(d)) {
mchung@1472 340 String cn = d.getTarget().getName();
mchung@1472 341 if (!doneClasses.contains(cn) && !deque.contains(cn)) {
mchung@1472 342 deque.add(cn);
mchung@1472 343 }
mchung@1472 344 a.addDependency(d);
mchung@1472 345 }
mchung@1472 346 }
mchung@1472 347 }
mchung@1472 348 }
mchung@1472 349
mchung@1472 350 // add Archive for looking up classes from the classpath
mchung@1472 351 // for transitive dependency analysis
mchung@1472 352 Deque<String> unresolved = roots;
mchung@1472 353 int depth = options.depth > 0 ? options.depth : Integer.MAX_VALUE;
mchung@1472 354 do {
mchung@1472 355 String name;
mchung@1472 356 while ((name = unresolved.poll()) != null) {
mchung@1472 357 if (doneClasses.contains(name)) {
mchung@1472 358 continue;
mchung@1472 359 }
mchung@1472 360 ClassFile cf = null;
mchung@1472 361 for (Archive a : classpaths) {
mchung@1472 362 cf = a.reader().getClassFile(name);
mchung@1472 363 if (cf != null) {
mchung@1472 364 String classFileName;
mchung@1472 365 try {
mchung@1472 366 classFileName = cf.getName();
mchung@1472 367 } catch (ConstantPoolException e) {
mchung@1472 368 throw new ClassFileError(e);
mchung@1472 369 }
mchung@1472 370 a.addClass(classFileName);
mchung@1472 371 if (!doneClasses.contains(classFileName)) {
mchung@1472 372 // if name is a fully-qualified class name specified
mchung@1472 373 // from command-line, this class might already be parsed
mchung@1472 374 doneClasses.add(classFileName);
mchung@1472 375 if (depth > 0) {
mchung@1472 376 for (Dependency d : finder.findDependencies(cf)) {
mchung@1472 377 if (filter.accepts(d)) {
mchung@1472 378 String cn = d.getTarget().getName();
mchung@1472 379 if (!doneClasses.contains(cn) && !deque.contains(cn)) {
mchung@1472 380 deque.add(cn);
mchung@1472 381 }
mchung@1472 382 a.addDependency(d);
mchung@1472 383 }
mchung@1472 384 }
mchung@1472 385 }
mchung@1472 386 }
mchung@1472 387 break;
mchung@1472 388 }
mchung@1472 389 }
mchung@1472 390 if (cf == null) {
mchung@1472 391 NOT_FOUND.addClass(name);
mchung@1472 392 }
mchung@1472 393 }
mchung@1472 394 unresolved = deque;
mchung@1472 395 deque = new LinkedList<String>();
mchung@1472 396 } while (!unresolved.isEmpty() && depth-- > 0);
mchung@1472 397 }
mchung@1472 398
mchung@1472 399 private void printPackageDeps(PrintWriter out) {
mchung@1472 400 for (Archive source : sourceLocations) {
mchung@1472 401 SortedMap<Location, SortedSet<Location>> deps = source.getDependencies();
mchung@1472 402 if (deps.isEmpty())
mchung@1472 403 continue;
mchung@1472 404
mchung@1472 405 for (Archive target : source.getRequiredArchives()) {
mchung@1472 406 out.format("%s -> %s%n", source, target);
mchung@1472 407 }
mchung@1472 408
mchung@1472 409 Map<String, Archive> pkgs = new TreeMap<String, Archive>();
mchung@1472 410 SortedMap<String, Archive> targets = new TreeMap<String, Archive>();
mchung@1472 411 String pkg = "";
mchung@1472 412 for (Map.Entry<Location, SortedSet<Location>> e : deps.entrySet()) {
mchung@1472 413 String cn = e.getKey().getClassName();
mchung@1472 414 String p = packageOf(e.getKey());
mchung@1472 415 Archive origin = Archive.find(e.getKey());
mchung@1472 416 assert origin != null;
mchung@1472 417 if (!pkgs.containsKey(p)) {
mchung@1472 418 pkgs.put(p, origin);
mchung@1472 419 } else if (pkgs.get(p) != origin) {
mchung@1472 420 warning("warn.split.package", p, origin, pkgs.get(p));
mchung@1472 421 }
mchung@1472 422
mchung@1472 423 if (!p.equals(pkg)) {
mchung@1472 424 printTargets(out, targets);
mchung@1472 425 pkg = p;
mchung@1472 426 targets.clear();
mchung@1472 427 out.format(" %s (%s)%n", p, origin.getFileName());
mchung@1472 428 }
mchung@1472 429
mchung@1472 430 for (Location t : e.getValue()) {
mchung@1472 431 p = packageOf(t);
mchung@1472 432 Archive target = Archive.find(t);
mchung@1472 433 if (!targets.containsKey(p)) {
mchung@1472 434 targets.put(p, target);
mchung@1472 435 }
mchung@1472 436 }
mchung@1472 437 }
mchung@1472 438 printTargets(out, targets);
mchung@1472 439 out.println();
mchung@1472 440 }
mchung@1472 441 }
mchung@1472 442
mchung@1472 443 private void printTargets(PrintWriter out, Map<String, Archive> targets) {
mchung@1472 444 for (Map.Entry<String, Archive> t : targets.entrySet()) {
mchung@1472 445 String pn = t.getKey();
mchung@1472 446 out.format(" -> %-40s %s%n", pn, getPackageInfo(pn, t.getValue()));
mchung@1472 447 }
mchung@1472 448 }
mchung@1472 449
mchung@1472 450 private String getPackageInfo(String pn, Archive source) {
mchung@1472 451 if (PlatformClassPath.contains(source)) {
mchung@1472 452 String name = PlatformClassPath.getProfileName(pn);
mchung@1472 453 if (name.isEmpty()) {
mchung@1472 454 return "JDK internal API (" + source.getFileName() + ")";
mchung@1472 455 }
mchung@1472 456 return options.showProfile ? name : "";
mchung@1472 457 }
mchung@1472 458 return source.getFileName();
mchung@1472 459 }
mchung@1472 460
mchung@1472 461 private static String packageOf(Location loc) {
mchung@1472 462 String pkg = loc.getPackageName();
mchung@1472 463 return pkg.isEmpty() ? "<unnamed>" : pkg;
mchung@1472 464 }
mchung@1472 465
mchung@1472 466 private void printClassDeps(PrintWriter out) {
mchung@1472 467 for (Archive source : sourceLocations) {
mchung@1472 468 SortedMap<Location, SortedSet<Location>> deps = source.getDependencies();
mchung@1472 469 if (deps.isEmpty())
mchung@1472 470 continue;
mchung@1472 471
mchung@1472 472 for (Archive target : source.getRequiredArchives()) {
mchung@1472 473 out.format("%s -> %s%n", source, target);
mchung@1472 474 }
mchung@1472 475 out.format("%s%n", source);
mchung@1472 476 for (Map.Entry<Location, SortedSet<Location>> e : deps.entrySet()) {
mchung@1472 477 String cn = e.getKey().getClassName();
mchung@1472 478 Archive origin = Archive.find(e.getKey());
mchung@1472 479 out.format(" %s (%s)%n", cn, origin.getFileName());
mchung@1472 480 for (Location t : e.getValue()) {
mchung@1472 481 cn = t.getClassName();
mchung@1472 482 Archive target = Archive.find(t);
mchung@1472 483 out.format(" -> %-60s %s%n", cn, getPackageInfo(t.getPackageName(), target));
mchung@1472 484 }
mchung@1472 485 }
mchung@1472 486 out.println();
mchung@1472 487 }
mchung@1472 488 }
mchung@1472 489 public void handleOptions(String[] args) throws BadArgs {
mchung@1472 490 // process options
mchung@1472 491 for (int i=0; i < args.length; i++) {
mchung@1472 492 if (args[i].charAt(0) == '-') {
mchung@1472 493 String name = args[i];
mchung@1472 494 Option option = getOption(name);
mchung@1472 495 String param = null;
mchung@1472 496 if (option.hasArg) {
mchung@1472 497 if (name.startsWith("--") && name.indexOf('=') > 0) {
mchung@1472 498 param = name.substring(name.indexOf('=') + 1, name.length());
mchung@1472 499 } else if (i + 1 < args.length) {
mchung@1472 500 param = args[++i];
mchung@1472 501 }
mchung@1472 502 if (param == null || param.isEmpty() || param.charAt(0) == '-') {
mchung@1472 503 throw new BadArgs("err.missing.arg", name).showUsage(true);
mchung@1472 504 }
mchung@1472 505 }
mchung@1472 506 option.process(this, name, param);
mchung@1472 507 if (option.ignoreRest()) {
mchung@1472 508 i = args.length;
mchung@1472 509 }
mchung@1472 510 } else {
mchung@1472 511 // process rest of the input arguments
mchung@1472 512 for (; i < args.length; i++) {
mchung@1472 513 String name = args[i];
mchung@1472 514 if (name.charAt(0) == '-') {
mchung@1472 515 throw new BadArgs("err.option.after.class", name).showUsage(true);
mchung@1472 516 }
mchung@1472 517 if (name.equals("*") || name.equals("\"*\"")) {
mchung@1472 518 options.wildcard = true;
mchung@1472 519 } else {
mchung@1472 520 classes.add(name);
mchung@1472 521 }
mchung@1472 522 }
mchung@1472 523 }
mchung@1472 524 }
mchung@1472 525 }
mchung@1472 526
mchung@1472 527 private Option getOption(String name) throws BadArgs {
mchung@1472 528 for (Option o : recognizedOptions) {
mchung@1472 529 if (o.matches(name)) {
mchung@1472 530 return o;
mchung@1472 531 }
mchung@1472 532 }
mchung@1472 533 throw new BadArgs("err.unknown.option", name).showUsage(true);
mchung@1472 534 }
mchung@1472 535
mchung@1472 536 private void reportError(String key, Object... args) {
mchung@1472 537 log.println(getMessage("error.prefix") + " " + getMessage(key, args));
mchung@1472 538 }
mchung@1472 539
mchung@1472 540 private void warning(String key, Object... args) {
mchung@1472 541 log.println(getMessage("warn.prefix") + " " + getMessage(key, args));
mchung@1472 542 }
mchung@1472 543
mchung@1472 544 private void showHelp() {
mchung@1472 545 log.println(getMessage("main.usage", PROGNAME));
mchung@1472 546 for (Option o : recognizedOptions) {
mchung@1472 547 String name = o.aliases[0].substring(1); // there must always be at least one name
mchung@1472 548 name = name.charAt(0) == '-' ? name.substring(1) : name;
mchung@1472 549 if (o.isHidden() || name.equals("h")) {
mchung@1472 550 continue;
mchung@1472 551 }
mchung@1472 552 log.println(getMessage("main.opt." + name));
mchung@1472 553 }
mchung@1472 554 }
mchung@1472 555
mchung@1472 556 private void showVersion(boolean full) {
mchung@1472 557 log.println(version(full ? "full" : "release"));
mchung@1472 558 }
mchung@1472 559
mchung@1472 560 private String version(String key) {
mchung@1472 561 // key=version: mm.nn.oo[-milestone]
mchung@1472 562 // key=full: mm.mm.oo[-milestone]-build
mchung@1472 563 if (ResourceBundleHelper.versionRB == null) {
mchung@1472 564 return System.getProperty("java.version");
mchung@1472 565 }
mchung@1472 566 try {
mchung@1472 567 return ResourceBundleHelper.versionRB.getString(key);
mchung@1472 568 } catch (MissingResourceException e) {
mchung@1472 569 return getMessage("version.unknown", System.getProperty("java.version"));
mchung@1472 570 }
mchung@1472 571 }
mchung@1472 572
mchung@1472 573 public String getMessage(String key, Object... args) {
mchung@1472 574 try {
mchung@1472 575 return MessageFormat.format(ResourceBundleHelper.bundle.getString(key), args);
mchung@1472 576 } catch (MissingResourceException e) {
mchung@1472 577 throw new InternalError("Missing message: " + key);
mchung@1472 578 }
mchung@1472 579 }
mchung@1472 580
mchung@1472 581 private static class Options {
mchung@1472 582 enum Verbose {
mchung@1472 583 CLASS,
mchung@1472 584 PACKAGE,
mchung@1472 585 SUMMARY,
mchung@1472 586 VERBOSE
mchung@1472 587 };
mchung@1472 588
mchung@1472 589 boolean help;
mchung@1472 590 boolean version;
mchung@1472 591 boolean fullVersion;
mchung@1472 592 boolean showFlags;
mchung@1472 593 boolean showProfile;
mchung@1472 594 boolean showSummary;
mchung@1472 595 boolean wildcard;
mchung@1472 596 String regex;
mchung@1472 597 String classpath = "";
mchung@1472 598 int depth = 1;
mchung@1472 599 Verbose verbose = Verbose.PACKAGE;
mchung@1472 600 Set<String> packageNames = new HashSet<String>();
mchung@1472 601 }
mchung@1472 602
mchung@1472 603 private static class ResourceBundleHelper {
mchung@1472 604 static final ResourceBundle versionRB;
mchung@1472 605 static final ResourceBundle bundle;
mchung@1472 606
mchung@1472 607 static {
mchung@1472 608 Locale locale = Locale.getDefault();
mchung@1472 609 try {
mchung@1472 610 bundle = ResourceBundle.getBundle("com.sun.tools.jdeps.resources.jdeps", locale);
mchung@1472 611 } catch (MissingResourceException e) {
mchung@1472 612 throw new InternalError("Cannot find jdeps resource bundle for locale " + locale);
mchung@1472 613 }
mchung@1472 614 try {
mchung@1472 615 versionRB = ResourceBundle.getBundle("com.sun.tools.jdeps.resources.version");
mchung@1472 616 } catch (MissingResourceException e) {
mchung@1472 617 throw new InternalError("version.resource.missing");
mchung@1472 618 }
mchung@1472 619 }
mchung@1472 620 }
mchung@1472 621
mchung@1472 622 private List<Archive> getArchives(List<String> filenames) throws IOException {
mchung@1472 623 List<Archive> result = new ArrayList<Archive>();
mchung@1472 624 for (String s : filenames) {
mchung@1472 625 File f = new File(s);
mchung@1472 626 if (f.exists()) {
mchung@1472 627 result.add(new Archive(f, ClassFileReader.newInstance(f)));
mchung@1472 628 } else {
mchung@1472 629 warning("warn.file.not.exist", s);
mchung@1472 630 }
mchung@1472 631 }
mchung@1472 632 return result;
mchung@1472 633 }
mchung@1472 634
mchung@1472 635 private List<Archive> getClassPathArchives(String paths) throws IOException {
mchung@1472 636 List<Archive> result = new ArrayList<Archive>();
mchung@1472 637 if (paths.isEmpty()) {
mchung@1472 638 return result;
mchung@1472 639 }
mchung@1472 640 for (String p : paths.split(File.pathSeparator)) {
mchung@1472 641 if (p.length() > 0) {
mchung@1472 642 File f = new File(p);
mchung@1472 643 if (f.exists()) {
mchung@1472 644 result.add(new Archive(f, ClassFileReader.newInstance(f)));
mchung@1472 645 }
mchung@1472 646 }
mchung@1472 647 }
mchung@1472 648 return result;
mchung@1472 649 }
mchung@1472 650 }

mercurial