Main.java revision 3736:68754738ba9c
11541Srgrimes/* 21541Srgrimes * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved. 31541Srgrimes * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 41541Srgrimes * 551138Salfred * This code is free software; you can redistribute it and/or modify it 6107914Sdillon * under the terms of the GNU General Public License version 2 only, as 71541Srgrimes * published by the Free Software Foundation. Oracle designates this 81541Srgrimes * particular file as subject to the "Classpath" exception as provided 9106149Sdwmalone * by Oracle in the LICENSE file that accompanied this code. 101541Srgrimes * 1164002Speter * This code is distributed in the hope that it will be useful, but WITHOUT 121541Srgrimes * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 131541Srgrimes * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 141541Srgrimes * version 2 for more details (a copy is included in the LICENSE file that 151541Srgrimes * accompanied this code). 161541Srgrimes * 171541Srgrimes * You should have received a copy of the GNU General Public License version 181541Srgrimes * 2 along with this work; if not, write to the Free Software Foundation, 191541Srgrimes * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 201541Srgrimes * 211541Srgrimes * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 221541Srgrimes * or visit www.oracle.com if you need additional information or have any 231541Srgrimes * questions. 241541Srgrimes */ 251541Srgrimes 261541Srgrimespackage com.sun.tools.jdeprscan; 271541Srgrimes 281541Srgrimesimport java.io.File; 291541Srgrimesimport java.io.IOException; 301541Srgrimesimport java.io.PrintStream; 311541Srgrimesimport java.net.URI; 321541Srgrimesimport java.nio.charset.StandardCharsets; 331541Srgrimesimport java.nio.file.Files; 341541Srgrimesimport java.nio.file.FileSystems; 351541Srgrimesimport java.nio.file.Path; 361541Srgrimesimport java.nio.file.Paths; 371541Srgrimesimport java.util.ArrayDeque; 381541Srgrimesimport java.util.ArrayList; 391541Srgrimesimport java.util.Arrays; 401541Srgrimesimport java.util.Collection; 411541Srgrimesimport java.util.HashSet; 421541Srgrimesimport java.util.List; 431541Srgrimesimport java.util.Map; 441541Srgrimesimport java.util.NoSuchElementException; 451541Srgrimesimport java.util.Set; 461541Srgrimesimport java.util.Queue; 471541Srgrimesimport java.util.stream.Stream; 481541Srgrimesimport java.util.jar.JarEntry; 491541Srgrimesimport java.util.jar.JarFile; 501541Srgrimes 511541Srgrimesimport javax.tools.Diagnostic; 521541Srgrimesimport javax.tools.DiagnosticListener; 531541Srgrimesimport javax.tools.JavaCompiler; 541541Srgrimesimport javax.tools.JavaFileObject; 551541Srgrimesimport javax.tools.StandardJavaFileManager; 5652150Smarcelimport javax.tools.StandardLocation; 571541Srgrimesimport javax.tools.ToolProvider; 5852150Smarcel 591541Srgrimesimport com.sun.tools.javac.file.JavacFileManager; 601541Srgrimes 611541Srgrimesimport com.sun.tools.jdeprscan.scan.Scan; 6252150Smarcel 631541Srgrimesimport static java.util.stream.Collectors.*; 641541Srgrimes 651541Srgrimesimport javax.lang.model.element.PackageElement; 661541Srgrimesimport javax.lang.model.element.TypeElement; 671541Srgrimes 681541Srgrimes/** 691541Srgrimes * Deprecation Scanner tool. Loads API deprecation information from the 701541Srgrimes * JDK image, or optionally, from a jar file or class hierarchy. Then scans 711541Srgrimes * a class library for usages of those APIs. 721541Srgrimes * 731541Srgrimes * TODO: 741541Srgrimes * - audit error handling throughout, but mainly in scan package 751541Srgrimes * - handling of covariant overrides 761541Srgrimes * - handling of override of method found in multiple superinterfaces 771541Srgrimes * - convert type/method/field output to Java source like syntax, e.g. 781541Srgrimes * instead of java/lang/Runtime.runFinalizersOnExit(Z)V 791541Srgrimes * print void java.lang.Runtime.runFinalizersOnExit(boolean) 801541Srgrimes * - more example output in man page 811541Srgrimes * - more rigorous GNU style option parsing; use joptsimple? 821541Srgrimes * 831541Srgrimes * FUTURES: 841541Srgrimes * - add module support: --add-modules, --module-path, module arg 851541Srgrimes * - load deprecation declarations from a designated class library instead 861541Srgrimes * of the JDK 871541Srgrimes * - load deprecation declarations from a module 881541Srgrimes * - scan a module (but a modular jar can be treated just a like an ordinary jar) 891541Srgrimes * - multi-version jar 901541Srgrimes */ 911541Srgrimespublic class Main implements DiagnosticListener<JavaFileObject> { 921541Srgrimes final PrintStream out; 931541Srgrimes final PrintStream err; 941541Srgrimes final List<File> bootClassPath = new ArrayList<>(); 951541Srgrimes final List<File> classPath = new ArrayList<>(); 961541Srgrimes final List<File> systemModules = new ArrayList<>(); 971541Srgrimes final List<String> options = new ArrayList<>(); 981541Srgrimes final List<String> comments = new ArrayList<>(); 991541Srgrimes 1001541Srgrimes // Valid releases need to match what the compiler supports. 1011541Srgrimes // Keep these updated manually until there's a compiler API 1021541Srgrimes // that allows querying of supported releases. 1031541Srgrimes final Set<String> releasesWithoutForRemoval = Set.of("6", "7", "8"); 1041541Srgrimes final Set<String> releasesWithForRemoval = Set.of("9"); 1051541Srgrimes 1061541Srgrimes final Set<String> validReleases; 1071541Srgrimes { 1081541Srgrimes Set<String> temp = new HashSet<>(releasesWithoutForRemoval); 1091541Srgrimes temp.addAll(releasesWithForRemoval); 1101541Srgrimes validReleases = Set.of(temp.toArray(new String[0])); 1111541Srgrimes } 1121541Srgrimes 113105950Speter boolean verbose = false; 1141541Srgrimes boolean forRemoval = false; 1151541Srgrimes 1161541Srgrimes final JavaCompiler compiler; 1171541Srgrimes final StandardJavaFileManager fm; 1181541Srgrimes 1191541Srgrimes List<DeprData> deprList; // non-null after successful load phase 1201541Srgrimes 12152150Smarcel /** 1221541Srgrimes * Processes a collection of class names. Names should fully qualified 1231541Srgrimes * names in the form "pkg.pkg.pkg.classname". 1241541Srgrimes * 1251541Srgrimes * @param classNames collection of fully qualified classnames to process 1261541Srgrimes * @return true for success, false for failure 1271541Srgrimes * @throws IOException if an I/O error occurs 1281541Srgrimes */ 12914220Speter boolean doClassNames(Collection<String> classNames) throws IOException { 1301541Srgrimes if (verbose) { 1311541Srgrimes out.println("List of classes to process:"); 1321541Srgrimes classNames.forEach(out::println); 1331541Srgrimes out.println("End of class list."); 1341541Srgrimes } 1351541Srgrimes 1368019Sache // TODO: not sure this is necessary... 1378019Sache if (fm instanceof JavacFileManager) { 1381541Srgrimes ((JavacFileManager)fm).setSymbolFileEnabled(false); 1391541Srgrimes } 1401541Srgrimes 1411541Srgrimes fm.setLocation(StandardLocation.CLASS_PATH, classPath); 1421541Srgrimes if (!bootClassPath.isEmpty()) { 1431541Srgrimes fm.setLocation(StandardLocation.PLATFORM_CLASS_PATH, bootClassPath); 1441541Srgrimes } 1451541Srgrimes 1461541Srgrimes if (!systemModules.isEmpty()) { 1471541Srgrimes fm.setLocation(StandardLocation.SYSTEM_MODULES, systemModules); 1481541Srgrimes } 1491541Srgrimes 1501541Srgrimes LoadProc proc = new LoadProc(); 1511541Srgrimes JavaCompiler.CompilationTask task = 1521541Srgrimes compiler.getTask(null, fm, this, options, classNames, null); 1531541Srgrimes task.setProcessors(List.of(proc)); 1541541Srgrimes boolean r = task.call(); 1551541Srgrimes if (r) { 1561541Srgrimes if (forRemoval) { 1571541Srgrimes deprList = proc.getDeprecations().stream() 1581541Srgrimes .filter(DeprData::isForRemoval) 1591541Srgrimes .collect(toList()); 1601541Srgrimes } else { 16114220Speter deprList = proc.getDeprecations(); 16214220Speter } 16314220Speter } 1641541Srgrimes return r; 1651541Srgrimes } 1661541Srgrimes 1671541Srgrimes /** 1681541Srgrimes * Processes a stream of filenames (strings). The strings are in the 1691541Srgrimes * form pkg/pkg/pkg/classname.class relative to the root of a package 1701541Srgrimes * hierarchy. 1711541Srgrimes * 1721549Srgrimes * @param filenames a Stream of filenames to process 1731549Srgrimes * @return true for success, false for failure 1741549Srgrimes * @throws IOException if an I/O error occurs 1751549Srgrimes */ 1762442Sdg boolean doFileNames(Stream<String> filenames) throws IOException { 1771541Srgrimes return doClassNames( 1781541Srgrimes filenames.filter(name -> name.endsWith(".class")) 1792729Sdfr .filter(name -> !name.endsWith("package-info.class")) 1802729Sdfr .filter(name -> !name.endsWith("module-info.class")) 1811541Srgrimes .map(s -> s.replaceAll("\\.class$", "")) 1821541Srgrimes .map(s -> s.replace(File.separatorChar, '.')) 18345065Salc .collect(toList())); 18445065Salc } 1852858Swollman 1862297Swollman /** 18714220Speter * Replaces all but the first occurrence of '/' with '.'. Assumes 18814220Speter * that the name is in the format module/pkg/pkg/classname.class. 18914220Speter * That is, the name should contain at least one '/' character 1901541Srgrimes * separating the module name from the package-class name. 1911541Srgrimes * 1921541Srgrimes * @param filename the input filename 1931541Srgrimes * @return the modular classname 19432889Sphk */ 19532889Sphk String convertModularFileName(String filename) { 19632889Sphk int slash = filename.indexOf('/'); 19732889Sphk return filename.substring(0, slash) 1981541Srgrimes + "/" 1991541Srgrimes + filename.substring(slash+1).replace('/', '.'); 2001541Srgrimes } 2011541Srgrimes 2021541Srgrimes /** 2031541Srgrimes * Processes a stream of filenames (strings) including a module prefix. 2041541Srgrimes * The strings are in the form module/pkg/pkg/pkg/classname.class relative 2051541Srgrimes * to the root of a directory containing modules. The strings are processed 2061541Srgrimes * into module-qualified class names of the form 2071541Srgrimes * "module/pkg.pkg.pkg.classname". 2081541Srgrimes * 2091541Srgrimes * @param filenames a Stream of filenames to process 2101541Srgrimes * @return true for success, false for failure 2111541Srgrimes * @throws IOException if an I/O error occurs 2121541Srgrimes */ 2131541Srgrimes boolean doModularFileNames(Stream<String> filenames) throws IOException { 2141541Srgrimes return doClassNames( 21535938Sdyson filenames.filter(name -> name.endsWith(".class")) 21635938Sdyson .filter(name -> !name.endsWith("package-info.class")) 21728400Speter .filter(name -> !name.endsWith("module-info.class")) 21825582Speter .map(s -> s.replaceAll("\\.class$", "")) 21929349Speter .map(this::convertModularFileName) 2202124Sdg .collect(toList())); 2212124Sdg } 2222124Sdg 2232124Sdg /** 2242124Sdg * Processes named class files in the given directory. The directory 2252124Sdg * should be the root of a package hierarchy. If classNames is 2262124Sdg * empty, walks the directory hierarchy to find all classes. 2272124Sdg * 2282124Sdg * @param dirname the name of the directory to process 2292124Sdg * @param classNames the names of classes to process 23012865Speter * @return true for success, false for failure 23112865Speter * @throws IOException if an I/O error occurs 23212865Speter */ 23359829Speter boolean processDirectory(String dirname, Collection<String> classNames) throws IOException { 23412865Speter if (!Files.isDirectory(Paths.get(dirname))) { 23512865Speter err.printf("%s: not a directory%n", dirname); 23612865Speter return false; 23712865Speter } 23812865Speter 23912865Speter classPath.add(0, new File(dirname)); 24012865Speter 24112865Speter if (classNames.isEmpty()) { 24225582Speter Path base = Paths.get(dirname); 24325582Speter int baseCount = base.getNameCount(); 24425582Speter try (Stream<Path> paths = Files.walk(base)) { 24525582Speter Stream<String> files = 24625582Speter paths.filter(p -> p.getNameCount() > baseCount) 24725582Speter .map(p -> p.subpath(baseCount, p.getNameCount())) 24825582Speter .map(Path::toString); 24925582Speter return doFileNames(files); 25025582Speter } 25114220Speter } else { 25214220Speter return doClassNames(classNames); 25314220Speter } 25414220Speter } 25514220Speter 25614220Speter /** 25714220Speter * Processes all class files in the given jar file. 25814220Speter * 25914220Speter * @param jarname the name of the jar file to process 26014220Speter * @return true for success, false for failure 26114220Speter * @throws IOException if an I/O error occurs 26229349Speter */ 26324452Speter boolean doJarFile(String jarname) throws IOException { 26424440Speter try (JarFile jf = new JarFile(jarname)) { 26525537Sdfr Stream<String> files = 26625537Sdfr jf.stream() 26725537Sdfr .map(JarEntry::getName); 26825537Sdfr return doFileNames(files); 26925537Sdfr } 27025537Sdfr } 27125537Sdfr 27225537Sdfr /** 27325537Sdfr * Processes named class files from the given jar file, 27425537Sdfr * or all classes if classNames is empty. 27525537Sdfr * 27625537Sdfr * @param jarname the name of the jar file to process 27725537Sdfr * @param classNames the names of classes to process 27825537Sdfr * @return true for success, false for failure 27925537Sdfr * @throws IOException if an I/O error occurs 28025537Sdfr */ 28125537Sdfr boolean processJarFile(String jarname, Collection<String> classNames) throws IOException { 28235938Sdyson classPath.add(0, new File(jarname)); 28325537Sdfr 28435938Sdyson if (classNames.isEmpty()) { 28535938Sdyson return doJarFile(jarname); 28635938Sdyson } else { 28735938Sdyson return doClassNames(classNames); 28835938Sdyson } 28935938Sdyson } 29035938Sdyson 29125537Sdfr /** 29225537Sdfr * Processes named class files from rt.jar of a JDK version 7 or 8. 29325537Sdfr * If classNames is empty, processes all classes. 29425537Sdfr * 29525537Sdfr * @param jdkHome the path to the "home" of the JDK to process 29625537Sdfr * @param classNames the names of classes to process 29725537Sdfr * @return true for success, false for failure 29825537Sdfr * @throws IOException if an I/O error occurs 29925537Sdfr */ 30025537Sdfr boolean processOldJdk(String jdkHome, Collection<String> classNames) throws IOException { 30125537Sdfr String RTJAR = jdkHome + "/jre/lib/rt.jar"; 30225537Sdfr String CSJAR = jdkHome + "/jre/lib/charsets.jar"; 30325537Sdfr 30425537Sdfr bootClassPath.add(0, new File(RTJAR)); 30525537Sdfr bootClassPath.add(1, new File(CSJAR)); 30625537Sdfr options.add("-source"); 30751138Salfred options.add("8"); 30851138Salfred 30951138Salfred if (classNames.isEmpty()) { 31025537Sdfr return doJarFile(RTJAR); 31125537Sdfr } else { 31225537Sdfr return doClassNames(classNames); 31325537Sdfr } 31425537Sdfr } 31525537Sdfr 31625537Sdfr /** 31725537Sdfr * Processes listed classes given a JDK 9 home. 31825537Sdfr */ 31925537Sdfr boolean processJdk9(String jdkHome, Collection<String> classes) throws IOException { 32028400Speter systemModules.add(new File(jdkHome)); 32156115Speter return doClassNames(classes); 32256115Speter } 32336034Speter 32426671Sdyson /** 32526671Sdyson * Processes the class files from the currently running JDK, 32626671Sdyson * using the jrt: filesystem. 32726671Sdyson * 32826671Sdyson * @return true for success, false for failure 32926671Sdyson * @throws IOException if an I/O error occurs 33026671Sdyson */ 33126671Sdyson boolean processSelf(Collection<String> classes) throws IOException { 33269514Sjake options.add("--add-modules"); 33369514Sjake options.add("java.se.ee,jdk.xml.bind"); // TODO why jdk.xml.bind? 33426671Sdyson 33526671Sdyson if (classes.isEmpty()) { 33629391Sphk Path modules = FileSystems.getFileSystem(URI.create("jrt:/")) 33734925Sdufault .getPath("/modules"); 33834925Sdufault 33934925Sdufault // names are /modules/<modulename>/pkg/.../Classname.class 34034925Sdufault try (Stream<Path> paths = Files.walk(modules)) { 34134925Sdufault Stream<String> files = 34234925Sdufault paths.filter(p -> p.getNameCount() > 2) 34334925Sdufault .map(p -> p.subpath(1, p.getNameCount())) 34434925Sdufault .map(Path::toString); 34535938Sdyson return doModularFileNames(files); 34699856Salfred } 34741089Speter } else { 34846155Sphk return doClassNames(classes); 34949420Sjkh } 35051791Smarcel } 35151791Smarcel 352105950Speter /** 35351791Smarcel * Process classes from a particular JDK release, using only information 354105950Speter * in this JDK. 35551791Smarcel * 35651791Smarcel * @param release "6", "7", "8", or "9" 35756271Srwatson * @param classes collection of classes to process, may be empty 35856271Srwatson * @return success value 35956271Srwatson */ 36056271Srwatson boolean processRelease(String release, Collection<String> classes) throws IOException { 36156271Srwatson options.addAll(List.of("--release", release)); 36256271Srwatson 36356271Srwatson if (release.equals("9")) { 36456271Srwatson List<String> rootMods = List.of("java.se", "java.se.ee"); 36554803Srwatson TraverseProc proc = new TraverseProc(rootMods); 36654803Srwatson JavaCompiler.CompilationTask task = 36754803Srwatson compiler.getTask(null, fm, this, 36854803Srwatson // options 36955943Sjasone List.of("--add-modules", String.join(",", rootMods)), 37056115Speter // classes 37156115Speter List.of("java.lang.Object"), 37259288Sjlemon null); 37359288Sjlemon task.setProcessors(List.of(proc)); 37498198Srwatson if (!task.call()) { 37598198Srwatson return false; 37698198Srwatson } 37798198Srwatson Map<PackageElement, List<TypeElement>> types = proc.getPublicTypes(); 37898198Srwatson options.add("--add-modules"); 37998198Srwatson options.add(String.join(",", rootMods)); 38069449Salfred return doClassNames( 38175039Srwatson types.values().stream() 38275039Srwatson .flatMap(List::stream) 38375039Srwatson .map(TypeElement::toString) 38475427Srwatson .collect(toList())); 38583652Speter } else { 38683796Srwatson // TODO: kind of a hack... 38784884Srwatson // Create a throwaway compilation task with options "--release N" 38885891Sphk // which has the side effect of setting the file manager's 38990889Sjulian // PLATFORM_CLASS_PATH to the right value. 39090889Sjulian JavaCompiler compiler = ToolProvider.getSystemJavaCompiler(); 391103972Sarchie StandardJavaFileManager fm = 392103972Sarchie compiler.getStandardFileManager(this, null, StandardCharsets.UTF_8); 393103972Sarchie JavaCompiler.CompilationTask task = 394100897Srwatson compiler.getTask(null, fm, this, List.of("--release", release), null, null); 395100897Srwatson List<Path> paths = new ArrayList<>(); 396100897Srwatson for (Path p : fm.getLocationAsPaths(StandardLocation.PLATFORM_CLASS_PATH)) { 397100897Srwatson try (Stream<Path> str = Files.walk(p)) { 398100897Srwatson str.forEachOrdered(paths::add); 399100897Srwatson } 40094936Smux } 40196084Smux 40297372Smarcel options.add("-Xlint:-options"); 40399856Salfred 404101426Srwatson return doClassNames( 405103575Salfred paths.stream() 406103575Salfred .filter(path -> path.toString().endsWith(".sig")) 407103575Salfred .map(path -> path.subpath(1, path.getNameCount())) 408103575Salfred .map(Path::toString) 409103575Salfred .map(s -> s.replaceAll("\\.sig$", "")) 410103575Salfred .map(s -> s.replace('/', '.')) 411103575Salfred .collect(toList())); 412103575Salfred } 413103575Salfred } 414103575Salfred 415103575Salfred /** 416103575Salfred * An enum denoting the mode in which the tool is running. 417103575Salfred * Different modes correspond to the different process* methods. 418103575Salfred * The exception is UNKNOWN, which indicates that a mode wasn't 419105692Srwatson * specified on the command line, which is an error. 420105692Srwatson */ 421105692Srwatson static enum LoadMode { 422104731Srwatson CLASSES, DIR, JAR, OLD_JDK, JDK9, SELF, RELEASE, LOAD_CSV 423104731Srwatson } 424104731Srwatson 425106467Srwatson static enum ScanMode { 426105950Speter ARGS, LIST, PRINT_CSV 427105950Speter } 428105692Srwatson 429105692Srwatson /** 430105692Srwatson * A checked exception that's thrown if a command-line syntax error 431106978Sdeischen * is detected. 432106978Sdeischen */ 433106978Sdeischen static class UsageException extends Exception { 434107914Sdillon private static final long serialVersionUID = 3611828659572908743L; 4351541Srgrimes } 436 437 /** 438 * Convenience method to throw UsageException if a condition is false. 439 * 440 * @param cond the condition that's required to be true 441 * @throws UsageException 442 */ 443 void require(boolean cond) throws UsageException { 444 if (!cond) { 445 throw new UsageException(); 446 } 447 } 448 449 /** 450 * Constructs an instance of the finder tool. 451 * 452 * @param out the stream to which the tool's output is sent 453 * @param err the stream to which error messages are sent 454 */ 455 Main(PrintStream out, PrintStream err) { 456 this.out = out; 457 this.err = err; 458 compiler = ToolProvider.getSystemJavaCompiler(); 459 fm = compiler.getStandardFileManager(this, null, StandardCharsets.UTF_8); 460 } 461 462 /** 463 * Prints the diagnostic to the err stream. 464 * 465 * Specified by the DiagnosticListener interface. 466 * 467 * @param diagnostic the tool diagnostic to print 468 */ 469 @Override 470 public void report(Diagnostic<? extends JavaFileObject> diagnostic) { 471 err.println(diagnostic); 472 } 473 474 /** 475 * Parses arguments and performs the requested processing. 476 * 477 * @param argArray command-line arguments 478 * @return true on success, false on error 479 */ 480 boolean run(String... argArray) { 481 Queue<String> args = new ArrayDeque<>(Arrays.asList(argArray)); 482 LoadMode loadMode = LoadMode.RELEASE; 483 ScanMode scanMode = ScanMode.ARGS; 484 String dir = null; 485 String jar = null; 486 String jdkHome = null; 487 String release = "9"; 488 List<String> loadClasses = new ArrayList<>(); 489 String csvFile = null; 490 491 try { 492 while (!args.isEmpty()) { 493 String a = args.element(); 494 if (a.startsWith("-")) { 495 args.remove(); 496 switch (a) { 497 case "--class-path": 498 classPath.clear(); 499 Arrays.stream(args.remove().split(File.pathSeparator)) 500 .map(File::new) 501 .forEachOrdered(classPath::add); 502 break; 503 case "--for-removal": 504 forRemoval = true; 505 break; 506 case "--full-version": 507 out.println(System.getProperty("java.vm.version")); 508 return false; 509 case "--help": 510 case "-h": 511 out.println(Messages.get("main.usage")); 512 out.println(); 513 out.println(Messages.get("main.help")); 514 return false; 515 case "-l": 516 case "--list": 517 require(scanMode == ScanMode.ARGS); 518 scanMode = ScanMode.LIST; 519 break; 520 case "--release": 521 loadMode = LoadMode.RELEASE; 522 release = args.remove(); 523 if (!validReleases.contains(release)) { 524 throw new UsageException(); 525 } 526 break; 527 case "-v": 528 case "--verbose": 529 verbose = true; 530 break; 531 case "--version": 532 out.println(System.getProperty("java.version")); 533 return false; 534 case "--Xcompiler-arg": 535 options.add(args.remove()); 536 break; 537 case "--Xcsv-comment": 538 comments.add(args.remove()); 539 break; 540 case "--Xhelp": 541 out.println(Messages.get("main.xhelp")); 542 return false; 543 case "--Xload-class": 544 loadMode = LoadMode.CLASSES; 545 loadClasses.add(args.remove()); 546 break; 547 case "--Xload-csv": 548 loadMode = LoadMode.LOAD_CSV; 549 csvFile = args.remove(); 550 break; 551 case "--Xload-dir": 552 loadMode = LoadMode.DIR; 553 dir = args.remove(); 554 break; 555 case "--Xload-jar": 556 loadMode = LoadMode.JAR; 557 jar = args.remove(); 558 break; 559 case "--Xload-jdk9": 560 loadMode = LoadMode.JDK9; 561 jdkHome = args.remove(); 562 break; 563 case "--Xload-old-jdk": 564 loadMode = LoadMode.OLD_JDK; 565 jdkHome = args.remove(); 566 break; 567 case "--Xload-self": 568 loadMode = LoadMode.SELF; 569 break; 570 case "--Xprint-csv": 571 require(scanMode == ScanMode.ARGS); 572 scanMode = ScanMode.PRINT_CSV; 573 break; 574 default: 575 throw new UsageException(); 576 } 577 } else { 578 break; 579 } 580 } 581 582 if ((scanMode == ScanMode.ARGS) == args.isEmpty()) { 583 throw new UsageException(); 584 } 585 586 if ( forRemoval && loadMode == LoadMode.RELEASE && 587 releasesWithoutForRemoval.contains(release)) { 588 throw new UsageException(); 589 } 590 591 boolean success = false; 592 593 switch (loadMode) { 594 case CLASSES: 595 success = doClassNames(loadClasses); 596 break; 597 case DIR: 598 success = processDirectory(dir, loadClasses); 599 break; 600 case JAR: 601 success = processJarFile(jar, loadClasses); 602 break; 603 case JDK9: 604 require(!args.isEmpty()); 605 success = processJdk9(jdkHome, loadClasses); 606 break; 607 case LOAD_CSV: 608 deprList = DeprDB.loadFromFile(csvFile); 609 success = true; 610 break; 611 case OLD_JDK: 612 success = processOldJdk(jdkHome, loadClasses); 613 break; 614 case RELEASE: 615 success = processRelease(release, loadClasses); 616 break; 617 case SELF: 618 success = processSelf(loadClasses); 619 break; 620 default: 621 throw new UsageException(); 622 } 623 624 if (!success) { 625 return false; 626 } 627 } catch (NoSuchElementException | UsageException ex) { 628 err.println(Messages.get("main.usage")); 629 return false; 630 } catch (IOException ioe) { 631 if (verbose) { 632 ioe.printStackTrace(err); 633 } else { 634 err.println(ioe); 635 } 636 return false; 637 } 638 639 // now the scanning phase 640 641 boolean scanStatus = true; 642 643 switch (scanMode) { 644 case LIST: 645 for (DeprData dd : deprList) { 646 if (!forRemoval || dd.isForRemoval()) { 647 out.println(Pretty.print(dd)); 648 } 649 } 650 break; 651 case PRINT_CSV: 652 out.println("#jdepr1"); 653 comments.forEach(s -> out.println("# " + s)); 654 for (DeprData dd : deprList) { 655 CSV.write(out, dd.kind, dd.typeName, dd.nameSig, dd.since, dd.forRemoval); 656 } 657 break; 658 case ARGS: 659 DeprDB db = DeprDB.loadFromList(deprList); 660 List<String> cp = classPath.stream() 661 .map(File::toString) 662 .collect(toList()); 663 Scan scan = new Scan(out, err, cp, db, verbose); 664 665 for (String a : args) { 666 boolean s; 667 if (a.endsWith(".jar")) { 668 s = scan.scanJar(a); 669 } else if (a.endsWith(".class")) { 670 s = scan.processClassFile(a); 671 } else if (Files.isDirectory(Paths.get(a))) { 672 s = scan.scanDir(a); 673 } else { 674 s = scan.processClassName(a.replace('.', '/')); 675 } 676 scanStatus = scanStatus && s; 677 } 678 break; 679 } 680 681 return scanStatus; 682 } 683 684 /** 685 * Programmatic main entry point: initializes the tool instance to 686 * use stdout and stderr; runs the tool, passing command-line args; 687 * returns an exit status. 688 * 689 * @return true on success, false otherwise 690 */ 691 public static boolean call(PrintStream out, PrintStream err, String... args) { 692 return new Main(out, err).run(args); 693 } 694 695 /** 696 * Calls the main entry point and exits the JVM with an exit 697 * status determined by the return status. 698 */ 699 public static void main(String[] args) { 700 System.exit(call(System.out, System.err, args) ? 0 : 1); 701 } 702} 703