langtools/src/jdk.javadoc/share/classes/com/sun/tools/javadoc/JavadocTool.java
changeset 37938 42baa89d2156
parent 37858 7c04fcb12bd4
child 37939 3eb8c2a89b77
equal deleted inserted replaced
37858:7c04fcb12bd4 37938:42baa89d2156
     1 /*
       
     2  * Copyright (c) 2001, 2016, Oracle and/or its affiliates. All rights reserved.
       
     3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
       
     4  *
       
     5  * This code is free software; you can redistribute it and/or modify it
       
     6  * under the terms of the GNU General Public License version 2 only, as
       
     7  * published by the Free Software Foundation.  Oracle designates this
       
     8  * particular file as subject to the "Classpath" exception as provided
       
     9  * by Oracle in the LICENSE file that accompanied this code.
       
    10  *
       
    11  * This code is distributed in the hope that it will be useful, but WITHOUT
       
    12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
       
    13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
       
    14  * version 2 for more details (a copy is included in the LICENSE file that
       
    15  * accompanied this code).
       
    16  *
       
    17  * You should have received a copy of the GNU General Public License version
       
    18  * 2 along with this work; if not, write to the Free Software Foundation,
       
    19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
       
    20  *
       
    21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
       
    22  * or visit www.oracle.com if you need additional information or have any
       
    23  * questions.
       
    24  */
       
    25 
       
    26 package com.sun.tools.javadoc;
       
    27 
       
    28 import java.io.File;
       
    29 import java.io.IOException;
       
    30 import java.util.Collection;
       
    31 import java.util.EnumSet;
       
    32 import java.util.HashSet;
       
    33 import java.util.LinkedHashMap;
       
    34 import java.util.LinkedHashSet;
       
    35 import java.util.Map;
       
    36 import java.util.Set;
       
    37 
       
    38 import javax.tools.JavaFileManager;
       
    39 import javax.tools.JavaFileManager.Location;
       
    40 import javax.tools.JavaFileObject;
       
    41 import javax.tools.StandardJavaFileManager;
       
    42 import javax.tools.StandardLocation;
       
    43 
       
    44 import com.sun.tools.javac.code.ClassFinder;
       
    45 import com.sun.tools.javac.code.Symbol.Completer;
       
    46 import com.sun.tools.javac.code.Symbol.ModuleSymbol;
       
    47 import com.sun.tools.javac.comp.Enter;
       
    48 import com.sun.tools.javac.tree.JCTree;
       
    49 import com.sun.tools.javac.tree.JCTree.JCClassDecl;
       
    50 import com.sun.tools.javac.tree.JCTree.JCCompilationUnit;
       
    51 import com.sun.tools.javac.util.Abort;
       
    52 import com.sun.tools.javac.util.Context;
       
    53 import com.sun.tools.javac.util.List;
       
    54 import com.sun.tools.javac.util.ListBuffer;
       
    55 
       
    56 
       
    57 /**
       
    58  *  This class could be the main entry point for Javadoc when Javadoc is used as a
       
    59  *  component in a larger software system. It provides operations to
       
    60  *  construct a new javadoc processor, and to run it on a set of source
       
    61  *  files.
       
    62  *
       
    63  *  <p><b>This is NOT part of any supported API.
       
    64  *  If you write code that depends on this, you do so at your own risk.
       
    65  *  This code and its internal interfaces are subject to change or
       
    66  *  deletion without notice.</b>
       
    67  *
       
    68  *  @author Neal Gafter
       
    69  */
       
    70 public class JavadocTool extends com.sun.tools.javac.main.JavaCompiler {
       
    71     DocEnv docenv;
       
    72 
       
    73     final Messager messager;
       
    74     final ClassFinder javadocFinder;
       
    75     final Enter javadocEnter;
       
    76     final Set<JavaFileObject> uniquefiles;
       
    77 
       
    78     /**
       
    79      * Construct a new JavaCompiler processor, using appropriately
       
    80      * extended phases of the underlying compiler.
       
    81      */
       
    82     protected JavadocTool(Context context) {
       
    83         super(context);
       
    84         messager = Messager.instance0(context);
       
    85         javadocFinder = JavadocClassFinder.instance(context);
       
    86         javadocEnter = JavadocEnter.instance(context);
       
    87         uniquefiles = new HashSet<>();
       
    88     }
       
    89 
       
    90     /**
       
    91      * For javadoc, the parser needs to keep comments. Overrides method from JavaCompiler.
       
    92      */
       
    93     @Override
       
    94     protected boolean keepComments() {
       
    95         return true;
       
    96     }
       
    97 
       
    98     /**
       
    99      *  Construct a new javadoc tool.
       
   100      */
       
   101     public static JavadocTool make0(Context context) {
       
   102         // force the use of Javadoc's class finder
       
   103         JavadocClassFinder.preRegister(context);
       
   104 
       
   105         // force the use of Javadoc's own enter phase
       
   106         JavadocEnter.preRegister(context);
       
   107 
       
   108         // force the use of Javadoc's own member enter phase
       
   109         JavadocMemberEnter.preRegister(context);
       
   110 
       
   111         // force the use of Javadoc's own todo phase
       
   112         JavadocTodo.preRegister(context);
       
   113 
       
   114         // force the use of Messager as a Log
       
   115         Messager.instance0(context);
       
   116 
       
   117         return new JavadocTool(context);
       
   118     }
       
   119 
       
   120     public RootDocImpl getRootDocImpl(String doclocale,
       
   121                                       String encoding,
       
   122                                       ModifierFilter filter,
       
   123                                       List<String> args,
       
   124                                       List<String[]> options,
       
   125                                       Iterable<? extends JavaFileObject> fileObjects,
       
   126                                       boolean breakiterator,
       
   127                                       List<String> subPackages,
       
   128                                       List<String> excludedPackages,
       
   129                                       boolean docClasses,
       
   130                                       boolean legacyDoclet,
       
   131                       boolean quiet) throws IOException {
       
   132         docenv = DocEnv.instance(context);
       
   133         docenv.showAccess = filter;
       
   134         docenv.quiet = quiet;
       
   135         docenv.breakiterator = breakiterator;
       
   136         docenv.setLocale(doclocale);
       
   137         docenv.setEncoding(encoding);
       
   138         docenv.docClasses = docClasses;
       
   139         docenv.legacyDoclet = legacyDoclet;
       
   140 
       
   141         javadocFinder.sourceCompleter = docClasses ? Completer.NULL_COMPLETER : sourceCompleter;
       
   142 
       
   143         if (docClasses) {
       
   144             // If -Xclasses is set, the args should be a series of class names
       
   145             for (String arg: args) {
       
   146                 if (!isValidPackageName(arg)) // checks
       
   147                     docenv.error(null, "main.illegal_class_name", arg);
       
   148             }
       
   149             if (messager.nerrors() != 0) {
       
   150                 return null;
       
   151             }
       
   152             return new RootDocImpl(docenv, args, options);
       
   153         }
       
   154 
       
   155         ListBuffer<JCCompilationUnit> classTrees = new ListBuffer<>();
       
   156         Set<String> includedPackages = new LinkedHashSet<>();
       
   157 
       
   158         try {
       
   159             StandardJavaFileManager fm = docenv.fileManager instanceof StandardJavaFileManager
       
   160                     ? (StandardJavaFileManager) docenv.fileManager : null;
       
   161             Set<String> packageNames = new LinkedHashSet<>();
       
   162             // Normally, the args should be a series of package names or file names.
       
   163             // Parse the files and collect the package names.
       
   164             for (String arg: args) {
       
   165                 if (fm != null && arg.endsWith(".java") && new File(arg).exists()) {
       
   166                     if (new File(arg).getName().equals("module-info.java")) {
       
   167                         docenv.warning(null, "main.file_ignored", arg);
       
   168                     } else {
       
   169                         parse(fm.getJavaFileObjects(arg), classTrees, true);
       
   170                     }
       
   171                 } else if (isValidPackageName(arg)) {
       
   172                     packageNames.add(arg);
       
   173                 } else if (arg.endsWith(".java")) {
       
   174                     if (fm == null)
       
   175                         throw new IllegalArgumentException();
       
   176                     else
       
   177                         docenv.error(null, "main.file_not_found", arg);
       
   178                 } else {
       
   179                     docenv.error(null, "main.illegal_package_name", arg);
       
   180                 }
       
   181             }
       
   182 
       
   183             // Parse file objects provide via the DocumentationTool API
       
   184             parse(fileObjects, classTrees, true);
       
   185             modules.enter(classTrees.toList(), null);
       
   186 
       
   187             syms.unnamedModule.complete(); // TEMP to force reading all named modules
       
   188 
       
   189             // Build up the complete list of any packages to be documented
       
   190             Location location =
       
   191                     modules.multiModuleMode && !modules.noModules ? StandardLocation.MODULE_SOURCE_PATH
       
   192                     : docenv.fileManager.hasLocation(StandardLocation.SOURCE_PATH) ? StandardLocation.SOURCE_PATH
       
   193                     : StandardLocation.CLASS_PATH;
       
   194 
       
   195             PackageTable t = new PackageTable(docenv.fileManager, location)
       
   196                     .packages(packageNames)
       
   197                     .subpackages(subPackages, excludedPackages);
       
   198 
       
   199             includedPackages = t.getIncludedPackages();
       
   200 
       
   201             // Parse the files in the packages to be documented
       
   202             ListBuffer<JCCompilationUnit> packageTrees = new ListBuffer<>();
       
   203             for (String packageName: includedPackages) {
       
   204                 List<JavaFileObject> files = t.getFiles(packageName);
       
   205                 docenv.notice("main.Loading_source_files_for_package", packageName);
       
   206 
       
   207                 if (files.isEmpty())
       
   208                     messager.warning(Messager.NOPOS, "main.no_source_files_for_package", packageName);
       
   209                 parse(files, packageTrees, false);
       
   210             }
       
   211             modules.enter(packageTrees.toList(), null);
       
   212 
       
   213             if (messager.nerrors() != 0) {
       
   214                 return null;
       
   215             }
       
   216 
       
   217             // Enter symbols for all files
       
   218             docenv.notice("main.Building_tree");
       
   219             javadocEnter.main(classTrees.toList().appendList(packageTrees.toList()));
       
   220             enterDone = true;
       
   221         } catch (Abort ex) {}
       
   222 
       
   223         if (messager.nerrors() != 0)
       
   224             return null;
       
   225 
       
   226         return new RootDocImpl(docenv, listClasses(classTrees.toList()), List.from(includedPackages), options);
       
   227     }
       
   228 
       
   229     /** Is the given string a valid package name? */
       
   230     boolean isValidPackageName(String s) {
       
   231         int index;
       
   232         while ((index = s.indexOf('.')) != -1) {
       
   233             if (!isValidClassName(s.substring(0, index))) return false;
       
   234             s = s.substring(index+1);
       
   235         }
       
   236         return isValidClassName(s);
       
   237     }
       
   238 
       
   239     private void parse(Iterable<? extends JavaFileObject> files, ListBuffer<JCCompilationUnit> trees,
       
   240                        boolean trace) {
       
   241         for (JavaFileObject fo: files) {
       
   242             if (uniquefiles.add(fo)) { // ignore duplicates
       
   243                 if (trace)
       
   244                     docenv.notice("main.Loading_source_file", fo.getName());
       
   245                 trees.append(parse(fo));
       
   246             }
       
   247         }
       
   248     }
       
   249 
       
   250     /** Are surrogates supported?
       
   251      */
       
   252     final static boolean surrogatesSupported = surrogatesSupported();
       
   253     private static boolean surrogatesSupported() {
       
   254         try {
       
   255             boolean b = Character.isHighSurrogate('a');
       
   256             return true;
       
   257         } catch (NoSuchMethodError ex) {
       
   258             return false;
       
   259         }
       
   260     }
       
   261 
       
   262     /**
       
   263      * Return true if given file name is a valid class name
       
   264      * (including "package-info").
       
   265      * @param s the name of the class to check.
       
   266      * @return true if given class name is a valid class name
       
   267      * and false otherwise.
       
   268      */
       
   269     public static boolean isValidClassName(String s) {
       
   270         if (s.length() < 1) return false;
       
   271         if (s.equals("package-info")) return true;
       
   272         if (surrogatesSupported) {
       
   273             int cp = s.codePointAt(0);
       
   274             if (!Character.isJavaIdentifierStart(cp))
       
   275                 return false;
       
   276             for (int j=Character.charCount(cp); j<s.length(); j+=Character.charCount(cp)) {
       
   277                 cp = s.codePointAt(j);
       
   278                 if (!Character.isJavaIdentifierPart(cp))
       
   279                     return false;
       
   280             }
       
   281         } else {
       
   282             if (!Character.isJavaIdentifierStart(s.charAt(0)))
       
   283                 return false;
       
   284             for (int j=1; j<s.length(); j++)
       
   285                 if (!Character.isJavaIdentifierPart(s.charAt(j)))
       
   286                     return false;
       
   287         }
       
   288         return true;
       
   289     }
       
   290 
       
   291     /**
       
   292      * From a list of top level trees, return the list of contained class definitions
       
   293      */
       
   294     List<JCClassDecl> listClasses(List<JCCompilationUnit> trees) {
       
   295         ListBuffer<JCClassDecl> result = new ListBuffer<>();
       
   296         for (JCCompilationUnit t : trees) {
       
   297             for (JCTree def : t.defs) {
       
   298                 if (def.hasTag(JCTree.Tag.CLASSDEF))
       
   299                     result.append((JCClassDecl)def);
       
   300             }
       
   301         }
       
   302         return result.toList();
       
   303     }
       
   304 
       
   305     /**
       
   306      * A table to manage included and excluded packages.
       
   307      */
       
   308     class PackageTable {
       
   309         private final Map<String, Entry> entries = new LinkedHashMap<>();
       
   310         private final Set<String> includedPackages = new LinkedHashSet<>();
       
   311         private final JavaFileManager fm;
       
   312         private final Location location;
       
   313         private final Set<JavaFileObject.Kind> sourceKinds = EnumSet.of(JavaFileObject.Kind.SOURCE);
       
   314 
       
   315         /**
       
   316          * Creates a table to manage included and excluded packages.
       
   317          * @param fm The file manager used to locate source files
       
   318          * @param locn the location used to locate source files
       
   319          */
       
   320         PackageTable(JavaFileManager fm, Location locn) {
       
   321             this.fm = fm;
       
   322             this.location = locn;
       
   323             getEntry("").excluded = false;
       
   324         }
       
   325 
       
   326         PackageTable packages(Collection<String> packageNames) {
       
   327             includedPackages.addAll(packageNames);
       
   328             return this;
       
   329         }
       
   330 
       
   331         PackageTable subpackages(Collection<String> packageNames, Collection<String> excludePackageNames)
       
   332                 throws IOException {
       
   333             for (String p: excludePackageNames) {
       
   334                 getEntry(p).excluded = true;
       
   335             }
       
   336 
       
   337             for (String packageName: packageNames) {
       
   338                 Location packageLocn = getLocation(packageName);
       
   339                 for (JavaFileObject fo: fm.list(packageLocn, packageName, sourceKinds, true)) {
       
   340                     String binaryName = fm.inferBinaryName(packageLocn, fo);
       
   341                     String pn = getPackageName(binaryName);
       
   342                     String simpleName = getSimpleName(binaryName);
       
   343                     Entry e = getEntry(pn);
       
   344                     if (!e.isExcluded() && isValidClassName(simpleName)) {
       
   345                         includedPackages.add(pn);
       
   346                         e.files = (e.files == null ? List.of(fo) : e.files.prepend(fo));
       
   347                     }
       
   348                 }
       
   349             }
       
   350             return this;
       
   351         }
       
   352 
       
   353         /**
       
   354          * Returns the aggregate set of included packages.
       
   355          * @return the aggregate set of included packages
       
   356          */
       
   357         Set<String> getIncludedPackages() {
       
   358             return includedPackages;
       
   359         }
       
   360 
       
   361         /**
       
   362          * Returns the set of source files for a package.
       
   363          * @param packageName the specified package
       
   364          * @return the set of file objects for the specified package
       
   365          * @throws IOException if an error occurs while accessing the files
       
   366          */
       
   367         List<JavaFileObject> getFiles(String packageName) throws IOException {
       
   368             Entry e = getEntry(packageName);
       
   369             // The files may have been found as a side effect of searching for subpackages
       
   370             if (e.files != null)
       
   371                 return e.files;
       
   372 
       
   373             ListBuffer<JavaFileObject> lb = new ListBuffer<>();
       
   374             Location packageLocn = getLocation(packageName);
       
   375             for (JavaFileObject fo: fm.list(packageLocn, packageName, sourceKinds, false)) {
       
   376                 String binaryName = fm.inferBinaryName(packageLocn, fo);
       
   377                 String simpleName = getSimpleName(binaryName);
       
   378                 if (isValidClassName(simpleName)) {
       
   379                     lb.append(fo);
       
   380                 }
       
   381             }
       
   382 
       
   383             return lb.toList();
       
   384         }
       
   385 
       
   386         private Location getLocation(String packageName) throws IOException {
       
   387             if (location == StandardLocation.MODULE_SOURCE_PATH) {
       
   388                 // TODO: handle invalid results
       
   389                 ModuleSymbol msym = syms.inferModule(names.fromString(packageName));
       
   390                 return fm.getModuleLocation(location, msym.name.toString());
       
   391             } else {
       
   392                 return location;
       
   393             }
       
   394         }
       
   395 
       
   396         private Entry getEntry(String name) {
       
   397             Entry e = entries.get(name);
       
   398             if (e == null)
       
   399                 entries.put(name, e = new Entry(name));
       
   400             return e;
       
   401         }
       
   402 
       
   403         private String getPackageName(String name) {
       
   404             int lastDot = name.lastIndexOf(".");
       
   405             return (lastDot == -1 ? "" : name.substring(0, lastDot));
       
   406         }
       
   407 
       
   408         private String getSimpleName(String name) {
       
   409             int lastDot = name.lastIndexOf(".");
       
   410             return (lastDot == -1 ? name : name.substring(lastDot + 1));
       
   411         }
       
   412 
       
   413         class Entry {
       
   414             final String name;
       
   415             Boolean excluded;
       
   416             List<JavaFileObject> files;
       
   417 
       
   418             Entry(String name) {
       
   419                 this.name = name;
       
   420             }
       
   421 
       
   422             boolean isExcluded() {
       
   423                 if (excluded == null)
       
   424                     excluded = getEntry(getPackageName(name)).isExcluded();
       
   425                 return excluded;
       
   426             }
       
   427         }
       
   428     }
       
   429 
       
   430 }