Path: blob/aarch64-shenandoah-jdk8u272-b10/langtools/src/share/classes/com/sun/tools/javadoc/JavadocTool.java
38899 views
/*1* Copyright (c) 2001, 2014, 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 com.sun.tools.javadoc;2627import java.io.File;28import java.io.IOException;29import java.util.Collection;30import java.util.EnumSet;31import java.util.HashMap;32import java.util.HashSet;33import java.util.Map;34import java.util.Set;35import javax.tools.JavaFileManager.Location;36import javax.tools.JavaFileObject;37import javax.tools.StandardJavaFileManager;38import javax.tools.StandardLocation;3940import com.sun.tools.javac.code.Symbol.CompletionFailure;41import com.sun.tools.javac.tree.JCTree;42import com.sun.tools.javac.tree.JCTree.JCClassDecl;43import com.sun.tools.javac.tree.JCTree.JCCompilationUnit;44import com.sun.tools.javac.util.Abort;45import com.sun.tools.javac.util.Context;46import com.sun.tools.javac.util.List;47import com.sun.tools.javac.util.ListBuffer;48import com.sun.tools.javac.util.Position;495051/**52* This class could be the main entry point for Javadoc when Javadoc is used as a53* component in a larger software system. It provides operations to54* construct a new javadoc processor, and to run it on a set of source55* files.56*57* <p><b>This is NOT part of any supported API.58* If you write code that depends on this, you do so at your own risk.59* This code and its internal interfaces are subject to change or60* deletion without notice.</b>61*62* @author Neal Gafter63*/64public class JavadocTool extends com.sun.tools.javac.main.JavaCompiler {65DocEnv docenv;6667final Messager messager;68final JavadocClassReader javadocReader;69final JavadocEnter javadocEnter;70final Set<JavaFileObject> uniquefiles;7172/**73* Construct a new JavaCompiler processor, using appropriately74* extended phases of the underlying compiler.75*/76protected JavadocTool(Context context) {77super(context);78messager = Messager.instance0(context);79javadocReader = JavadocClassReader.instance0(context);80javadocEnter = JavadocEnter.instance0(context);81uniquefiles = new HashSet<>();82}8384/**85* For javadoc, the parser needs to keep comments. Overrides method from JavaCompiler.86*/87protected boolean keepComments() {88return true;89}9091/**92* Construct a new javadoc tool.93*/94public static JavadocTool make0(Context context) {95Messager messager = null;96try {97// force the use of Javadoc's class reader98JavadocClassReader.preRegister(context);99100// force the use of Javadoc's own enter phase101JavadocEnter.preRegister(context);102103// force the use of Javadoc's own member enter phase104JavadocMemberEnter.preRegister(context);105106// force the use of Javadoc's own todo phase107JavadocTodo.preRegister(context);108109// force the use of Messager as a Log110messager = Messager.instance0(context);111112return new JavadocTool(context);113} catch (CompletionFailure ex) {114messager.error(Position.NOPOS, ex.getMessage());115return null;116}117}118119public RootDocImpl getRootDocImpl(String doclocale,120String encoding,121ModifierFilter filter,122List<String> javaNames,123List<String[]> options,124Iterable<? extends JavaFileObject> fileObjects,125boolean breakiterator,126List<String> subPackages,127List<String> excludedPackages,128boolean docClasses,129boolean legacyDoclet,130boolean quiet) throws IOException {131docenv = DocEnv.instance(context);132docenv.showAccess = filter;133docenv.quiet = quiet;134docenv.breakiterator = breakiterator;135docenv.setLocale(doclocale);136docenv.setEncoding(encoding);137docenv.docClasses = docClasses;138docenv.legacyDoclet = legacyDoclet;139javadocReader.sourceCompleter = docClasses ? null : thisCompleter;140141ListBuffer<String> names = new ListBuffer<String>();142ListBuffer<JCCompilationUnit> classTrees = new ListBuffer<JCCompilationUnit>();143ListBuffer<JCCompilationUnit> packTrees = new ListBuffer<JCCompilationUnit>();144145try {146StandardJavaFileManager fm = docenv.fileManager instanceof StandardJavaFileManager147? (StandardJavaFileManager) docenv.fileManager : null;148for (List<String> it = javaNames; it.nonEmpty(); it = it.tail) {149String name = it.head;150if (!docClasses && fm != null && name.endsWith(".java") && new File(name).exists()) {151JavaFileObject fo = fm.getJavaFileObjects(name).iterator().next();152parse(fo, classTrees, true);153} else if (isValidPackageName(name)) {154names = names.append(name);155} else if (name.endsWith(".java")) {156if (fm == null)157throw new IllegalArgumentException();158else159docenv.error(null, "main.file_not_found", name);160} else {161docenv.error(null, "main.illegal_package_name", name);162}163}164for (JavaFileObject fo: fileObjects) {165parse(fo, classTrees, true);166}167168if (!docClasses) {169// Recursively search given subpackages. If any packages170//are found, add them to the list.171Map<String,List<JavaFileObject>> packageFiles =172searchSubPackages(subPackages, names, excludedPackages);173174// Parse the packages175for (List<String> packs = names.toList(); packs.nonEmpty(); packs = packs.tail) {176// Parse sources ostensibly belonging to package.177String packageName = packs.head;178parsePackageClasses(packageName, packageFiles.get(packageName), packTrees, excludedPackages);179}180181if (messager.nerrors() != 0) return null;182183// Enter symbols for all files184docenv.notice("main.Building_tree");185javadocEnter.main(classTrees.toList().appendList(packTrees.toList()));186}187} catch (Abort ex) {}188189if (messager.nerrors() != 0)190return null;191192if (docClasses)193return new RootDocImpl(docenv, javaNames, options);194else195return new RootDocImpl(docenv, listClasses(classTrees.toList()), names.toList(), options);196}197198/** Is the given string a valid package name? */199boolean isValidPackageName(String s) {200int index;201while ((index = s.indexOf('.')) != -1) {202if (!isValidClassName(s.substring(0, index))) return false;203s = s.substring(index+1);204}205return isValidClassName(s);206}207208/**209* search all directories in path for subdirectory name. Add all210* .java files found in such a directory to args.211*/212private void parsePackageClasses(String name,213List<JavaFileObject> files,214ListBuffer<JCCompilationUnit> trees,215List<String> excludedPackages)216throws IOException {217if (excludedPackages.contains(name)) {218return;219}220221docenv.notice("main.Loading_source_files_for_package", name);222223if (files == null) {224Location location = docenv.fileManager.hasLocation(StandardLocation.SOURCE_PATH)225? StandardLocation.SOURCE_PATH : StandardLocation.CLASS_PATH;226ListBuffer<JavaFileObject> lb = new ListBuffer<JavaFileObject>();227for (JavaFileObject fo: docenv.fileManager.list(228location, name, EnumSet.of(JavaFileObject.Kind.SOURCE), false)) {229String binaryName = docenv.fileManager.inferBinaryName(location, fo);230String simpleName = getSimpleName(binaryName);231if (isValidClassName(simpleName)) {232lb.append(fo);233}234}235files = lb.toList();236}237if (files.nonEmpty()) {238for (JavaFileObject fo : files) {239parse(fo, trees, false);240}241} else {242messager.warning(Messager.NOPOS, "main.no_source_files_for_package",243name.replace(File.separatorChar, '.'));244}245}246247private void parse(JavaFileObject fo, ListBuffer<JCCompilationUnit> trees,248boolean trace) {249if (uniquefiles.add(fo)) { // ignore duplicates250if (trace)251docenv.notice("main.Loading_source_file", fo.getName());252trees.append(parse(fo));253}254}255256/**257* Recursively search all directories in path for subdirectory name.258* Add all packages found in such a directory to packages list.259*/260private Map<String,List<JavaFileObject>> searchSubPackages(261List<String> subPackages,262ListBuffer<String> packages,263List<String> excludedPackages)264throws IOException {265Map<String,List<JavaFileObject>> packageFiles =266new HashMap<String,List<JavaFileObject>>();267268Map<String,Boolean> includedPackages = new HashMap<String,Boolean>();269includedPackages.put("", true);270for (String p: excludedPackages)271includedPackages.put(p, false);272273StandardLocation path = docenv.fileManager.hasLocation(StandardLocation.SOURCE_PATH)274? StandardLocation.SOURCE_PATH : StandardLocation.CLASS_PATH;275276searchSubPackages(subPackages,277includedPackages,278packages, packageFiles,279path,280EnumSet.of(JavaFileObject.Kind.SOURCE));281282return packageFiles;283}284285private void searchSubPackages(List<String> subPackages,286Map<String,Boolean> includedPackages,287ListBuffer<String> packages,288Map<String, List<JavaFileObject>> packageFiles,289StandardLocation location, Set<JavaFileObject.Kind> kinds)290throws IOException {291for (String subPackage: subPackages) {292if (!isIncluded(subPackage, includedPackages))293continue;294295for (JavaFileObject fo: docenv.fileManager.list(location, subPackage, kinds, true)) {296String binaryName = docenv.fileManager.inferBinaryName(location, fo);297String packageName = getPackageName(binaryName);298String simpleName = getSimpleName(binaryName);299if (isIncluded(packageName, includedPackages) && isValidClassName(simpleName)) {300List<JavaFileObject> list = packageFiles.get(packageName);301list = (list == null ? List.of(fo) : list.prepend(fo));302packageFiles.put(packageName, list);303if (!packages.contains(packageName))304packages.add(packageName);305}306}307}308}309310private String getPackageName(String name) {311int lastDot = name.lastIndexOf(".");312return (lastDot == -1 ? "" : name.substring(0, lastDot));313}314315private String getSimpleName(String name) {316int lastDot = name.lastIndexOf(".");317return (lastDot == -1 ? name : name.substring(lastDot + 1));318}319320private boolean isIncluded(String packageName, Map<String,Boolean> includedPackages) {321Boolean b = includedPackages.get(packageName);322if (b == null) {323b = isIncluded(getPackageName(packageName), includedPackages);324includedPackages.put(packageName, b);325}326return b;327}328329/**330* Recursively search all directories in path for subdirectory name.331* Add all packages found in such a directory to packages list.332*/333private void searchSubPackage(String packageName,334ListBuffer<String> packages,335List<String> excludedPackages,336Collection<File> pathnames) {337if (excludedPackages.contains(packageName))338return;339340String packageFilename = packageName.replace('.', File.separatorChar);341boolean addedPackage = false;342for (File pathname : pathnames) {343File f = new File(pathname, packageFilename);344String filenames[] = f.list();345// if filenames not null, then found directory346if (filenames != null) {347for (String filename : filenames) {348if (!addedPackage349&& (isValidJavaSourceFile(filename) ||350isValidJavaClassFile(filename))351&& !packages.contains(packageName)) {352packages.append(packageName);353addedPackage = true;354} else if (isValidClassName(filename) &&355(new File(f, filename)).isDirectory()) {356searchSubPackage(packageName + "." + filename,357packages, excludedPackages, pathnames);358}359}360}361}362}363364/**365* Return true if given file name is a valid class file name.366* @param file the name of the file to check.367* @return true if given file name is a valid class file name368* and false otherwise.369*/370private static boolean isValidJavaClassFile(String file) {371if (!file.endsWith(".class")) return false;372String clazzName = file.substring(0, file.length() - ".class".length());373return isValidClassName(clazzName);374}375376/**377* Return true if given file name is a valid Java source file name.378* @param file the name of the file to check.379* @return true if given file name is a valid Java source file name380* and false otherwise.381*/382private static boolean isValidJavaSourceFile(String file) {383if (!file.endsWith(".java")) return false;384String clazzName = file.substring(0, file.length() - ".java".length());385return isValidClassName(clazzName);386}387388/** Are surrogates supported?389*/390final static boolean surrogatesSupported = surrogatesSupported();391private static boolean surrogatesSupported() {392try {393boolean b = Character.isHighSurrogate('a');394return true;395} catch (NoSuchMethodError ex) {396return false;397}398}399400/**401* Return true if given file name is a valid class name402* (including "package-info").403* @param s the name of the class to check.404* @return true if given class name is a valid class name405* and false otherwise.406*/407public static boolean isValidClassName(String s) {408if (s.length() < 1) return false;409if (s.equals("package-info")) return true;410if (surrogatesSupported) {411int cp = s.codePointAt(0);412if (!Character.isJavaIdentifierStart(cp))413return false;414for (int j=Character.charCount(cp); j<s.length(); j+=Character.charCount(cp)) {415cp = s.codePointAt(j);416if (!Character.isJavaIdentifierPart(cp))417return false;418}419} else {420if (!Character.isJavaIdentifierStart(s.charAt(0)))421return false;422for (int j=1; j<s.length(); j++)423if (!Character.isJavaIdentifierPart(s.charAt(j)))424return false;425}426return true;427}428429/**430* From a list of top level trees, return the list of contained class definitions431*/432List<JCClassDecl> listClasses(List<JCCompilationUnit> trees) {433ListBuffer<JCClassDecl> result = new ListBuffer<JCClassDecl>();434for (JCCompilationUnit t : trees) {435for (JCTree def : t.defs) {436if (def.hasTag(JCTree.Tag.CLASSDEF))437result.append((JCClassDecl)def);438}439}440return result.toList();441}442443}444445446