JavadocTool.java revision 3576:b9593e0ea1e0
11558Srgrimes/* 21558Srgrimes * Copyright (c) 2001, 2016, Oracle and/or its affiliates. All rights reserved. 31558Srgrimes * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 41558Srgrimes * 51558Srgrimes * This code is free software; you can redistribute it and/or modify it 61558Srgrimes * under the terms of the GNU General Public License version 2 only, as 71558Srgrimes * published by the Free Software Foundation. Oracle designates this 81558Srgrimes * particular file as subject to the "Classpath" exception as provided 91558Srgrimes * by Oracle in the LICENSE file that accompanied this code. 101558Srgrimes * 111558Srgrimes * This code is distributed in the hope that it will be useful, but WITHOUT 121558Srgrimes * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 131558Srgrimes * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 141558Srgrimes * version 2 for more details (a copy is included in the LICENSE file that 151558Srgrimes * accompanied this code). 161558Srgrimes * 171558Srgrimes * You should have received a copy of the GNU General Public License version 181558Srgrimes * 2 along with this work; if not, write to the Free Software Foundation, 191558Srgrimes * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 201558Srgrimes * 211558Srgrimes * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 221558Srgrimes * or visit www.oracle.com if you need additional information or have any 231558Srgrimes * questions. 241558Srgrimes */ 251558Srgrimes 261558Srgrimespackage jdk.javadoc.internal.tool; 271558Srgrimes 2823681Speter 2950476Speterimport java.io.File; 301558Srgrimesimport java.io.IOException; 31245081Srmacklemimport java.util.ArrayList; 321558Srgrimesimport java.util.Collection; 331558Srgrimesimport java.util.Collections; 341558Srgrimesimport java.util.EnumSet; 351558Srgrimesimport java.util.HashSet; 361558Srgrimesimport java.util.LinkedHashMap; 371558Srgrimesimport java.util.LinkedHashSet; 381558Srgrimesimport java.util.List; 391558Srgrimesimport java.util.Map; 4068960Sruimport java.util.Set; 411558Srgrimes 421558Srgrimesimport javax.tools.JavaFileManager; 4337663Scharnierimport javax.tools.JavaFileManager.Location; 441558Srgrimesimport javax.tools.JavaFileObject; 451558Srgrimesimport javax.tools.StandardJavaFileManager; 461558Srgrimesimport javax.tools.StandardLocation; 471558Srgrimes 481558Srgrimesimport com.sun.tools.javac.code.ClassFinder; 4970152Sruimport com.sun.tools.javac.code.Symbol.Completer; 5070152Sruimport com.sun.tools.javac.code.Symbol.CompletionFailure; 5170152Sruimport com.sun.tools.javac.code.Symbol.ModuleSymbol; 5270152Sruimport com.sun.tools.javac.code.Symbol.PackageSymbol; 531558Srgrimesimport com.sun.tools.javac.comp.Enter; 541558Srgrimesimport com.sun.tools.javac.tree.JCTree; 551558Srgrimesimport com.sun.tools.javac.tree.JCTree.JCClassDecl; 561558Srgrimesimport com.sun.tools.javac.tree.JCTree.JCCompilationUnit; 57192934Srmacklemimport com.sun.tools.javac.util.Abort; 5879118Sddimport com.sun.tools.javac.util.Context; 5979213Sruimport com.sun.tools.javac.util.ListBuffer; 6079213Sruimport com.sun.tools.javac.util.Name; 61192934Srmacklemimport com.sun.tools.javac.util.Position; 621558Srgrimesimport jdk.javadoc.doclet.DocletEnvironment; 63107788Sru 64180071Sdanger 65180071Sdanger/** 66180071Sdanger * This class could be the main entry point for Javadoc when Javadoc is used as a 67107788Sru * component in a larger software system. It provides operations to 681558Srgrimes * construct a new javadoc processor, and to run it on a set of source 691558Srgrimes * files. 70107788Sru * 711558Srgrimes * <p><b>This is NOT part of any supported API. 72192934Srmacklem * If you write code that depends on this, you do so at your own risk. 731558Srgrimes * This code and its internal interfaces are subject to change or 741558Srgrimes * deletion without notice.</b> 75223954Srmacklem * 76237216Seadler * @author Neal Gafter 77223954Srmacklem */ 78223954Srmacklempublic class JavadocTool extends com.sun.tools.javac.main.JavaCompiler { 79223954Srmacklem ToolEnvironment toolEnv; 80223954Srmacklem 81223954Srmacklem final Messager messager; 82107788Sru final ClassFinder javadocFinder; 831558Srgrimes final Enter javadocEnter; 841558Srgrimes final Set<JavaFileObject> uniquefiles; 851558Srgrimes 86107788Sru /** 8723681Speter * Construct a new JavaCompiler processor, using appropriately 8823681Speter * extended phases of the underlying compiler. 8947594Skris */ 9047594Skris protected JavadocTool(Context context) { 91223954Srmacklem super(context); 92223954Srmacklem messager = Messager.instance0(context); 93237216Seadler javadocFinder = JavadocClassFinder.instance(context); 94223954Srmacklem javadocEnter = JavadocEnter.instance(context); 95223954Srmacklem uniquefiles = new HashSet<>(); 96223954Srmacklem } 97223954Srmacklem 98192934Srmacklem /** 99209926Smaxim * For javadoc, the parser needs to keep comments. Overrides method from JavaCompiler. 100223954Srmacklem */ 101223954Srmacklem @Override 102223954Srmacklem protected boolean keepComments() { 103223954Srmacklem return true; 104223954Srmacklem } 105223954Srmacklem 1061558Srgrimes /** 107180071Sdanger * Construct a new javadoc tool. 108180112Sdanger */ 109180071Sdanger public static JavadocTool make0(Context context) { 110180112Sdanger Messager messager = null; 111180071Sdanger try { 112107788Sru // force the use of Javadoc's class finder 1131558Srgrimes JavadocClassFinder.preRegister(context); 1141558Srgrimes 115107788Sru // force the use of Javadoc's own enter phase 1161558Srgrimes JavadocEnter.preRegister(context); 117107788Sru 118180112Sdanger // force the use of Javadoc's own member enter phase 1191558Srgrimes JavadocMemberEnter.preRegister(context); 120192934Srmacklem 121192934Srmacklem // force the use of Javadoc's own todo phase 122192934Srmacklem JavadocTodo.preRegister(context); 1231558Srgrimes 1241558Srgrimes // force the use of Messager as a Log 1251558Srgrimes messager = Messager.instance0(context); 1261558Srgrimes 127180154Sdanger return new JavadocTool(context); 1281558Srgrimes } catch (CompletionFailure ex) { 1291558Srgrimes messager.error(Position.NOPOS, ex.getMessage()); 1301558Srgrimes return null; 1311558Srgrimes } 13271099Sru } 1331558Srgrimes 1341558Srgrimes public DocletEnvironment getEnvironment(String encoding, 1351558Srgrimes String showAccess, 136180154Sdanger String overviewpath, 1371558Srgrimes List<String> args, 1381558Srgrimes Iterable<? extends JavaFileObject> fileObjects, 1391558Srgrimes List<String> subPackages, 1401558Srgrimes List<String> excludedPackages, 1411558Srgrimes boolean docClasses, 1421558Srgrimes boolean quiet) throws IOException { 1431558Srgrimes toolEnv = ToolEnvironment.instance(context); 1441558Srgrimes toolEnv.intialize(encoding, showAccess, overviewpath, args, fileObjects, 145180154Sdanger subPackages, excludedPackages, docClasses, quiet); 1461558Srgrimes 1471558Srgrimes javadocFinder.sourceCompleter = docClasses ? Completer.NULL_COMPLETER : sourceCompleter; 1481558Srgrimes 149180154Sdanger if (docClasses) { 1501558Srgrimes // If -Xclasses is set, the args should be a series of class names 151180112Sdanger for (String arg: args) { 1521558Srgrimes if (!isValidPackageName(arg)) // checks 1531558Srgrimes toolEnv.error(null, "main.illegal_class_name", arg); 1541558Srgrimes } 1551558Srgrimes if (messager.nerrors() != 0) { 1561558Srgrimes return null; 1571558Srgrimes } 1581558Srgrimes return new DocEnvImpl(toolEnv, args); 1591558Srgrimes } 1601558Srgrimes 1611558Srgrimes ListBuffer<JCCompilationUnit> classTrees = new ListBuffer<>(); 1621558Srgrimes Set<String> includedPackages = new LinkedHashSet<>(); 1631558Srgrimes 1641558Srgrimes try { 1651558Srgrimes 1661558Srgrimes StandardJavaFileManager fm = toolEnv.fileManager instanceof StandardJavaFileManager 1671558Srgrimes ? (StandardJavaFileManager) toolEnv.fileManager : null; 1681558Srgrimes Set<String> packageNames = new LinkedHashSet<>(); 1691558Srgrimes // Normally, the args should be a series of package names or file names. 1701558Srgrimes // Parse the files and collect the package names. 1711558Srgrimes for (String arg: args) { 1721558Srgrimes if (fm != null && arg.endsWith(".java") && new File(arg).exists()) { 1731558Srgrimes if (new File(arg).getName().equals("module-info.java")) { 1741558Srgrimes toolEnv.warning("main.file_ignored", arg); 1751558Srgrimes } else { 1761558Srgrimes parse(fm.getJavaFileObjects(arg), classTrees, true); 177184588Sdfr } 178184588Sdfr } else if (isValidPackageName(arg)) { 179184588Sdfr packageNames.add(arg); 180184588Sdfr } else if (arg.endsWith(".java")) { 181184588Sdfr if (fm == null) 182184588Sdfr throw new IllegalArgumentException(); 183184588Sdfr else 184184588Sdfr toolEnv.error(null, "main.file_not_found", arg); 185184588Sdfr } else { 186184588Sdfr toolEnv.error(null, "main.illegal_package_name", arg); 187184588Sdfr } 1881558Srgrimes } 1891558Srgrimes 190107788Sru // Parse file objects provide via the DocumentationTool API 1911558Srgrimes parse(fileObjects, classTrees, true); 1921558Srgrimes 1931558Srgrimes modules.initModules(classTrees.toList()); 1941558Srgrimes 1951558Srgrimes // Build up the complete list of any packages to be documented 1961558Srgrimes Location location = modules.multiModuleMode ? StandardLocation.MODULE_SOURCE_PATH 1971558Srgrimes : toolEnv.fileManager.hasLocation(StandardLocation.SOURCE_PATH) ? StandardLocation.SOURCE_PATH 19837663Scharnier : StandardLocation.CLASS_PATH; 19937663Scharnier 20027447Sdfr PackageTable t = new PackageTable(toolEnv.fileManager, location) 20127447Sdfr .packages(packageNames) 20257669Ssheldonh .subpackages(subPackages, excludedPackages); 20357669Ssheldonh 204180112Sdanger includedPackages = t.getIncludedPackages(); 20557669Ssheldonh 20627447Sdfr // Parse the files in the packages to be documented 20737663Scharnier ListBuffer<JCCompilationUnit> packageTrees = new ListBuffer<>(); 20837663Scharnier for (String packageName: includedPackages) { 20937663Scharnier List<JavaFileObject> files = t.getFiles(packageName); 21027447Sdfr toolEnv.notice("main.Loading_source_files_for_package", packageName); 21127447Sdfr if (files.isEmpty()) 21227447Sdfr toolEnv.warning("main.no_source_files_for_package", packageName); 21327447Sdfr parse(files, packageTrees, false); 21427447Sdfr } 21527447Sdfr modules.enter(packageTrees.toList(), null); 21627447Sdfr 21727447Sdfr if (messager.nerrors() != 0) { 21827447Sdfr return null; 219127480Sceri } 220127317Sceri 221127317Sceri // Enter symbols for all files 22227447Sdfr toolEnv.notice("main.Building_tree"); 22327447Sdfr javadocEnter.main(classTrees.toList().appendList(packageTrees.toList())); 22427447Sdfr 225154990Sjkoshy enterDone = true; 22637663Scharnier } catch (Abort ex) {} 22727447Sdfr 22881462Sru if (messager.nerrors() != 0) 22981462Sru return null; 23057669Ssheldonh toolEnv.docEnv = new DocEnvImpl(toolEnv, listClasses(classTrees.toList()), 23157669Ssheldonh new ArrayList<>(includedPackages)); 23227447Sdfr return toolEnv.docEnv; 23327447Sdfr } 23427447Sdfr 23527447Sdfr /** Is the given string a valid package name? */ 23627447Sdfr boolean isValidPackageName(String s) { 23727447Sdfr int index; 23827447Sdfr while ((index = s.indexOf('.')) != -1) { 23927447Sdfr if (!isValidClassName(s.substring(0, index))) return false; 24027447Sdfr s = s.substring(index+1); 24127447Sdfr } 242100336Sjoerg return isValidClassName(s); 243100336Sjoerg } 244100336Sjoerg 245100336Sjoerg private void parse(Iterable<? extends JavaFileObject> files, ListBuffer<JCCompilationUnit> trees, 246100336Sjoerg boolean trace) { 247100336Sjoerg for (JavaFileObject fo: files) { 248100336Sjoerg if (uniquefiles.add(fo)) { // ignore duplicates 249100336Sjoerg if (trace) 250100336Sjoerg toolEnv.notice("main.Loading_source_file", fo.getName()); 2511558Srgrimes trees.append(parse(fo)); 2521558Srgrimes } 2531558Srgrimes } 254180071Sdanger } 255180071Sdanger 256180071Sdanger /** Are surrogates supported? 257180071Sdanger */ 258180071Sdanger final static boolean surrogatesSupported = surrogatesSupported(); 259180112Sdanger private static boolean surrogatesSupported() { 260180112Sdanger try { 261180112Sdanger boolean b = Character.isHighSurrogate('a'); 26271099Sru return true; 26337663Scharnier } catch (NoSuchMethodError ex) { 2641558Srgrimes return false; 2651558Srgrimes } 2661558Srgrimes } 2671558Srgrimes 2681558Srgrimes /** 2691558Srgrimes * Return true if given file name is a valid class name 2701558Srgrimes * (including "package-info"). 2711558Srgrimes * @param s the name of the class to check. 272180071Sdanger * @return true if given class name is a valid class name 273180071Sdanger * and false otherwise. 274180071Sdanger */ 2751558Srgrimes public static boolean isValidClassName(String s) { 2761558Srgrimes if (s.length() < 1) return false; 2771558Srgrimes if (s.equals("package-info")) return true; 2781558Srgrimes if (surrogatesSupported) { 2791558Srgrimes int cp = s.codePointAt(0); 280180154Sdanger if (!Character.isJavaIdentifierStart(cp)) 2811558Srgrimes return false; 2821558Srgrimes for (int j=Character.charCount(cp); j<s.length(); j+=Character.charCount(cp)) { 2831558Srgrimes cp = s.codePointAt(j); 2841558Srgrimes if (!Character.isJavaIdentifierPart(cp)) 2851558Srgrimes return false; 286180071Sdanger } 287180071Sdanger } else { 288180071Sdanger if (!Character.isJavaIdentifierStart(s.charAt(0))) 289180071Sdanger return false; 290180071Sdanger for (int j=1; j<s.length(); j++) 291180071Sdanger if (!Character.isJavaIdentifierPart(s.charAt(j))) 292180071Sdanger return false; 2931558Srgrimes } 2941558Srgrimes return true; 29571099Sru } 296100336Sjoerg 297100336Sjoerg /** 298100336Sjoerg * From a list of top level trees, return the list of contained class definitions 2991558Srgrimes */ 300180071Sdanger List<JCClassDecl> listClasses(List<JCCompilationUnit> trees) { 301180071Sdanger List<JCClassDecl> result = new ArrayList<>(); 302180071Sdanger for (JCCompilationUnit t : trees) { 303180112Sdanger for (JCTree def : t.defs) { 304180112Sdanger if (def.hasTag(JCTree.Tag.CLASSDEF)) 305180112Sdanger result.add((JCClassDecl)def); 306180112Sdanger } 307180112Sdanger } 308180112Sdanger return result; 309180071Sdanger } 310192934Srmacklem 311192934Srmacklem /** 312192934Srmacklem * A table to manage included and excluded packages. 313192934Srmacklem */ 314245081Srmacklem class PackageTable { 315245081Srmacklem private final Map<String, Entry> entries = new LinkedHashMap<>(); 316192934Srmacklem private final Set<String> includedPackages = new LinkedHashSet<>(); 317192934Srmacklem private final JavaFileManager fm; 318192934Srmacklem private final Location location; 319192934Srmacklem private final Set<JavaFileObject.Kind> sourceKinds = EnumSet.of(JavaFileObject.Kind.SOURCE); 320192934Srmacklem 321245081Srmacklem /** 322245081Srmacklem * Creates a table to manage included and excluded packages. 323192934Srmacklem * @param fm The file manager used to locate source files 324192934Srmacklem * @param locn the location used to locate source files 325192934Srmacklem */ 326192934Srmacklem PackageTable(JavaFileManager fm, Location locn) { 327100336Sjoerg this.fm = fm; 328192934Srmacklem this.location = locn; 329192934Srmacklem getEntry("").excluded = false; 330192934Srmacklem } 331192934Srmacklem 332192934Srmacklem PackageTable packages(Collection<String> packageNames) { 333192934Srmacklem includedPackages.addAll(packageNames); 334192934Srmacklem return this; 335192934Srmacklem } 336192934Srmacklem 337192934Srmacklem PackageTable subpackages(Collection<String> packageNames, Collection<String> excludePackageNames) 338192934Srmacklem throws IOException { 339192934Srmacklem for (String p: excludePackageNames) { 340100336Sjoerg getEntry(p).excluded = true; 341100336Sjoerg } 342100336Sjoerg 343100336Sjoerg for (String packageName: packageNames) { 3441558Srgrimes for (JavaFileObject fo: fm.list(location, packageName, sourceKinds, true)) { 345180071Sdanger String binaryName = fm.inferBinaryName(location, fo); 346100336Sjoerg String pn = getPackageName(binaryName); 347100336Sjoerg String simpleName = getSimpleName(binaryName); 348100336Sjoerg Entry e = getEntry(pn); 349100336Sjoerg if (!e.isExcluded() && isValidClassName(simpleName)) { 350100336Sjoerg includedPackages.add(pn); 351100336Sjoerg e.files = (e.files == null 352100336Sjoerg ? com.sun.tools.javac.util.List.of(fo) 353100336Sjoerg : e.files.prepend(fo)); 354100336Sjoerg } 355100336Sjoerg } 356100336Sjoerg } 357100336Sjoerg return this; 358100336Sjoerg } 359100336Sjoerg 360100336Sjoerg /** 361100336Sjoerg * Returns the aggregate set of included packages. 362100336Sjoerg * @return the aggregate set of included packages 363100336Sjoerg */ 3641558Srgrimes Set<String> getIncludedPackages() { 3651558Srgrimes return includedPackages; 3661558Srgrimes } 3671558Srgrimes 368180071Sdanger /** 369180071Sdanger * Returns the set of source files for a package. 3701558Srgrimes * @param packageName the specified package 371103716Smarkm * @return the set of file objects for the specified package 372100336Sjoerg * @throws IOException if an error occurs while accessing the files 373184588Sdfr */ 374184588Sdfr List<JavaFileObject> getFiles(String packageName) throws IOException { 375192934Srmacklem Entry e = getEntry(packageName); 376192934Srmacklem // The files may have been found as a side effect of searching for subpackages 3771558Srgrimes if (e.files != null) 3781558Srgrimes return e.files; 3791558Srgrimes 380180154Sdanger ListBuffer<JavaFileObject> lb = new ListBuffer<>(); 3811558Srgrimes Location packageLocn = getLocation(packageName); 382154990Sjkoshy if (packageLocn == null) 3831558Srgrimes return Collections.emptyList(); 384107788Sru for (JavaFileObject fo: fm.list(packageLocn, packageName, sourceKinds, false)) { 385124034Sobrien String binaryName = fm.inferBinaryName(packageLocn, fo); 386180112Sdanger String simpleName = getSimpleName(binaryName); 387154990Sjkoshy if (isValidClassName(simpleName)) { 3881558Srgrimes lb.append(fo); 3891558Srgrimes } 3901558Srgrimes } 3911558Srgrimes 392180112Sdanger return lb.toList(); 393180071Sdanger } 394180071Sdanger 395180071Sdanger private Location getLocation(String packageName) throws IOException { 396180071Sdanger if (location == StandardLocation.MODULE_SOURCE_PATH) { 397180071Sdanger // TODO: handle invalid results better. 398180071Sdanger Name pack = names.fromString(packageName); 3991558Srgrimes 4001558Srgrimes for (ModuleSymbol msym : modules.allModules()) { 4011558Srgrimes PackageSymbol p = syms.getPackage(msym, pack); 4021558Srgrimes if (p != null && !p.members().isEmpty()) { 4031558Srgrimes return fm.getModuleLocation(location, msym.name.toString()); 404180071Sdanger } 405180071Sdanger } 4061558Srgrimes 407180071Sdanger return null; 408180071Sdanger } else { 4091558Srgrimes return location; 410180112Sdanger } 411154990Sjkoshy } 4121558Srgrimes 4131558Srgrimes private Entry getEntry(String name) { 414180112Sdanger Entry e = entries.get(name); 415180071Sdanger if (e == null) 416180071Sdanger entries.put(name, e = new Entry(name)); 4171558Srgrimes return e; 418180112Sdanger } 419154990Sjkoshy 420180071Sdanger private String getPackageName(String name) { 421180071Sdanger int lastDot = name.lastIndexOf("."); 422180112Sdanger return (lastDot == -1 ? "" : name.substring(0, lastDot)); 423180071Sdanger } 424180071Sdanger 425180071Sdanger private String getSimpleName(String name) { 426180071Sdanger int lastDot = name.lastIndexOf("."); 427180071Sdanger return (lastDot == -1 ? name : name.substring(lastDot + 1)); 428103716Smarkm } 42962459Ssheldonh 430180112Sdanger class Entry { 431180071Sdanger final String name; 432180071Sdanger Boolean excluded; 433180112Sdanger com.sun.tools.javac.util.List<JavaFileObject> files; 434180112Sdanger 435180112Sdanger Entry(String name) { 436180112Sdanger this.name = name; 437180112Sdanger } 438180112Sdanger 439180112Sdanger boolean isExcluded() { 440180071Sdanger if (excluded == null) 441180112Sdanger excluded = getEntry(getPackageName(name)).isExcluded(); 442180071Sdanger return excluded; 443180112Sdanger } 444180112Sdanger } 445180112Sdanger } 446180071Sdanger 447180071Sdanger} 448180112Sdanger