Path: blob/master/src/jdk.javadoc/share/classes/jdk/javadoc/internal/tool/JavadocTool.java
41161 views
/*1* Copyright (c) 2001, 2021, Oracle and/or its affiliates. All rights reserved.2* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.3*4* This code is free software; you can redistribute it and/or modify it5* under the terms of the GNU General Public License version 2 only, as6* published by the Free Software Foundation. Oracle designates this7* particular file as subject to the "Classpath" exception as provided8* by Oracle in the LICENSE file that accompanied this code.9*10* This code is distributed in the hope that it will be useful, but WITHOUT11* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or12* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License13* version 2 for more details (a copy is included in the LICENSE file that14* accompanied this code).15*16* You should have received a copy of the GNU General Public License version17* 2 along with this work; if not, write to the Free Software Foundation,18* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.19*20* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA21* or visit www.oracle.com if you need additional information or have any22* questions.23*/2425package jdk.javadoc.internal.tool;2627import java.nio.file.Files;28import java.nio.file.InvalidPathException;29import java.nio.file.Paths;30import java.util.ArrayList;31import java.util.HashSet;32import java.util.LinkedHashSet;33import java.util.List;34import java.util.Set;3536import javax.lang.model.element.Element;37import javax.lang.model.element.ElementKind;38import javax.tools.JavaFileObject;39import javax.tools.StandardJavaFileManager;4041import com.sun.tools.javac.code.ClassFinder;42import com.sun.tools.javac.code.DeferredCompletionFailureHandler;43import com.sun.tools.javac.code.Symbol.Completer;44import com.sun.tools.javac.code.Symbol.CompletionFailure;45import com.sun.tools.javac.code.Symbol.PackageSymbol;46import com.sun.tools.javac.comp.Enter;47import com.sun.tools.javac.tree.JCTree;48import com.sun.tools.javac.tree.JCTree.JCClassDecl;49import com.sun.tools.javac.tree.JCTree.JCCompilationUnit;50import com.sun.tools.javac.util.Abort;51import com.sun.tools.javac.util.Context;52import com.sun.tools.javac.util.ListBuffer;53import com.sun.tools.javac.util.Position;54import jdk.javadoc.doclet.DocletEnvironment;5556import static jdk.javadoc.internal.tool.Main.Result.*;5758/**59* This class could be the main entry point for Javadoc when Javadoc is used as a60* component in a larger software system. It provides operations to61* construct a new javadoc processor, and to run it on a set of source62* files.63*64* <p><b>This is NOT part of any supported API.65* If you write code that depends on this, you do so at your own risk.66* This code and its internal interfaces are subject to change or67* deletion without notice.</b>68*/69public class JavadocTool extends com.sun.tools.javac.main.JavaCompiler {70ToolEnvironment toolEnv;7172final Messager messager;73final ClassFinder javadocFinder;74final DeferredCompletionFailureHandler dcfh;75final Enter javadocEnter;76final Set<JavaFileObject> uniquefiles;7778/**79* Construct a new JavaCompiler processor, using appropriately80* extended phases of the underlying compiler.81*/82protected JavadocTool(Context context) {83super(context);84messager = Messager.instance0(context);85javadocFinder = JavadocClassFinder.instance(context);86dcfh = DeferredCompletionFailureHandler.instance(context);87javadocEnter = JavadocEnter.instance(context);88uniquefiles = new HashSet<>();89}9091/**92* For javadoc, the parser needs to keep comments. Overrides method from JavaCompiler.93*/94@Override95protected boolean keepComments() {96return true;97}9899/**100* Construct a new javadoc tool.101*/102public static JavadocTool make0(Context context) {103Messager messager = null;104try {105// force the use of Javadoc's class finder106JavadocClassFinder.preRegister(context);107108// force the use of Javadoc's own enter phase109JavadocEnter.preRegister(context);110111// force the use of Javadoc's own member enter phase112JavadocMemberEnter.preRegister(context);113114// force the use of Javadoc's own todo phase115JavadocTodo.preRegister(context);116117// force the use of Messager as a Log118messager = Messager.instance0(context);119120return new JavadocTool(context);121} catch (CompletionFailure ex) {122assert messager != null;123messager.error(Position.NOPOS, ex.getMessage());124return null;125}126}127128public DocletEnvironment getEnvironment(ToolOptions toolOptions,129List<String> javaNames,130Iterable<? extends JavaFileObject> fileObjects)131throws ToolException132{133toolEnv = ToolEnvironment.instance(context);134toolEnv.initialize(toolOptions);135ElementsTable etable = new ElementsTable(context, toolOptions);136javadocFinder.sourceCompleter = etable.xclasses137? Completer.NULL_COMPLETER138: sourceCompleter;139140if (etable.xclasses) {141// If -Xclasses is set, the args should be a list of class names142for (String arg: javaNames) {143if (!isValidPackageName(arg)) { // checks144String text = messager.getText("main.illegal_class_name", arg);145throw new ToolException(CMDERR, text);146}147}148if (messager.hasErrors()) {149return null;150}151etable.setClassArgList(javaNames);152// prepare, force the data structures to be analyzed153etable.analyze();154return new DocEnvImpl(toolEnv, etable);155}156157ListBuffer<JCCompilationUnit> classTrees = new ListBuffer<>();158159try {160StandardJavaFileManager fm = toolEnv.fileManager instanceof StandardJavaFileManager sfm161? sfm162: null;163Set<String> packageNames = new LinkedHashSet<>();164// Normally, the args should be a series of package names or file names.165// Parse the files and collect the package names.166for (String arg: javaNames) {167if (fm != null && arg.endsWith(".java") && isRegularFile(arg)) {168parse(fm.getJavaFileObjects(arg), classTrees, true);169} else if (isValidPackageName(arg)) {170packageNames.add(arg);171} else if (arg.endsWith(".java")) {172if (fm == null) {173String text = messager.getText("main.assertion.error", "fm == null");174throw new ToolException(ABNORMAL, text);175} else {176String text = messager.getText("main.file_not_found", arg);177throw new ToolException(ERROR, text);178}179} else {180String text = messager.getText("main.illegal_package_name", arg);181throw new ToolException(CMDERR, text);182}183}184185// Parse file objects provide via the DocumentationTool API186parse(fileObjects, classTrees, true);187188etable.packages(packageNames)189.classTrees(classTrees.toList())190.scanSpecifiedItems();191192// abort, if errors were encountered during modules initialization193if (messager.hasErrors()) {194return null;195}196197// Parse the files in the packages and subpackages to be documented198ListBuffer<JCCompilationUnit> allTrees = new ListBuffer<>();199allTrees.addAll(classTrees);200parse(etable.getFilesToParse(), allTrees, false);201modules.newRound();202modules.initModules(allTrees.toList());203204if (messager.hasErrors()) {205return null;206}207208// Enter symbols for all files209toolEnv.notice("main.Building_tree");210javadocEnter.main(allTrees.toList());211212if (messager.hasErrors()) {213return null;214}215216etable.setClassDeclList(listClasses(classTrees.toList()));217218dcfh.setHandler(dcfh.userCodeHandler);219etable.analyze();220221// Ensure that package-info is read for all included packages222for (Element e : etable.getIncludedElements()) {223if (e.getKind() == ElementKind.PACKAGE) {224PackageSymbol p = (PackageSymbol) e;225if (p.package_info != null) {226p.package_info.complete();227}228}229}230231} catch (CompletionFailure cf) {232throw new ToolException(ABNORMAL, cf.getMessage(), cf);233} catch (Abort abort) {234if (messager.hasErrors()) {235// presumably a message has been emitted, keep silent236throw new ToolException(ABNORMAL, "", abort);237} else {238String text = messager.getText("main.internal.error");239Throwable t = abort.getCause() == null ? abort : abort.getCause();240throw new ToolException(ABNORMAL, text, t);241}242}243244if (messager.hasErrors())245return null;246247toolEnv.docEnv = new DocEnvImpl(toolEnv, etable);248return toolEnv.docEnv;249}250251private boolean isRegularFile(String s) {252try {253return Files.isRegularFile(Paths.get(s));254} catch (InvalidPathException e) {255return false;256}257}258259/** Is the given string a valid package name? */260boolean isValidPackageName(String s) {261if (s.contains("/")) {262String[] a = s.split("/");263if (a.length == 2) {264return isValidPackageName0(a[0]) && isValidPackageName0(a[1]);265}266return false;267}268return isValidPackageName0(s);269}270271private boolean isValidPackageName0(String s) {272for (int index = s.indexOf('.') ; index != -1; index = s.indexOf('.')) {273if (!isValidClassName(s.substring(0, index))) {274return false;275}276s = s.substring(index + 1);277}278return isValidClassName(s);279}280281private void parse(Iterable<? extends JavaFileObject> files, ListBuffer<JCCompilationUnit> trees,282boolean trace) {283for (JavaFileObject fo: files) {284if (uniquefiles.add(fo)) { // ignore duplicates285if (trace)286toolEnv.notice("main.Loading_source_file", fo.getName());287trees.append(parse(fo));288}289}290}291292/** Are surrogates supported? */293static final boolean surrogatesSupported = surrogatesSupported();294private static boolean surrogatesSupported() {295try {296boolean b = Character.isHighSurrogate('a');297return true;298} catch (NoSuchMethodError ex) {299return false;300}301}302303/**304* Return true if given file name is a valid class name305* (including "package-info").306* @param s the name of the class to check.307* @return true if given class name is a valid class name308* and false otherwise.309*/310public static boolean isValidClassName(String s) {311if (s.length() < 1) return false;312if (s.equals("package-info")) return true;313if (surrogatesSupported) {314int cp = s.codePointAt(0);315if (!Character.isJavaIdentifierStart(cp))316return false;317for (int j = Character.charCount(cp); j < s.length(); j += Character.charCount(cp)) {318cp = s.codePointAt(j);319if (!Character.isJavaIdentifierPart(cp))320return false;321}322} else {323if (!Character.isJavaIdentifierStart(s.charAt(0)))324return false;325for (int j = 1; j < s.length(); j++)326if (!Character.isJavaIdentifierPart(s.charAt(j)))327return false;328}329return true;330}331332/**333* From a list of top level trees, return the list of contained class definitions334*/335List<JCClassDecl> listClasses(List<JCCompilationUnit> trees) {336List<JCClassDecl> result = new ArrayList<>();337for (JCCompilationUnit t : trees) {338for (JCTree def : t.defs) {339if (def.hasTag(JCTree.Tag.CLASSDEF))340result.add((JCClassDecl)def);341}342}343return result;344}345}346347348