Path: blob/master/src/jdk.javadoc/share/classes/jdk/javadoc/internal/tool/ElementsTable.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*/24package jdk.javadoc.internal.tool;2526import java.io.IOException;27import java.util.ArrayList;28import java.util.Collection;29import java.util.Collections;30import java.util.EnumMap;31import java.util.EnumSet;32import java.util.HashMap;33import java.util.HashSet;34import java.util.LinkedHashMap;35import java.util.LinkedHashSet;36import java.util.List;37import java.util.Map;38import java.util.Set;3940import javax.lang.model.element.Element;41import javax.lang.model.element.ElementKind;42import javax.lang.model.element.Modifier;43import javax.lang.model.element.ModuleElement;44import javax.lang.model.element.ModuleElement.ExportsDirective;45import javax.lang.model.element.ModuleElement.RequiresDirective;46import javax.lang.model.element.PackageElement;47import javax.lang.model.element.TypeElement;48import javax.lang.model.util.ElementFilter;49import javax.lang.model.util.SimpleElementVisitor14;50import javax.tools.JavaFileManager;51import javax.tools.JavaFileManager.Location;52import javax.tools.JavaFileObject;53import javax.tools.StandardLocation;5455import com.sun.tools.javac.code.Kinds.Kind;56import com.sun.tools.javac.code.Source;57import com.sun.tools.javac.code.Source.Feature;58import com.sun.tools.javac.code.Symbol;59import com.sun.tools.javac.code.Symbol.ClassSymbol;60import com.sun.tools.javac.code.Symbol.CompletionFailure;61import com.sun.tools.javac.code.Symbol.ModuleSymbol;62import com.sun.tools.javac.code.Symbol.PackageSymbol;63import com.sun.tools.javac.code.Symtab;64import com.sun.tools.javac.comp.Modules;65import com.sun.tools.javac.main.JavaCompiler;66import com.sun.tools.javac.tree.JCTree.JCClassDecl;67import com.sun.tools.javac.tree.JCTree.JCCompilationUnit;68import com.sun.tools.javac.tree.JCTree.JCModuleDecl;69import com.sun.tools.javac.tree.TreeInfo;70import com.sun.tools.javac.util.Context;71import com.sun.tools.javac.util.ListBuffer;72import com.sun.tools.javac.util.Name;73import com.sun.tools.javac.util.Names;74import jdk.javadoc.doclet.DocletEnvironment;75import jdk.javadoc.doclet.DocletEnvironment.ModuleMode;7677import static com.sun.tools.javac.code.Scope.LookupKind.NON_RECURSIVE;7879import static javax.lang.model.util.Elements.Origin.*;80import static javax.tools.JavaFileObject.Kind.*;8182import static jdk.javadoc.internal.tool.Main.Result.*;83import static jdk.javadoc.internal.tool.JavadocTool.isValidClassName;848586/**87* This class manages elements specified on the command line, and88* produces "specified" and "included" data sets, needed by the89* doclet environment, as well as querying an elements' visibility90* or inclusion.91*92* A. Initialization phase: the class is initialized with the93* options table by the caller. Some program elements may not94* be specified via specific options, such as packages, classes,95* these are set with the use of setter methods, such setClassArgList96* and setClassDeclList.97*98* B. Scan and decode phase: this is performed by scanSpecifiedItems,99* to identify the modules specified on the command line, modules100* specified with qualified packages and qualified subpackages, the101* modules so identified are used to initialize the module system.102*103* C. Intermediate phase: before the final analysis can be done,104* intermediate methods can be used to get specified elements from105* the initialization phase, typically used to parse sources or packages106* specified on the command line.107*108* D. Analysis phase: the final analysis is performed to determine109* the packages that ought to be included, as follows:110*111* 1. computes the specified modules, by considering the option112* "expand-requires", this must be done exhaustively, as the package113* computation phase expects a completed module graph, in order to114* check the target of a qualified export is in the included set.115*116* 2. computes the packages that must be documented, by considering117* the option "show-packages", also if only exported packages are118* to be considered, then also check for qualified packages, and119* include only those packages whose target is in the included set.120*121* 3. compute the specified packages, as part of this, first compute122* the subpackages and exclude any packages, if required.123*124* 4. Finally, compute the types found by previous parsing steps,125* noting that, all enclosed types (nested types) must also be126* considered.127*128* E. Finally, this class provides methods to obtain the specified sets,129* which are frozen and cached in the analysis phase, the included130* sets, are computed lazily and cached for future use. An element131* can be checked if it should be documented, in which case, the132* element is checked against the included set and the result is133* cached, for performance reasons.134*135* Definitions:136* Fully included: an element is included and some or parts137* of it components are included implicitly, subject to a138* selection criteria of its enclosed children.139*140* Included: if the item should be documented.141*142* Rules for processing:143*144* 1. A specified element, meaning an element given on the145* command-line, and exposed via specified elements collections.146* 2. Expand-contents, an internal pseudo term, meaning147* it is part of the recursive expansion of specified148* elements, meaning, the modules are expanded first, then149* the packages contained in the expanded modules, and then150* the types contained within the packages, to produce the151* collections returned by the methods152* getInclude{Module|Package|Type}Elements(), this is a153* downward expansion.154* 3. An included element, meaning it should be documented, and155* exposed via isIncluded, this enclosing element (module, package)156* is recursively included.157*/158public class ElementsTable {159160private final ToolEnvironment toolEnv;161private final Symtab syms;162private final Names names;163private final JavaFileManager fm;164private final List<Location> locations;165private final Modules modules;166private final ToolOptions options;167private final Messager messager;168private final JavaCompiler compiler;169170private final Map<String, Entry> entries = new LinkedHashMap<>();171172// specified elements173private Set<ModuleElement> specifiedModuleElements = new LinkedHashSet<>();174private Set<PackageElement> specifiedPackageElements = new LinkedHashSet<>();175private Set<TypeElement> specifiedTypeElements = new LinkedHashSet<>();176177// included elements178private Set<ModuleElement> includedModuleElements = null;179private Set<PackageElement> includedPackageElements = null;180private Set<TypeElement> includedTypeElements = null;181182// cmdline specifiers183private Set<ModulePackage> cmdLinePackages = new LinkedHashSet<>();184private Set<ModulePackage> excludePackages = new LinkedHashSet<>();185private Set<ModulePackage> subPackages = new LinkedHashSet<>();186187private List<JCClassDecl> classDecList = Collections.emptyList();188private List<String> classArgList = Collections.emptyList();189private com.sun.tools.javac.util.List<JCCompilationUnit> classTreeList = null;190191private final Set<JavaFileObject.Kind> sourceKinds = EnumSet.of(JavaFileObject.Kind.SOURCE);192193private final ModifierFilter accessFilter;194195private final AccessKind expandRequires;196197final boolean xclasses;198199/**200* Creates the table to manage included and excluded elements.201*202* @param context the context to locate commonly used objects203* @param options the tool options204*/205ElementsTable(Context context, ToolOptions options) {206this.toolEnv = ToolEnvironment.instance(context);207this.syms = Symtab.instance(context);208this.names = Names.instance(context);209this.fm = toolEnv.fileManager;210this.modules = Modules.instance(context);211this.options = options;212this.messager = Messager.instance0(context);213this.compiler = JavaCompiler.instance(context);214Source source = Source.instance(context);215216List<Location> locs = new ArrayList<>();217if (modules.multiModuleMode) {218locs.add(StandardLocation.MODULE_SOURCE_PATH);219} else {220if (toolEnv.fileManager.hasLocation(StandardLocation.SOURCE_PATH))221locs.add(StandardLocation.SOURCE_PATH);222else223locs.add(StandardLocation.CLASS_PATH);224}225if (Feature.MODULES.allowedInSource(source) && toolEnv.fileManager.hasLocation(StandardLocation.PATCH_MODULE_PATH))226locs.add(StandardLocation.PATCH_MODULE_PATH);227this.locations = Collections.unmodifiableList(locs);228229getEntry("").excluded = false;230231accessFilter = new ModifierFilter(options);232xclasses = options.xclasses();233expandRequires = options.expandRequires();234}235236/**237* Returns the module documentation level mode.238* @return the module documentation level mode239*/240public ModuleMode getModuleMode() {241switch(accessFilter.getAccessValue(ElementKind.MODULE)) {242case PACKAGE: case PRIVATE:243return DocletEnvironment.ModuleMode.ALL;244default:245return DocletEnvironment.ModuleMode.API;246}247}248249private Set<Element> specifiedElements = null;250/**251* Returns a set of elements specified on the252* command line, including any inner classes.253*254* @return the set of elements specified on the command line255*/256public Set<? extends Element> getSpecifiedElements() {257if (specifiedElements == null) {258Set<Element> result = new LinkedHashSet<>();259result.addAll(specifiedModuleElements);260result.addAll(specifiedPackageElements);261result.addAll(specifiedTypeElements);262specifiedElements = Collections.unmodifiableSet(result);263}264return specifiedElements;265}266267private Set<Element> includedElements = null;268/**269* Returns a set of elements included elements. The inclusion is as270* follows:271* A module is fully included,272* - is specified on the command line --module273* - is derived from the module graph, that is, by expanding the274* requires directive, based on --expand-requires275*276* A module is included if an enclosed package or type is277* specified on the command line.278*279* A package is fully included,280* - is specified on the command line281* - is derived from expanding -subpackages282* - can be documented in a fully included module based on --show-packages283*284* A package is included, if an enclosed package or a type is specified on285* the command line.286*287* Included type elements (including those within specified or included packages)288* to be documented.289*290* A type is fully included if291* - is specified on the command line with -sourcepath292* - is visible with --show-types filter293* A nested type is fully included if294* - is visible with --show-types filter295* - is enclosed in a fully included type296* @return the set of elements specified on the command line297*/298public Set<? extends Element> getIncludedElements() {299if (includedElements == null) {300Set<Element> result = new LinkedHashSet<>();301result.addAll(includedModuleElements);302result.addAll(includedPackageElements);303result.addAll(includedTypeElements);304includedElements = Collections.unmodifiableSet(result);305}306return includedElements;307}308309private IncludedVisitor includedVisitor = null;310311/**312* Returns true if the given element is included for consideration.313* This method accumulates elements in the cache as enclosed elements of314* fully included elements are tested.315* A member (constructor, method, field) is included if316* - it is visible in a fully included type (--show-members)317*318* @param e the element in question319*320* @see #getIncludedElements()321*322* @return true if included323*/324public boolean isIncluded(Element e) {325if (e == null) {326return false;327}328if (includedVisitor == null) {329includedVisitor = new IncludedVisitor();330}331return includedVisitor.visit(e);332}333334/**335* Performs the final computation and freezes the collections.336* This is a terminal operation, thus no further modifications337* are allowed to the specified data sets.338*339* @throws ToolException if an error occurs340*/341void analyze() throws ToolException {342// compute the specified element, by expanding module dependencies343computeSpecifiedModules();344345// compute all specified packages and subpackages346computeSpecifiedPackages();347348// compute the specified types349computeSpecifiedTypes();350351// compute the packages belonging to all the specified modules352Set<PackageElement> expandedModulePackages = computeModulePackages();353initializeIncludedSets(expandedModulePackages);354}355356ElementsTable classTrees(com.sun.tools.javac.util.List<JCCompilationUnit> classTrees) {357this.classTreeList = classTrees;358return this;359}360361/*362* This method sanity checks the following cases:363* a. a source-path containing a single module and many modules specified with --module364* b. no modules on source-path365* c. mismatched source-path and many modules specified with --module366*/367void sanityCheckSourcePathModules(List<String> moduleNames) throws ToolException {368if (!haveSourceLocationWithModule)369return;370371if (moduleNames.size() > 1) {372String text = messager.getText("main.cannot_use_sourcepath_for_modules",373String.join(", ", moduleNames));374throw new ToolException(CMDERR, text);375}376377String foundModule = getModuleName(StandardLocation.SOURCE_PATH);378if (foundModule == null) {379String text = messager.getText("main.module_not_found_on_sourcepath", moduleNames.get(0));380throw new ToolException(CMDERR, text);381}382383if (!moduleNames.get(0).equals(foundModule)) {384String text = messager.getText("main.sourcepath_does_not_contain_module", moduleNames.get(0));385throw new ToolException(CMDERR, text);386}387}388389private String getModuleName(Location location) throws ToolException {390try {391JavaFileObject jfo = fm.getJavaFileForInput(location,392"module-info", JavaFileObject.Kind.SOURCE);393if (jfo != null) {394JCCompilationUnit jcu = compiler.parse(jfo);395JCModuleDecl module = TreeInfo.getModule(jcu);396if (module != null) {397return module.getName().toString();398}399}400} catch (IOException ioe) {401String text = messager.getText("main.file.manager.list", location);402throw new ToolException(SYSERR, text, ioe);403}404return null;405}406407ElementsTable scanSpecifiedItems() throws ToolException {408409// scan modules specified on the command line410List<String> modules = options.modules();411List<String> mlist = new ArrayList<>();412for (String m : modules) {413List<Location> moduleLocations = getModuleLocation(locations, m);414if (moduleLocations.isEmpty()) {415String text = messager.getText("main.module_not_found", m);416throw new ToolException(CMDERR, text);417}418if (moduleLocations.contains(StandardLocation.SOURCE_PATH)) {419sanityCheckSourcePathModules(modules);420}421mlist.add(m);422ModuleSymbol msym = syms.enterModule(names.fromString(m));423specifiedModuleElements.add(msym);424}425426// scan for modules with qualified packages427cmdLinePackages.stream()428.filter(ModulePackage::hasModule)429.forEachOrdered(mpkg -> mlist.add(mpkg.moduleName));430431// scan for modules with qualified subpackages432options.subpackages().stream()433.map(ModulePackage::new)434.forEachOrdered(mpkg -> {435subPackages.add(mpkg);436if (mpkg.hasModule()) {437mlist.add(mpkg.moduleName);438}439});440441// all the modules specified on the command line have been scraped442// init the module systems443this.modules.addExtraAddModules(mlist.toArray(new String[mlist.size()]));444this.modules.initModules(this.classTreeList);445446return this;447}448449/**450* Returns the includes table after setting a class names specified on the command line.451*452* @param classList453* @return the include table454*/455ElementsTable setClassArgList(List<String> classList) {456classArgList = classList;457return this;458}459460/**461* Returns the includes table after setting the parsed class names.462*463* @param classesDecList464* @return the include table465*/466ElementsTable setClassDeclList(List<JCClassDecl> classesDecList) {467this.classDecList = classesDecList;468return this;469}470471/**472* Returns an includes table after setting the specified package473* names.474* @param packageNames packages on the command line475* @return the includes table after setting the specified package476* names477*/478ElementsTable packages(Collection<String> packageNames) {479packageNames.stream()480.map(ModulePackage::new)481.forEachOrdered(mpkg -> cmdLinePackages.add(mpkg));482return this;483}484485/**486* Returns the aggregate set of included packages and specified487* sub packages.488*489* @return the aggregate set of included packages and specified490* sub packages491*/492Iterable<ModulePackage> getPackagesToParse() throws IOException {493List<ModulePackage> result = new ArrayList<>();494result.addAll(cmdLinePackages);495result.addAll(subPackages);496return result;497}498499private void computeSubpackages() throws ToolException {500options.excludes().stream()501.map(ModulePackage::new)502.forEachOrdered(mpkg -> excludePackages.add(mpkg));503504excludePackages.forEach(p -> getEntry(p).excluded = true);505506for (ModulePackage modpkg : subPackages) {507List<Location> locs = getLocation(modpkg);508for (Location loc : locs) {509addPackagesFromLocations(loc, modpkg);510}511}512}513514/* Call fm.list and wrap any IOException that occurs in a ToolException */515private Iterable<JavaFileObject> fmList(Location location,516String packagename,517Set<JavaFileObject.Kind> kinds,518boolean recurse) throws ToolException {519try {520return fm.list(location, packagename, kinds, recurse);521} catch (IOException ioe) {522String text = messager.getText("main.file.manager.list", packagename);523throw new ToolException(SYSERR, text, ioe);524}525}526527private void addPackagesFromLocations(Location packageLocn, ModulePackage modpkg) throws ToolException {528for (JavaFileObject fo : fmList(packageLocn, modpkg.packageName, sourceKinds, true)) {529String binaryName = fm.inferBinaryName(packageLocn, fo);530String pn = getPackageName(binaryName);531String simpleName = getSimpleName(binaryName);532Entry e = getEntry(pn);533if (!e.isExcluded() && isValidClassName(simpleName)) {534ModuleSymbol msym = (modpkg.hasModule())535? syms.getModule(names.fromString(modpkg.moduleName))536: findModuleOfPackageName(modpkg.packageName);537538if (msym != null && !msym.isUnnamed()) {539syms.enterPackage(msym, names.fromString(pn));540ModulePackage npkg = new ModulePackage(msym.toString(), pn);541cmdLinePackages.add(npkg);542} else {543cmdLinePackages.add(e.modpkg);544}545e.files = (e.files == null546? com.sun.tools.javac.util.List.of(fo)547: e.files.prepend(fo));548}549}550}551552/**553* Returns the "requires" modules for the target module.554* @param mdle the target module element555* @param onlyTransitive true gets all the requires transitive, otherwise556* gets all the non-transitive requires557*558* @return a set of modules559*/560private Set<ModuleElement> getModuleRequires(ModuleElement mdle, boolean onlyTransitive) throws ToolException {561Set<ModuleElement> result = new HashSet<>();562for (RequiresDirective rd : ElementFilter.requiresIn(mdle.getDirectives())) {563ModuleElement dep = rd.getDependency();564if (result.contains(dep))565continue;566if (!isMandated(mdle, rd) && onlyTransitive == rd.isTransitive()) {567if (!haveModuleSources(dep)) {568if (!warnedNoSources.contains(dep)) {569messager.printWarningUsingKey(dep, "main.module_source_not_found", dep.getQualifiedName());570warnedNoSources.add(dep);571}572}573result.add(dep);574} else if (isMandated(mdle, rd) && haveModuleSources(dep)) {575result.add(dep);576}577}578return result;579}580581private boolean isMandated(ModuleElement mdle, RequiresDirective rd) {582return toolEnv.elements.getOrigin(mdle, rd) == MANDATED;583}584585Set<ModuleElement> warnedNoSources = new HashSet<>();586587Map<ModuleSymbol, Boolean> haveModuleSourcesCache = new HashMap<>();588private boolean haveModuleSources(ModuleElement mdle) throws ToolException {589ModuleSymbol msym = (ModuleSymbol) mdle;590if (msym.sourceLocation != null) {591return true;592}593if (msym.patchLocation != null) {594Boolean value = haveModuleSourcesCache.get(msym);595if (value == null) {596value = fmList(msym.patchLocation, "", sourceKinds, true).iterator().hasNext();597haveModuleSourcesCache.put(msym, value);598}599return value;600}601return false;602}603604private void computeSpecifiedModules() throws ToolException {605if (expandRequires == null) { // no expansion requested606specifiedModuleElements = Collections.unmodifiableSet(specifiedModuleElements);607return;608}609610final boolean expandAll = expandRequires.equals(AccessKind.PRIVATE)611|| expandRequires.equals(AccessKind.PACKAGE);612613Set<ModuleElement> result = new LinkedHashSet<>();614ListBuffer<ModuleElement> queue = new ListBuffer<>();615616// expand each specified module617for (ModuleElement mdle : specifiedModuleElements) {618result.add(mdle); // a specified module is included619queue.append(mdle);620Set<ModuleElement> publicRequires = getModuleRequires(mdle, true);621result.addAll(publicRequires);622// add all requires public623queue.addAll(publicRequires);624625if (expandAll) {626// add non-public requires if needed627result.addAll(getModuleRequires(mdle, false));628}629}630631// compute the transitive closure of all the requires public632for (ModuleElement m = queue.poll() ; m != null ; m = queue.poll()) {633for (ModuleElement mdle : getModuleRequires(m, true)) {634if (!result.contains(mdle)) {635result.add(mdle);636queue.append(mdle);637}638}639}640specifiedModuleElements = Collections.unmodifiableSet(result);641}642643private Set<PackageElement> getAllModulePackages(ModuleElement mdle) throws ToolException {644Set<PackageElement> result = new HashSet<>();645ModuleSymbol msym = (ModuleSymbol) mdle;646List<Location> msymlocs = getModuleLocation(locations, msym.name.toString());647for (Location msymloc : msymlocs) {648for (JavaFileObject fo : fmList(msymloc, "", sourceKinds, true)) {649if (fo.getName().endsWith("module-info.java")) {650continue;651}652String binaryName = fm.inferBinaryName(msymloc, fo);653String pn = getPackageName(binaryName);654PackageSymbol psym = syms.enterPackage(msym, names.fromString(pn));655result.add((PackageElement) psym);656}657}658return result;659}660661private Set<PackageElement> computeModulePackages() throws ToolException {662AccessKind accessValue = accessFilter.getAccessValue(ElementKind.PACKAGE);663final boolean documentAllModulePackages = (accessValue == AccessKind.PACKAGE ||664accessValue == AccessKind.PRIVATE);665666accessValue = accessFilter.getAccessValue(ElementKind.MODULE);667final boolean moduleDetailedMode = (accessValue == AccessKind.PACKAGE ||668accessValue == AccessKind.PRIVATE);669Set<PackageElement> expandedModulePackages = new LinkedHashSet<>();670671for (ModuleElement mdle : specifiedModuleElements) {672if (documentAllModulePackages) { // include all packages673List<PackageElement> packages = ElementFilter.packagesIn(mdle.getEnclosedElements());674expandedModulePackages.addAll(packages);675expandedModulePackages.addAll(getAllModulePackages(mdle));676} else { // selectively include required packages677List<ExportsDirective> exports = ElementFilter.exportsIn(mdle.getDirectives());678for (ExportsDirective export : exports) {679// add if fully exported or add qualified exports only if desired680if (export.getTargetModules() == null681|| documentAllModulePackages || moduleDetailedMode) {682expandedModulePackages.add(export.getPackage());683}684}685}686687// add all packages specified on the command line688// belonging to this module689if (!cmdLinePackages.isEmpty()) {690for (ModulePackage modpkg : cmdLinePackages) {691PackageElement pkg = toolEnv.elements.getPackageElement(mdle,692modpkg.packageName);693if (pkg != null) {694expandedModulePackages.add(pkg);695}696}697}698}699return expandedModulePackages;700}701702private void initializeIncludedSets(Set<PackageElement> expandedModulePackages) {703704// process modules705Set<ModuleElement> imodules = new LinkedHashSet<>();706// add all the expanded modules707imodules.addAll(specifiedModuleElements);708709// process packages710Set<PackageElement> ipackages = new LinkedHashSet<>();711// add all packages belonging to expanded modules712ipackages.addAll(expandedModulePackages);713// add all specified packages714specifiedPackageElements.forEach(pkg -> {715ModuleElement mdle = toolEnv.elements.getModuleOf(pkg);716if (mdle != null)717imodules.add(mdle);718ipackages.add(pkg);719});720721// process types722Set<TypeElement> iclasses = new LinkedHashSet<>();723// add all types enclosed in expanded modules and packages724ipackages.forEach(pkg -> addAllClasses(iclasses, pkg));725// add all types and its nested types726specifiedTypeElements.forEach(klass -> {727ModuleElement mdle = toolEnv.elements.getModuleOf(klass);728if (mdle != null && !mdle.isUnnamed())729imodules.add(mdle);730PackageElement pkg = toolEnv.elements.getPackageOf(klass);731ipackages.add(pkg);732addAllClasses(iclasses, klass, true);733});734735// all done, freeze the collections736includedModuleElements = Collections.unmodifiableSet(imodules);737includedPackageElements = Collections.unmodifiableSet(ipackages);738includedTypeElements = Collections.unmodifiableSet(iclasses);739}740741/*742* Computes the included packages and freezes the specified packages list.743*/744private void computeSpecifiedPackages() throws ToolException {745746computeSubpackages();747748Set<PackageElement> packlist = new LinkedHashSet<>();749cmdLinePackages.forEach(modpkg -> {750PackageElement pkg;751if (modpkg.hasModule()) {752ModuleElement mdle = toolEnv.elements.getModuleElement(modpkg.moduleName);753pkg = toolEnv.elements.getPackageElement(mdle, modpkg.packageName);754} else {755pkg = toolEnv.elements.getPackageElement(modpkg.toString());756}757758if (pkg != null) {759packlist.add(pkg);760} else {761messager.printWarningUsingKey("main.package_not_found", modpkg.toString());762}763});764specifiedPackageElements = Collections.unmodifiableSet(packlist);765}766767/**768* Adds all classes as well as inner classes, to the specified769* list.770*/771private void computeSpecifiedTypes() throws ToolException {772Set<TypeElement> classes = new LinkedHashSet<>();773classDecList.forEach(def -> {774TypeElement te = def.sym;775if (te != null) {776addAllClasses(classes, te, true);777}778});779for (String className : classArgList) {780TypeElement te = toolEnv.loadClass(className);781if (te == null) {782String text = messager.getText("javadoc.class_not_found", className);783throw new ToolException(CMDERR, text);784} else {785addAllClasses(classes, te, true);786}787}788specifiedTypeElements = Collections.unmodifiableSet(classes);789}790791private void addFilesForParser(Collection<JavaFileObject> result,792Collection<ModulePackage> collection,793boolean recurse) throws ToolException {794for (ModulePackage modpkg : collection) {795toolEnv.notice("main.Loading_source_files_for_package", modpkg.toString());796List<JavaFileObject> files = getFiles(modpkg, recurse);797if (files.isEmpty()) {798String text = messager.getText("main.no_source_files_for_package",799modpkg.toString());800throw new ToolException(CMDERR, text);801} else {802result.addAll(files);803}804}805}806807/**808* Returns an aggregated list of java file objects from the items809* specified on the command line. The packages specified should not810* recurse, however sub-packages should recurse into the sub directories.811* @return a list of java file objects812* @throws IOException if an error occurs813*/814List<JavaFileObject> getFilesToParse() throws ToolException {815List<JavaFileObject> result = new ArrayList<>();816addFilesForParser(result, cmdLinePackages, false);817addFilesForParser(result, subPackages, true);818return result;819}820821/**822* Returns the set of source files for a package.823*824* @param modpkg the specified package825* @return the set of file objects for the specified package826* @throws ToolException if an error occurs while accessing the files827*/828private List<JavaFileObject> getFiles(ModulePackage modpkg,829boolean recurse) throws ToolException {830Entry e = getEntry(modpkg);831// The files may have been found as a side effect of searching for subpackages832if (e.files != null) {833return e.files;834}835836ListBuffer<JavaFileObject> lb = new ListBuffer<>();837List<Location> locs = getLocation(modpkg);838if (locs.isEmpty()) {839return Collections.emptyList();840}841String pname = modpkg.packageName;842for (Location packageLocn : locs) {843for (JavaFileObject fo : fmList(packageLocn, pname, sourceKinds, recurse)) {844String binaryName = fm.inferBinaryName(packageLocn, fo);845String simpleName = getSimpleName(binaryName);846if (isValidClassName(simpleName)) {847lb.append(fo);848}849}850}851return lb.toList();852}853854private ModuleSymbol findModuleOfPackageName(String packageName) {855Name pack = names.fromString(packageName);856for (ModuleSymbol msym : modules.allModules()) {857PackageSymbol p = syms.getPackage(msym, pack);858if (p != null && !p.members().isEmpty()) {859return msym;860}861}862return null;863}864865private List<Location> getLocation(ModulePackage modpkg) throws ToolException {866if (locations.size() == 1 && !locations.contains(StandardLocation.MODULE_SOURCE_PATH)) {867return Collections.singletonList(locations.get(0));868}869870if (modpkg.hasModule()) {871return getModuleLocation(locations, modpkg.moduleName);872}873// TODO: handle invalid results better.874ModuleSymbol msym = findModuleOfPackageName(modpkg.packageName);875if (msym == null) {876return Collections.emptyList();877}878return getModuleLocation(locations, msym.name.toString());879}880881boolean haveSourceLocationWithModule = false;882883private List<Location> getModuleLocation(List<Location> locations, String msymName) throws ToolException {884List<Location> out = new ArrayList<>();885// search in the patch module first, this overrides others886if (locations.contains(StandardLocation.PATCH_MODULE_PATH)) {887Location loc = getModuleLocation(StandardLocation.PATCH_MODULE_PATH, msymName);888if (loc != null)889out.add(loc);890}891for (Location location : locations) {892// skip patch module, already done893if (location == StandardLocation.PATCH_MODULE_PATH) {894continue;895} else if (location == StandardLocation.MODULE_SOURCE_PATH) {896Location loc = getModuleLocation(location, msymName);897if (loc != null)898out.add(loc);899} else if (location == StandardLocation.SOURCE_PATH) {900haveSourceLocationWithModule = true;901out.add(StandardLocation.SOURCE_PATH);902}903}904return out;905}906907private Location getModuleLocation(Location location, String msymName) throws ToolException {908try {909return fm.getLocationForModule(location, msymName);910} catch (IOException ioe) {911String text = messager.getText("main.doclet_could_not_get_location", msymName);912throw new ToolException(ERROR, text, ioe);913}914}915916private Entry getEntry(String name) {917return getEntry(new ModulePackage(name));918}919920private Entry getEntry(ModulePackage modpkg) {921Entry e = entries.get(modpkg.packageName);922if (e == null) {923entries.put(modpkg.packageName, e = new Entry(modpkg));924}925return e;926}927928private String getPackageName(String name) {929int lastDot = name.lastIndexOf(".");930return (lastDot == -1 ? "" : name.substring(0, lastDot));931}932933private String getSimpleName(String name) {934int lastDot = name.lastIndexOf(".");935return (lastDot == -1 ? name : name.substring(lastDot + 1));936}937938/**939* Adds all inner classes of this class, and their inner classes recursively, to the list940*/941private void addAllClasses(Collection<TypeElement> list, TypeElement typeElement, boolean filtered) {942ClassSymbol klass = (ClassSymbol)typeElement;943try {944// eliminate needless checking, do this first.945if (list.contains(klass)) return;946// ignore classes with invalid Java class names947if (!JavadocTool.isValidClassName(klass.name.toString())) return;948if (filtered && !isTypeElementSelected(klass)) return;949list.add(klass);950for (Symbol sym : klass.members().getSymbols(NON_RECURSIVE)) {951if (sym != null && sym.kind == Kind.TYP) {952ClassSymbol s = (ClassSymbol)sym;953addAllClasses(list, s, filtered);954}955}956} catch (CompletionFailure e) {957if (e.getMessage() != null)958messager.printWarning(e.getMessage());959else960messager.printWarningUsingKey("main.unexpected.exception", e);961}962}963964/**965* Returns a list of all classes contained in this package, including966* member classes of those classes, and their member classes, etc.967*/968private void addAllClasses(Collection<TypeElement> list, PackageElement pkg) {969boolean filtered = true;970for (Element isym : pkg.getEnclosedElements()) {971addAllClasses(list, (TypeElement)isym, filtered);972}973}974975private boolean isTypeElementSelected(TypeElement te) {976return (xclasses || toolEnv.getFileKind(te) == SOURCE) && isSelected(te);977}978979SimpleElementVisitor14<Boolean, Void> visibleElementVisitor = null;980/**981* Returns true if the element is selected, by applying982* the access filter checks. Special treatment is applied to983* types, for a top level type the access filter applies completely,984* however if is a nested type then it is allowed either if985* the enclosing is a static or the enclosing is also selected.986*987* @param e the element to be checked988* @return true if the element is visible989*/990public boolean isSelected(Element e) {991if (toolEnv.isSynthetic((Symbol) e)) {992return false;993}994if (visibleElementVisitor == null) {995visibleElementVisitor = new SimpleElementVisitor14<Boolean, Void>() {996@Override997public Boolean visitType(TypeElement e, Void p) {998if (!accessFilter.checkModifier(e)) {999return false; // it is not allowed1000}1001Element encl = e.getEnclosingElement();10021003// check if nested1004if (encl.getKind() == ElementKind.PACKAGE)1005return true; // top-level class, allow it10061007// is enclosed static1008if (encl.getModifiers().contains(Modifier.STATIC))1009return true; // allowed10101011// check the enclosing1012return visit(encl);1013}10141015@Override1016protected Boolean defaultAction(Element e, Void p) {1017return accessFilter.checkModifier(e);1018}10191020@Override1021public Boolean visitUnknown(Element e, Void p) {1022throw new AssertionError("unknown element: " + e);1023}1024};1025}1026return visibleElementVisitor.visit(e);1027}10281029private class IncludedVisitor extends SimpleElementVisitor14<Boolean, Void> {1030private final Set<Element> includedCache;10311032public IncludedVisitor() {1033includedCache = new LinkedHashSet<>();1034}10351036@Override1037public Boolean visitModule(ModuleElement e, Void p) {1038// deduced by specified and/or requires expansion1039return includedModuleElements.contains(e);1040}10411042@Override1043public Boolean visitPackage(PackageElement e, Void p) {1044// deduced by specified or downward expansions1045return includedPackageElements.contains(e);1046}10471048@Override1049public Boolean visitType(TypeElement e, Void p) {1050if (includedTypeElements.contains(e)) {1051return true;1052}1053if (isTypeElementSelected(e)) {1054// Class is nameable from top-level and1055// the class and all enclosing classes1056// pass the modifier filter.1057PackageElement pkg = toolEnv.elements.getPackageOf(e);1058if (specifiedPackageElements.contains(pkg)) {1059return true;1060}1061Element enclosing = e.getEnclosingElement();1062if (enclosing != null) {1063switch(enclosing.getKind()) {1064case PACKAGE:1065return specifiedPackageElements.contains((PackageElement)enclosing);1066case CLASS: case INTERFACE: case ENUM: case ANNOTATION_TYPE:1067return visit((TypeElement) enclosing);1068default:1069throw new AssertionError("unknown element: " + enclosing);1070}1071}1072}1073return false;1074}10751076// members1077@Override1078public Boolean defaultAction(Element e, Void p) {1079if (includedCache.contains(e))1080return true;1081if (visit(e.getEnclosingElement()) && isSelected(e)) {1082switch(e.getKind()) {1083case ANNOTATION_TYPE: case CLASS: case ENUM: case INTERFACE:1084case MODULE: case OTHER: case PACKAGE:1085throw new AssertionError("invalid element for this operation: " + e);1086default:1087// the only allowed kinds in the cache are "members"1088includedCache.add(e);1089return true;1090}1091}1092return false;1093}10941095@Override1096public Boolean visitUnknown(Element e, Void p) {1097throw new AssertionError("unknown element: " + e);1098}10991100}11011102class Entry {1103final ModulePackage modpkg;1104Boolean excluded = false;1105com.sun.tools.javac.util.List<JavaFileObject> files;11061107Entry(ModulePackage modpkg) {1108this.modpkg = modpkg;1109}11101111Entry(String name) {1112modpkg = new ModulePackage(name);1113}11141115boolean isExcluded() {1116return excluded;1117}11181119@Override1120public String toString() {1121return "Entry{" + "modpkg=" + modpkg + ", excluded=" + excluded + ", files=" + files + '}';1122}1123}11241125/**1126* A container class to retrieve the module and package pair1127* from a parsed qualified package name.1128*/1129static class ModulePackage {11301131public final String moduleName;1132public final String packageName;11331134ModulePackage(String modulename, String packagename) {1135this.moduleName = modulename;1136this.packageName = packagename;1137}11381139ModulePackage(ModuleElement msym, String packagename) {1140this.moduleName = msym.toString();1141this.packageName = packagename;1142}11431144ModulePackage(String name) {1145String a[] = name.split("/");1146if (a.length == 2) {1147this.moduleName = a[0];1148this.packageName = a[1];1149} else {1150moduleName = null;1151packageName = name;1152}1153}11541155boolean hasModule() {1156return this.moduleName != null;1157}11581159@Override1160public boolean equals(Object obj) {1161if (obj instanceof ModulePackage that) {1162return this.toString().equals(that.toString());1163}1164return false;1165}11661167@Override1168public int hashCode() {1169return toString().hashCode();1170}11711172@Override1173public String toString() {1174return moduleName == null ? packageName : moduleName + "/" + packageName;1175}1176}11771178/**1179* A class which filters the access flags on classes, fields, methods, etc.1180*1181* @see javax.lang.model.element.Modifier1182*/11831184static class ModifierFilter {1185/**1186* The allowed ElementKind that can be stored.1187*/1188static final EnumSet<ElementKind> ALLOWED_KINDS = EnumSet.of(ElementKind.METHOD,1189ElementKind.CLASS,1190ElementKind.PACKAGE,1191ElementKind.MODULE);11921193// all possible access levels allowed for each element1194private final EnumMap<ElementKind, EnumSet<AccessKind>> filterMap =1195new EnumMap<>(ElementKind.class);11961197// the specified access level for each element1198private final EnumMap<ElementKind, AccessKind> accessMap =1199new EnumMap<>(ElementKind.class);12001201/**1202* Constructor - Specify a filter.1203*1204* @param options the tool options1205*/1206ModifierFilter(ToolOptions options) {12071208AccessKind accessValue = null;1209for (ElementKind kind : ALLOWED_KINDS) {1210switch (kind) {1211case METHOD:1212accessValue = options.showMembersAccess();1213break;1214case CLASS:1215accessValue = options.showTypesAccess();1216break;1217case PACKAGE:1218accessValue = options.showPackagesAccess();1219break;1220case MODULE:1221accessValue = options.showModuleContents();1222break;1223default:1224throw new AssertionError("unknown element: " + kind);12251226}1227accessMap.put(kind, accessValue);1228filterMap.put(kind, getFilterSet(accessValue));1229}1230}12311232static EnumSet<AccessKind> getFilterSet(AccessKind accessValue) {1233switch (accessValue) {1234case PUBLIC:1235return EnumSet.of(AccessKind.PUBLIC);1236case PROTECTED:1237default:1238return EnumSet.of(AccessKind.PUBLIC, AccessKind.PROTECTED);1239case PACKAGE:1240return EnumSet.of(AccessKind.PUBLIC, AccessKind.PROTECTED, AccessKind.PACKAGE);1241case PRIVATE:1242return EnumSet.allOf(AccessKind.class);1243}1244}12451246public AccessKind getAccessValue(ElementKind kind) {1247if (!ALLOWED_KINDS.contains(kind)) {1248throw new IllegalArgumentException("not allowed: " + kind);1249}1250return accessMap.getOrDefault(kind, AccessKind.PROTECTED);1251}12521253/**1254* Returns true if access is allowed.1255*1256* @param e the element in question1257* @return whether the modifiers pass this filter1258*/1259public boolean checkModifier(Element e) {1260Set<Modifier> modifiers = e.getModifiers();1261AccessKind fflag = AccessKind.PACKAGE;1262if (modifiers.contains(Modifier.PUBLIC)) {1263fflag = AccessKind.PUBLIC;1264} else if (modifiers.contains(Modifier.PROTECTED)) {1265fflag = AccessKind.PROTECTED;1266} else if (modifiers.contains(Modifier.PRIVATE)) {1267fflag = AccessKind.PRIVATE;1268}1269EnumSet<AccessKind> filterSet = filterMap.get(getAllowedKind(e.getKind()));1270return filterSet.contains(fflag);1271}12721273// convert a requested element kind to an allowed access kind1274private ElementKind getAllowedKind(ElementKind kind) {1275switch (kind) {1276case CLASS: case METHOD: case MODULE: case PACKAGE:1277return kind;1278case RECORD: case ANNOTATION_TYPE: case ENUM: case INTERFACE:1279return ElementKind.CLASS;1280case CONSTRUCTOR: case ENUM_CONSTANT: case EXCEPTION_PARAMETER:1281case FIELD: case INSTANCE_INIT: case LOCAL_VARIABLE: case PARAMETER:1282case RESOURCE_VARIABLE: case STATIC_INIT: case TYPE_PARAMETER:1283case RECORD_COMPONENT:1284return ElementKind.METHOD;1285default:1286throw new AssertionError("unsupported kind: " + kind);1287}1288}1289} // end ModifierFilter1290}129112921293