Mercurial > hg > openjdk > jigsaw > langtools
view src/share/classes/com/sun/tools/jdeps/JdepsTask.java @ 1787:a03c4a86ea2b
8010361: fix some langtools findbugs issues
Reviewed-by: darcy
author | jjg |
---|---|
date | Tue, 19 Mar 2013 17:04:03 -0700 |
parents | fd3fdaff0257 |
children |
line wrap: on
line source
/* * Copyright (c) 2012, 2013, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 only, as * published by the Free Software Foundation. Oracle designates this * particular file as subject to the "Classpath" exception as provided * by Oracle in the LICENSE file that accompanied this code. * * This code is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * version 2 for more details (a copy is included in the LICENSE file that * accompanied this code). * * You should have received a copy of the GNU General Public License version * 2 along with this work; if not, write to the Free Software Foundation, * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. * * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA * or visit www.oracle.com if you need additional information or have any * questions. */ package com.sun.tools.jdeps; import com.sun.tools.classfile.ClassFile; import com.sun.tools.classfile.ConstantPoolException; import com.sun.tools.classfile.Dependencies; import com.sun.tools.classfile.Dependencies.ClassFileError; import com.sun.tools.classfile.Dependency; import java.io.*; import java.text.MessageFormat; import java.util.*; import java.util.regex.Pattern; /** * Implementation for the jdeps tool for static class dependency analysis. */ class JdepsTask { static class BadArgs extends Exception { static final long serialVersionUID = 8765093759964640721L; BadArgs(String key, Object... args) { super(JdepsTask.getMessage(key, args)); this.key = key; this.args = args; } BadArgs showUsage(boolean b) { showUsage = b; return this; } final String key; final Object[] args; boolean showUsage; } static abstract class Option { Option(boolean hasArg, String... aliases) { this.hasArg = hasArg; this.aliases = aliases; } boolean isHidden() { return false; } boolean matches(String opt) { for (String a : aliases) { if (a.equals(opt)) { return true; } else if (opt.startsWith("--") && hasArg && opt.startsWith(a + "=")) { return true; } } return false; } boolean ignoreRest() { return false; } abstract void process(JdepsTask task, String opt, String arg) throws BadArgs; final boolean hasArg; final String[] aliases; } static abstract class HiddenOption extends Option { HiddenOption(boolean hasArg, String... aliases) { super(hasArg, aliases); } boolean isHidden() { return true; } } static Option[] recognizedOptions = { new Option(false, "-h", "-?", "--help") { void process(JdepsTask task, String opt, String arg) { task.options.help = true; } }, new Option(false, "-s", "--summary") { void process(JdepsTask task, String opt, String arg) { task.options.showSummary = true; task.options.verbose = Analyzer.Type.SUMMARY; } }, new Option(false, "-v", "--verbose") { void process(JdepsTask task, String opt, String arg) { task.options.verbose = Analyzer.Type.VERBOSE; } }, new Option(true, "-V", "--verbose-level") { void process(JdepsTask task, String opt, String arg) throws BadArgs { if ("package".equals(arg)) { task.options.verbose = Analyzer.Type.PACKAGE; } else if ("class".equals(arg)) { task.options.verbose = Analyzer.Type.CLASS; } else { throw new BadArgs("err.invalid.arg.for.option", opt); } } }, new Option(true, "-c", "--classpath") { void process(JdepsTask task, String opt, String arg) { task.options.classpath = arg; } }, new Option(true, "-p", "--package") { void process(JdepsTask task, String opt, String arg) { task.options.packageNames.add(arg); } }, new Option(true, "-e", "--regex") { void process(JdepsTask task, String opt, String arg) { task.options.regex = arg; } }, new Option(false, "-P", "--profile") { void process(JdepsTask task, String opt, String arg) throws BadArgs { task.options.showProfile = true; if (Profiles.getProfileCount() == 0) { throw new BadArgs("err.option.unsupported", opt, getMessage("err.profiles.msg")); } } }, new Option(false, "-R", "--recursive") { void process(JdepsTask task, String opt, String arg) { task.options.depth = 0; } }, new HiddenOption(true, "-d", "--depth") { void process(JdepsTask task, String opt, String arg) throws BadArgs { try { task.options.depth = Integer.parseInt(arg); } catch (NumberFormatException e) { throw new BadArgs("err.invalid.arg.for.option", opt); } } }, new Option(false, "--version") { void process(JdepsTask task, String opt, String arg) { task.options.version = true; } }, new HiddenOption(false, "--fullversion") { void process(JdepsTask task, String opt, String arg) { task.options.fullVersion = true; } }, }; private static final String PROGNAME = "jdeps"; private final Options options = new Options(); private final List<String> classes = new ArrayList<String>(); private PrintWriter log; void setLog(PrintWriter out) { log = out; } /** * Result codes. */ static final int EXIT_OK = 0, // Completed with no errors. EXIT_ERROR = 1, // Completed but reported errors. EXIT_CMDERR = 2, // Bad command-line arguments EXIT_SYSERR = 3, // System error or resource exhaustion. EXIT_ABNORMAL = 4;// terminated abnormally int run(String[] args) { if (log == null) { log = new PrintWriter(System.out); } try { handleOptions(args); if (options.help) { showHelp(); } if (options.version || options.fullVersion) { showVersion(options.fullVersion); } if (classes.isEmpty() && !options.wildcard) { if (options.help || options.version || options.fullVersion) { return EXIT_OK; } else { showHelp(); return EXIT_CMDERR; } } if (options.regex != null && options.packageNames.size() > 0) { showHelp(); return EXIT_CMDERR; } if (options.showSummary && options.verbose != Analyzer.Type.SUMMARY) { showHelp(); return EXIT_CMDERR; } boolean ok = run(); return ok ? EXIT_OK : EXIT_ERROR; } catch (BadArgs e) { reportError(e.key, e.args); if (e.showUsage) { log.println(getMessage("main.usage.summary", PROGNAME)); } return EXIT_CMDERR; } catch (IOException e) { return EXIT_ABNORMAL; } finally { log.flush(); } } private final List<Archive> sourceLocations = new ArrayList<Archive>(); private boolean run() throws IOException { findDependencies(); Analyzer analyzer = new Analyzer(options.verbose); analyzer.run(sourceLocations); if (options.verbose == Analyzer.Type.SUMMARY) { printSummary(log, analyzer); } else { printDependencies(log, analyzer); } return true; } private boolean isValidClassName(String name) { if (!Character.isJavaIdentifierStart(name.charAt(0))) { return false; } for (int i=1; i < name.length(); i++) { char c = name.charAt(i); if (c != '.' && !Character.isJavaIdentifierPart(c)) { return false; } } return true; } private void findDependencies() throws IOException { Dependency.Finder finder = Dependencies.getClassDependencyFinder(); Dependency.Filter filter; if (options.regex != null) { filter = Dependencies.getRegexFilter(Pattern.compile(options.regex)); } else if (options.packageNames.size() > 0) { filter = Dependencies.getPackageFilter(options.packageNames, false); } else { filter = new Dependency.Filter() { public boolean accepts(Dependency dependency) { return !dependency.getOrigin().equals(dependency.getTarget()); } }; } List<Archive> archives = new ArrayList<Archive>(); Deque<String> roots = new LinkedList<String>(); for (String s : classes) { File f = new File(s); if (f.exists()) { archives.add(new Archive(f, ClassFileReader.newInstance(f))); } else { if (isValidClassName(s)) { roots.add(s); } else { warning("warn.invalid.arg", s); } } } List<Archive> classpaths = new ArrayList<Archive>(); // for class file lookup if (options.wildcard) { // include all archives from classpath to the initial list archives.addAll(getClassPathArchives(options.classpath)); } else { classpaths.addAll(getClassPathArchives(options.classpath)); } classpaths.addAll(PlatformClassPath.getArchives()); // add all archives to the source locations for reporting sourceLocations.addAll(archives); sourceLocations.addAll(classpaths); // Work queue of names of classfiles to be searched. // Entries will be unique, and for classes that do not yet have // dependencies in the results map. Deque<String> deque = new LinkedList<String>(); Set<String> doneClasses = new HashSet<String>(); // get the immediate dependencies of the input files for (Archive a : archives) { for (ClassFile cf : a.reader().getClassFiles()) { String classFileName; try { classFileName = cf.getName(); } catch (ConstantPoolException e) { throw new ClassFileError(e); } if (!doneClasses.contains(classFileName)) { doneClasses.add(classFileName); } for (Dependency d : finder.findDependencies(cf)) { if (filter.accepts(d)) { String cn = d.getTarget().getName(); if (!doneClasses.contains(cn) && !deque.contains(cn)) { deque.add(cn); } a.addClass(d.getOrigin(), d.getTarget()); } } } } // add Archive for looking up classes from the classpath // for transitive dependency analysis Deque<String> unresolved = roots; int depth = options.depth > 0 ? options.depth : Integer.MAX_VALUE; do { String name; while ((name = unresolved.poll()) != null) { if (doneClasses.contains(name)) { continue; } ClassFile cf = null; for (Archive a : classpaths) { cf = a.reader().getClassFile(name); if (cf != null) { String classFileName; try { classFileName = cf.getName(); } catch (ConstantPoolException e) { throw new ClassFileError(e); } if (!doneClasses.contains(classFileName)) { // if name is a fully-qualified class name specified // from command-line, this class might already be parsed doneClasses.add(classFileName); for (Dependency d : finder.findDependencies(cf)) { if (depth == 0) { // ignore the dependency a.addClass(d.getOrigin()); break; } else if (filter.accepts(d)) { a.addClass(d.getOrigin(), d.getTarget()); String cn = d.getTarget().getName(); if (!doneClasses.contains(cn) && !deque.contains(cn)) { deque.add(cn); } } } } break; } } if (cf == null) { doneClasses.add(name); } } unresolved = deque; deque = new LinkedList<String>(); } while (!unresolved.isEmpty() && depth-- > 0); } private void printSummary(final PrintWriter out, final Analyzer analyzer) { Analyzer.Visitor visitor = new Analyzer.Visitor() { public void visit(String origin, String target, String profile) { if (options.showProfile) { out.format("%-30s -> %s%n", origin, target); } } public void visit(Archive origin, Archive target) { if (!options.showProfile) { out.format("%-30s -> %s%n", origin, target); } } }; analyzer.visitSummary(visitor); } private void printDependencies(final PrintWriter out, final Analyzer analyzer) { Analyzer.Visitor visitor = new Analyzer.Visitor() { private String pkg = ""; public void visit(String origin, String target, String profile) { if (!origin.equals(pkg)) { pkg = origin; out.format(" %s (%s)%n", origin, analyzer.getArchive(origin).getFileName()); } out.format(" -> %-50s %s%n", target, (options.showProfile && !profile.isEmpty()) ? profile : analyzer.getArchiveName(target, profile)); } public void visit(Archive origin, Archive target) { out.format("%s -> %s%n", origin, target); } }; analyzer.visit(visitor); } public void handleOptions(String[] args) throws BadArgs { // process options for (int i=0; i < args.length; i++) { if (args[i].charAt(0) == '-') { String name = args[i]; Option option = getOption(name); String param = null; if (option.hasArg) { if (name.startsWith("--") && name.indexOf('=') > 0) { param = name.substring(name.indexOf('=') + 1, name.length()); } else if (i + 1 < args.length) { param = args[++i]; } if (param == null || param.isEmpty() || param.charAt(0) == '-') { throw new BadArgs("err.missing.arg", name).showUsage(true); } } option.process(this, name, param); if (option.ignoreRest()) { i = args.length; } } else { // process rest of the input arguments for (; i < args.length; i++) { String name = args[i]; if (name.charAt(0) == '-') { throw new BadArgs("err.option.after.class", name).showUsage(true); } if (name.equals("*") || name.equals("\"*\"")) { options.wildcard = true; } else { classes.add(name); } } } } } private Option getOption(String name) throws BadArgs { for (Option o : recognizedOptions) { if (o.matches(name)) { return o; } } throw new BadArgs("err.unknown.option", name).showUsage(true); } private void reportError(String key, Object... args) { log.println(getMessage("error.prefix") + " " + getMessage(key, args)); } private void warning(String key, Object... args) { log.println(getMessage("warn.prefix") + " " + getMessage(key, args)); } private void showHelp() { log.println(getMessage("main.usage", PROGNAME)); for (Option o : recognizedOptions) { String name = o.aliases[0].substring(1); // there must always be at least one name name = name.charAt(0) == '-' ? name.substring(1) : name; if (o.isHidden() || name.equals("h")) { continue; } log.println(getMessage("main.opt." + name)); } } private void showVersion(boolean full) { log.println(version(full ? "full" : "release")); } private String version(String key) { // key=version: mm.nn.oo[-milestone] // key=full: mm.mm.oo[-milestone]-build if (ResourceBundleHelper.versionRB == null) { return System.getProperty("java.version"); } try { return ResourceBundleHelper.versionRB.getString(key); } catch (MissingResourceException e) { return getMessage("version.unknown", System.getProperty("java.version")); } } static String getMessage(String key, Object... args) { try { return MessageFormat.format(ResourceBundleHelper.bundle.getString(key), args); } catch (MissingResourceException e) { throw new InternalError("Missing message: " + key); } } private static class Options { boolean help; boolean version; boolean fullVersion; boolean showProfile; boolean showSummary; boolean wildcard; String regex; String classpath = ""; int depth = 1; Analyzer.Type verbose = Analyzer.Type.PACKAGE; Set<String> packageNames = new HashSet<String>(); } private static class ResourceBundleHelper { static final ResourceBundle versionRB; static final ResourceBundle bundle; static { Locale locale = Locale.getDefault(); try { bundle = ResourceBundle.getBundle("com.sun.tools.jdeps.resources.jdeps", locale); } catch (MissingResourceException e) { throw new InternalError("Cannot find jdeps resource bundle for locale " + locale); } try { versionRB = ResourceBundle.getBundle("com.sun.tools.jdeps.resources.version"); } catch (MissingResourceException e) { throw new InternalError("version.resource.missing"); } } } private List<Archive> getArchives(List<String> filenames) throws IOException { List<Archive> result = new ArrayList<Archive>(); for (String s : filenames) { File f = new File(s); if (f.exists()) { result.add(new Archive(f, ClassFileReader.newInstance(f))); } else { warning("warn.file.not.exist", s); } } return result; } private List<Archive> getClassPathArchives(String paths) throws IOException { List<Archive> result = new ArrayList<Archive>(); if (paths.isEmpty()) { return result; } for (String p : paths.split(File.pathSeparator)) { if (p.length() > 0) { File f = new File(p); if (f.exists()) { result.add(new Archive(f, ClassFileReader.newInstance(f))); } } } return result; } }