1/*
2 * Copyright (c) 2001, 2017, 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 */
25package jdk.javadoc.internal.tool;
26
27import java.io.IOException;
28import java.util.ArrayList;
29import java.util.Collection;
30import java.util.Collections;
31import java.util.EnumMap;
32import java.util.EnumSet;
33import java.util.HashMap;
34import java.util.HashSet;
35import java.util.LinkedHashMap;
36import java.util.LinkedHashSet;
37import java.util.List;
38import java.util.Map;
39import java.util.Set;
40
41import javax.lang.model.element.Element;
42import javax.lang.model.element.ElementKind;
43import javax.lang.model.element.Modifier;
44import javax.lang.model.element.ModuleElement;
45import javax.lang.model.element.ModuleElement.ExportsDirective;
46import javax.lang.model.element.ModuleElement.RequiresDirective;
47import javax.lang.model.element.PackageElement;
48import javax.lang.model.element.TypeElement;
49import javax.lang.model.util.ElementFilter;
50import javax.lang.model.util.SimpleElementVisitor9;
51import javax.tools.JavaFileManager;
52import javax.tools.JavaFileManager.Location;
53import javax.tools.JavaFileObject;
54import javax.tools.StandardLocation;
55
56import com.sun.tools.javac.code.Kinds.Kind;
57import com.sun.tools.javac.code.Source;
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;
76
77import static com.sun.tools.javac.code.Scope.LookupKind.NON_RECURSIVE;
78
79import static javax.lang.model.util.Elements.Origin.*;
80import static javax.tools.JavaFileObject.Kind.*;
81
82import static jdk.javadoc.internal.tool.Main.Result.*;
83import static jdk.javadoc.internal.tool.JavadocTool.isValidClassName;
84
85
86/**
87 * This class manages elements specified on the command line, and
88 * produces "specified" and "included" data sets, needed by the
89 * doclet environment, as well as querying an elements' visibility
90 * or inclusion.
91 *
92 * A. Initialization phase: the class is initialized with the
93 *    options table by the caller. Some program elements may not
94 *    be specified via specific options, such as packages, classes,
95 *    these are set with the use of setter methods, such setClassArgList
96 *    and setClassDeclList.
97 *
98 * B. Scan and decode phase: this is performed by scanSpecifiedItems,
99 *    to identify the modules specified on the command line, modules
100 *    specified with qualified packages and qualified subpackages, the
101 *    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 from
105 *    the initialization phase, typically used to parse sources or packages
106 *    specified on the command line.
107 *
108 * D. Analysis phase: the final analysis is performed to determine
109 *    the packages that ought to be included, as follows:
110 *
111 *    1. computes the specified modules, by considering the option
112 *       "expand-requires", this must be done exhaustively, as the package
113 *       computation phase expects a completed module graph, in order to
114 *       check the target of a qualified export is in the included set.
115 *
116 *    2. computes the packages that must be documented, by considering
117 *       the option "show-packages", also if only exported packages are
118 *       to be considered, then also check for qualified packages, and
119 *       include only those packages whose target is in the included set.
120 *
121 *    3. compute the specified packages, as part of this, first compute
122 *       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 be
126 *       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 included
130 *    sets, are computed lazily and cached for future use. An element
131 *    can be checked if it should be documented, in which case, the
132 *    element is checked against the included set and the result is
133 *    cached, for performance reasons.
134 *
135 * Definitions:
136 *    Fully included: an element is included and some or parts
137 *    of it components are included implicitly, subject to a
138 *    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 the
145 *    command-line, and exposed via specified elements collections.
146 * 2. Expand-contents, an internal pseudo term, meaning
147 *    it is part of the recursive expansion of specified
148 *    elements, meaning, the modules are expanded first, then
149 *    the packages contained in the expanded modules, and then
150 *    the types contained within the packages, to produce the
151 *    collections returned by the methods
152 *    getInclude{Module|Package|Type}Elements(), this is a
153 *    downward expansion.
154 * 3. An included element, meaning it should be documented, and
155 *    exposed via isIncluded, this enclosing element (module, package)
156 *    is recursively included.
157 */
158public class ElementsTable {
159
160    private final ToolEnvironment toolEnv;
161    private final Symtab syms;
162    private final Names names;
163    private final JavaFileManager fm;
164    private final List<Location> locations;
165    private final Modules modules;
166    private final Map<ToolOption, Object> opts;
167    private final Messager messager;
168    private final JavaCompiler compiler;
169
170    private final Map<String, Entry> entries = new LinkedHashMap<>();
171
172    // specified elements
173    private Set<ModuleElement> specifiedModuleElements = new LinkedHashSet<>();
174    private Set<PackageElement> specifiedPackageElements = new LinkedHashSet<>();
175    private Set<TypeElement> specifiedTypeElements =new LinkedHashSet<>();
176
177    // included elements
178    private Set<ModuleElement> includedModuleElements = null;
179    private Set<PackageElement> includedPackageElements = null;
180    private Set<TypeElement> includedTypeElements = null;
181
182    // cmdline specifiers
183    private Set<ModulePackage> cmdLinePackages = new LinkedHashSet<>();
184    private Set<ModulePackage> excludePackages = new LinkedHashSet<>();
185    private Set<ModulePackage> subPackages = new LinkedHashSet<>();
186
187    private List<JCClassDecl> classDecList = Collections.emptyList();
188    private List<String> classArgList = Collections.emptyList();
189    private com.sun.tools.javac.util.List<JCCompilationUnit> classTreeList = null;
190
191    private final Set<JavaFileObject.Kind> sourceKinds = EnumSet.of(JavaFileObject.Kind.SOURCE);
192
193    private final ModifierFilter accessFilter;
194
195    private final AccessKind expandRequires;
196
197    final boolean xclasses;
198
199    /**
200     * Creates the table to manage included and excluded elements.
201     *
202     * @param context the context to locate commonly used objects
203     * @param location the location used to locate source files
204     */
205    ElementsTable(Context context, Map<ToolOption, Object> opts) {
206        this.toolEnv = ToolEnvironment.instance(context);
207        this.syms = Symtab.instance(context);
208        this.names = Names.instance(context);
209        this.fm = toolEnv.fileManager;
210        this.modules = Modules.instance(context);
211        this.opts = opts;
212        this.messager = Messager.instance0(context);
213        this.compiler = JavaCompiler.instance(context);
214        Source source = Source.instance(context);
215
216        List<Location> locs = new ArrayList<>();
217        if (modules.multiModuleMode) {
218            locs.add(StandardLocation.MODULE_SOURCE_PATH);
219        } else {
220            if (toolEnv.fileManager.hasLocation(StandardLocation.SOURCE_PATH))
221                locs.add(StandardLocation.SOURCE_PATH);
222            else
223                locs.add(StandardLocation.CLASS_PATH);
224        }
225        if (source.allowModules() && toolEnv.fileManager.hasLocation(StandardLocation.PATCH_MODULE_PATH))
226            locs.add(StandardLocation.PATCH_MODULE_PATH);
227        this.locations = Collections.unmodifiableList(locs);
228
229        getEntry("").excluded = false;
230
231        accessFilter = new ModifierFilter(opts);
232        xclasses = (boolean)opts.getOrDefault(ToolOption.XCLASSES, false);
233        expandRequires = (AccessKind)opts.get(ToolOption.EXPAND_REQUIRES);
234    }
235
236    /**
237     * Returns the module documentation level mode.
238     * @return the module documentation level mode
239     */
240    public ModuleMode getModuleMode() {
241        switch(accessFilter.getAccessValue(ElementKind.MODULE)) {
242            case PACKAGE: case PRIVATE:
243                return DocletEnvironment.ModuleMode.ALL;
244            default:
245                return DocletEnvironment.ModuleMode.API;
246        }
247    }
248
249    private Set<Element> specifiedElements = null;
250    /**
251     * Returns a set of elements specified on the
252     * command line, including any inner classes.
253     *
254     * @return the set of elements specified on the command line
255     */
256    public Set<? extends Element> getSpecifiedElements() {
257        if (specifiedElements == null) {
258            Set<Element> result = new LinkedHashSet<>();
259            result.addAll(specifiedModuleElements);
260            result.addAll(specifiedPackageElements);
261            result.addAll(specifiedTypeElements);
262            specifiedElements = Collections.unmodifiableSet(result);
263        }
264        return specifiedElements;
265    }
266
267    private Set<Element> includedElements = null;
268    /**
269     * Returns a set of elements included elements. The inclusion is as
270     * follows:
271     * A module is fully included,
272     *   - is specified on the command line --module
273     *   - is derived from the module graph, that is, by expanding the
274     *     requires directive, based on --expand-requires
275     *
276     * A module is included if an enclosed package or type is
277     * specified on the command line.
278     *
279     * A package is fully included,
280     *  - is specified on the command line
281     *  - is derived from expanding -subpackages
282     *  - can be documented in a fully included module based on --show-packages
283     *
284     * A package is included, if an enclosed package or a type is specified on
285     * 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 if
291     *  - is specified on the command line with -sourcepath
292     *  - is visible with --show-types filter
293     * A nested type is fully included if
294     *  - is visible with --show-types filter
295     *  - is enclosed in a fully included type
296     * @return the set of elements specified on the command line
297     */
298    public Set<? extends Element> getIncludedElements() {
299        if (includedElements == null) {
300            Set<Element> result = new LinkedHashSet<>();
301            result.addAll(includedModuleElements);
302            result.addAll(includedPackageElements);
303            result.addAll(includedTypeElements);
304            includedElements = Collections.unmodifiableSet(result);
305        }
306        return includedElements;
307    }
308
309    private IncludedVisitor includedVisitor = null;
310
311    /**
312     * Returns true if the given element is included for consideration.
313     * This method accumulates elements in the cache as enclosed elements of
314     * fully included elements are tested.
315     * A member (constructor, method, field) is included if
316     *  - it is visible in a fully included type (--show-members)
317     *
318     * @param e the element in question
319     *
320     * @see getIncludedModuleElements
321     * @see getIncludedPackageElements
322     * @see getIncludedTypeElements
323     *
324     * @return true if included
325     */
326    public boolean isIncluded(Element e) {
327        if (e == null) {
328            return false;
329        }
330        if (includedVisitor == null) {
331            includedVisitor = new IncludedVisitor();
332        }
333        return includedVisitor.visit(e);
334    }
335
336    /**
337     * Performs the final computation and freezes the collections.
338     * This is a terminal operation, thus no further modifications
339     * are allowed to the specified data sets.
340     *
341     * @throws ToolException if an error occurs
342     */
343    void analyze() throws ToolException {
344        // compute the specified element, by expanding module dependencies
345        computeSpecifiedModules();
346
347        // compute all specified packages and subpackages
348        computeSpecifiedPackages();
349
350        // compute the specified types
351        computeSpecifiedTypes();
352
353        // compute the packages belonging to all the specified modules
354        Set<PackageElement> expandedModulePackages = computeModulePackages();
355        initializeIncludedSets(expandedModulePackages);
356    }
357
358    ElementsTable classTrees(com.sun.tools.javac.util.List<JCCompilationUnit> classTrees) {
359        this.classTreeList = classTrees;
360        return this;
361    }
362
363    /*
364     * This method sanity checks the following cases:
365     * a. a source-path containing a single module and many modules specified with --module
366     * b. no modules on source-path
367     * c. mismatched source-path and many modules specified with --module
368     */
369    void sanityCheckSourcePathModules(List<String> moduleNames) throws ToolException {
370        if (!haveSourceLocationWithModule)
371            return;
372
373        if (moduleNames.size() > 1) {
374            String text = messager.getText("main.cannot_use_sourcepath_for_modules",
375                    String.join(", ", moduleNames));
376            throw new ToolException(CMDERR, text);
377        }
378
379        String foundModule = getModuleName(StandardLocation.SOURCE_PATH);
380        if (foundModule == null) {
381            String text = messager.getText("main.module_not_found_on_sourcepath", moduleNames.get(0));
382            throw new ToolException(CMDERR, text);
383        }
384
385        if (!moduleNames.get(0).equals(foundModule)) {
386            String text = messager.getText("main.sourcepath_does_not_contain_module", moduleNames.get(0));
387            throw new ToolException(CMDERR, text);
388        }
389    }
390
391    private String getModuleName(Location location) throws ToolException {
392        try {
393            JavaFileObject jfo = fm.getJavaFileForInput(location,
394                    "module-info", JavaFileObject.Kind.SOURCE);
395            if (jfo != null) {
396                JCCompilationUnit jcu = compiler.parse(jfo);
397                JCModuleDecl module = TreeInfo.getModule(jcu);
398                if (module != null) {
399                    return module.getName().toString();
400                }
401            }
402        } catch (IOException ioe) {
403            String text = messager.getText("main.file.manager.list", location);
404            throw new ToolException(SYSERR, text, ioe);
405        }
406        return null;
407    }
408
409    @SuppressWarnings("unchecked")
410    ElementsTable scanSpecifiedItems() throws ToolException {
411
412        // scan modules specified on the command line
413        List<String> moduleNames = (List<String>) opts.computeIfAbsent(ToolOption.MODULE,
414                s -> Collections.EMPTY_LIST);
415        List<String> mlist = new ArrayList<>();
416        for (String m : moduleNames) {
417            List<Location> moduleLocations = getModuleLocation(locations, m);
418            if (moduleLocations.isEmpty()) {
419                String text = messager.getText("main.module_not_found", m);
420                throw new ToolException(CMDERR, text);
421            }
422            if (moduleLocations.contains(StandardLocation.SOURCE_PATH)) {
423                sanityCheckSourcePathModules(moduleNames);
424            }
425            mlist.add(m);
426            ModuleSymbol msym = syms.enterModule(names.fromString(m));
427            specifiedModuleElements.add((ModuleElement) msym);
428        }
429
430        // scan for modules with qualified packages
431        cmdLinePackages.stream()
432                .filter((mpkg) -> (mpkg.hasModule()))
433                .forEachOrdered((mpkg) -> {
434                    mlist.add(mpkg.moduleName);
435        });
436
437        // scan for modules with qualified subpackages
438        ((List<String>)opts.computeIfAbsent(ToolOption.SUBPACKAGES, v -> Collections.EMPTY_LIST))
439            .stream()
440            .map(ModulePackage::new)
441            .forEachOrdered((mpkg) -> {
442                subPackages.add(mpkg);
443                if (mpkg.hasModule()) {
444                    mlist.add(mpkg.moduleName);
445                }
446            });
447
448        // all the modules specified on the command line have been scraped
449        // init the module systems
450        modules.addExtraAddModules(mlist.toArray(new String[mlist.size()]));
451        modules.initModules(this.classTreeList);
452
453        return this;
454    }
455
456    /**
457     * Returns the includes table after setting a class names specified on the command line.
458     *
459     * @param classList
460     * @return the include table
461     */
462    ElementsTable setClassArgList(List<String> classList) {
463        classArgList = classList;
464        return this;
465    }
466
467    /**
468     * Returns the includes table after setting the parsed class names.
469     *
470     * @param classesDecList
471     * @return the include table
472     */
473    ElementsTable setClassDeclList(List<JCClassDecl> classesDecList) {
474        this.classDecList = classesDecList;
475        return this;
476    }
477
478    /**
479     * Returns an includes table after setting the specified package
480     * names.
481     * @param packageNames packages on the command line
482     * @return the includes table after setting the specified package
483     * names
484     */
485    ElementsTable packages(Collection<String> packageNames) {
486        packageNames.stream()
487            .map(ModulePackage::new)
488            .forEachOrdered((mpkg) -> cmdLinePackages.add(mpkg));
489        return this;
490    }
491
492    /**
493     * Returns the aggregate set of included packages and specified
494     * sub packages.
495     *
496     * @return the aggregate set of included packages and specified
497     * sub packages
498     */
499    Iterable<ModulePackage> getPackagesToParse() throws IOException {
500        List<ModulePackage> result = new ArrayList<>();
501        result.addAll(cmdLinePackages);
502        result.addAll(subPackages);
503        return result;
504    }
505
506    @SuppressWarnings("unchecked")
507    private void computeSubpackages() throws ToolException {
508        ((List<String>) opts.computeIfAbsent(ToolOption.EXCLUDE, v -> Collections.EMPTY_LIST))
509                .stream()
510                .map(ModulePackage::new)
511                .forEachOrdered((mpkg) -> excludePackages.add(mpkg));
512
513        excludePackages.forEach((p) -> {
514            getEntry(p).excluded = true;
515        });
516
517        for (ModulePackage modpkg : subPackages) {
518            List<Location> locs = getLocation(modpkg);
519            for (Location loc : locs) {
520                addPackagesFromLocations(loc, modpkg);
521            }
522        }
523    }
524
525    /* Call fm.list and wrap any IOException that occurs in a ToolException */
526    private Iterable<JavaFileObject> fmList(Location location,
527                                            String packagename,
528                                            Set<JavaFileObject.Kind> kinds,
529                                            boolean recurse) throws ToolException {
530        try {
531            return fm.list(location, packagename, kinds, recurse);
532        } catch (IOException ioe) {
533            String text = messager.getText("main.file.manager.list", packagename);
534            throw new ToolException(SYSERR, text, ioe);
535        }
536    }
537
538    private void addPackagesFromLocations(Location packageLocn, ModulePackage modpkg) throws ToolException {
539        for (JavaFileObject fo : fmList(packageLocn, modpkg.packageName, sourceKinds, true)) {
540            String binaryName = fm.inferBinaryName(packageLocn, fo);
541            String pn = getPackageName(binaryName);
542            String simpleName = getSimpleName(binaryName);
543            Entry e = getEntry(pn);
544            if (!e.isExcluded() && isValidClassName(simpleName)) {
545                ModuleSymbol msym = (modpkg.hasModule())
546                        ? syms.getModule(names.fromString(modpkg.moduleName))
547                        : findModuleOfPackageName(modpkg.packageName);
548
549                if (msym != null && !msym.isUnnamed()) {
550                    syms.enterPackage(msym, names.fromString(pn));
551                    ModulePackage npkg = new ModulePackage(msym.toString(), pn);
552                    cmdLinePackages.add(npkg);
553                } else {
554                    cmdLinePackages.add(e.modpkg);
555                }
556                e.files = (e.files == null
557                        ? com.sun.tools.javac.util.List.of(fo)
558                        : e.files.prepend(fo));
559            }
560        }
561    }
562
563    /**
564     * Returns the "requires" modules for the target module.
565     * @param mdle the target module element
566     * @param onlyTransitive true gets all the requires transitive, otherwise
567     *                 gets all the non-transitive requires
568     *
569     * @return a set of modules
570     */
571    private Set<ModuleElement> getModuleRequires(ModuleElement mdle, boolean onlyTransitive) throws ToolException {
572        Set<ModuleElement> result = new HashSet<>();
573        for (RequiresDirective rd : ElementFilter.requiresIn(mdle.getDirectives())) {
574            ModuleElement dep = rd.getDependency();
575            if (result.contains(dep))
576                continue;
577            if (!isMandated(mdle, rd) && onlyTransitive == rd.isTransitive()) {
578                if (!haveModuleSources(dep)) {
579                    messager.printWarning(dep, "main.module_not_found", dep.getSimpleName());
580                }
581                result.add(dep);
582            } else if (isMandated(mdle, rd) && haveModuleSources(dep)) {
583                result.add(dep);
584            }
585        }
586        return result;
587    }
588
589    private boolean isMandated(ModuleElement mdle, RequiresDirective rd) {
590        return toolEnv.elements.getOrigin(mdle, rd) == MANDATED;
591    }
592
593    Map<ModuleSymbol, Boolean> haveModuleSourcesCache = new HashMap<>();
594    private boolean haveModuleSources(ModuleElement mdle) throws ToolException {
595        ModuleSymbol msym =  (ModuleSymbol)mdle;
596        if (msym.sourceLocation != null) {
597            return true;
598        }
599        if (msym.patchLocation != null) {
600            Boolean value = haveModuleSourcesCache.get(msym);
601            if (value == null) {
602                value = fmList(msym.patchLocation, "", sourceKinds, true).iterator().hasNext();
603                haveModuleSourcesCache.put(msym, value);
604            }
605            return value;
606        }
607        return false;
608    }
609
610    private void computeSpecifiedModules() throws ToolException {
611        if (expandRequires == null) { // no expansion requested
612            specifiedModuleElements = Collections.unmodifiableSet(specifiedModuleElements);
613            return;
614        }
615
616        final boolean expandAll = expandRequires.equals(AccessKind.PRIVATE)
617                || expandRequires.equals(AccessKind.PACKAGE);
618
619        Set<ModuleElement> result = new LinkedHashSet<>();
620        ListBuffer<ModuleElement> queue = new ListBuffer<>();
621
622        // expand each specified module
623        for (ModuleElement mdle : specifiedModuleElements) {
624            result.add(mdle); // a specified module is included
625            queue.append(mdle);
626            Set<ModuleElement> publicRequires = getModuleRequires(mdle, true);
627            result.addAll(publicRequires);
628            // add all requires public
629            queue.addAll(publicRequires);
630
631            if (expandAll) {
632                 // add non-public requires if needed
633                result.addAll(getModuleRequires(mdle, !expandAll));
634            }
635        }
636
637        // compute the transitive closure of all the requires public
638        for (ModuleElement m = queue.poll() ; m != null ; m = queue.poll()) {
639            for (ModuleElement mdle : getModuleRequires(m, true)) {
640                if (!result.contains(mdle)) {
641                    result.add(mdle);
642                    queue.append(mdle);
643                }
644            }
645        }
646        specifiedModuleElements = Collections.unmodifiableSet(result);
647    }
648
649    private Set<PackageElement> getAllModulePackages(ModuleElement mdle) throws ToolException {
650        Set<PackageElement> result = new HashSet<>();
651        ModuleSymbol msym = (ModuleSymbol) mdle;
652        List<Location> msymlocs = getModuleLocation(locations, msym.name.toString());
653        for (Location msymloc : msymlocs) {
654            for (JavaFileObject fo : fmList(msymloc, "", sourceKinds, true)) {
655                if (fo.getName().endsWith("module-info.java")) {
656                    continue;
657                }
658                String binaryName = fm.inferBinaryName(msymloc, fo);
659                String pn = getPackageName(binaryName);
660                PackageSymbol psym = syms.enterPackage(msym, names.fromString(pn));
661                result.add((PackageElement) psym);
662            }
663        }
664        return result;
665    }
666
667    private Set<PackageElement> computeModulePackages() throws ToolException {
668        AccessKind accessValue = accessFilter.getAccessValue(ElementKind.PACKAGE);
669        final boolean documentAllModulePackages = (accessValue == AccessKind.PACKAGE ||
670                accessValue == AccessKind.PRIVATE);
671
672        accessValue = accessFilter.getAccessValue(ElementKind.MODULE);
673        final boolean moduleDetailedMode = (accessValue == AccessKind.PACKAGE ||
674                accessValue == AccessKind.PRIVATE);
675        Set<PackageElement> expandedModulePackages = new LinkedHashSet<>();
676
677        for (ModuleElement mdle : specifiedModuleElements) {
678            if (documentAllModulePackages) { // include all packages
679                List<PackageElement> packages = ElementFilter.packagesIn(mdle.getEnclosedElements());
680                expandedModulePackages.addAll(packages);
681                expandedModulePackages.addAll(getAllModulePackages(mdle));
682            } else { // selectively include required packages
683                List<ExportsDirective> exports = ElementFilter.exportsIn(mdle.getDirectives());
684                for (ExportsDirective export : exports) {
685                    // add if fully exported or add qualified exports only if desired
686                    if (export.getTargetModules() == null
687                            || documentAllModulePackages || moduleDetailedMode) {
688                        expandedModulePackages.add(export.getPackage());
689                    }
690                }
691            }
692
693            // add all packages specified on the command line
694            // belonging to this module
695            if (!cmdLinePackages.isEmpty()) {
696                for (ModulePackage modpkg : cmdLinePackages) {
697                    PackageElement pkg = toolEnv.elements.getPackageElement(mdle,
698                            modpkg.packageName);
699                    if (pkg != null) {
700                        expandedModulePackages.add(pkg);
701                    }
702                }
703            }
704        }
705        return expandedModulePackages;
706    }
707
708    private void initializeIncludedSets(Set<PackageElement> expandedModulePackages) {
709
710        // process modules
711        Set<ModuleElement> imodules = new LinkedHashSet<>();
712        // add all the expanded modules
713        imodules.addAll(specifiedModuleElements);
714
715        // process packages
716        Set<PackageElement> ipackages = new LinkedHashSet<>();
717        // add all packages belonging to expanded modules
718        ipackages.addAll(expandedModulePackages);
719        // add all specified packages
720        specifiedPackageElements.forEach(pkg -> {
721            ModuleElement mdle = toolEnv.elements.getModuleOf(pkg);
722            if (mdle != null)
723                imodules.add(mdle);
724            ipackages.add(pkg);
725        });
726
727        // process types
728        Set<TypeElement> iclasses = new LinkedHashSet<>();
729        // add all types enclosed in expanded modules and packages
730        ipackages.forEach((pkg) -> {
731            addAllClasses(iclasses, pkg);
732        });
733        // add all types and its nested types
734        specifiedTypeElements.forEach((klass) -> {
735            ModuleElement mdle = toolEnv.elements.getModuleOf(klass);
736            if (mdle != null && !mdle.isUnnamed())
737                imodules.add(mdle);
738            PackageElement pkg = toolEnv.elements.getPackageOf(klass);
739            ipackages.add(pkg);
740            addAllClasses(iclasses, klass, true);
741        });
742
743        // all done, freeze the collections
744        includedModuleElements = Collections.unmodifiableSet(imodules);
745        includedPackageElements = Collections.unmodifiableSet(ipackages);
746        includedTypeElements = Collections.unmodifiableSet(iclasses);
747    }
748
749    /*
750     * Computes the included packages and freezes the specified packages list.
751     */
752    private void computeSpecifiedPackages() throws ToolException {
753
754        computeSubpackages();
755
756        Set<PackageElement> packlist = new LinkedHashSet<>();
757        cmdLinePackages.forEach((modpkg) -> {
758            PackageElement pkg;
759            if (modpkg.hasModule()) {
760                ModuleElement mdle = toolEnv.elements.getModuleElement(modpkg.moduleName);
761                pkg = toolEnv.elements.getPackageElement(mdle, modpkg.packageName);
762            } else {
763                pkg = toolEnv.elements.getPackageElement(modpkg.toString());
764            }
765
766            if (pkg != null) {
767                packlist.add(pkg);
768            } else {
769                messager.printWarningUsingKey("main.package_not_found", modpkg.toString());
770            }
771        });
772        specifiedPackageElements = Collections.unmodifiableSet(packlist);
773    }
774
775    /**
776     * Adds all classes as well as inner classes, to the specified
777     * list.
778     */
779    private void computeSpecifiedTypes() throws ToolException {
780        Set<TypeElement> classes = new LinkedHashSet<>();
781          classDecList.forEach((def) -> {
782            TypeElement te = (TypeElement) def.sym;
783            if (te != null) {
784                addAllClasses(classes, te, true);
785            }
786        });
787        for (String className : classArgList) {
788            TypeElement te = toolEnv.loadClass(className);
789            if (te == null) {
790                String text = messager.getText("javadoc.class_not_found", className);
791                throw new ToolException(CMDERR, text);
792            } else {
793                addAllClasses(classes, te, true);
794            }
795        }
796        specifiedTypeElements = Collections.unmodifiableSet(classes);
797    }
798
799    private void addFilesForParser(Collection<JavaFileObject> result,
800            Collection<ModulePackage> collection,
801            boolean recurse) throws ToolException {
802        for (ModulePackage modpkg : collection) {
803            toolEnv.notice("main.Loading_source_files_for_package", modpkg.toString());
804            List<JavaFileObject> files = getFiles(modpkg, recurse);
805            if (files.isEmpty()) {
806                String text = messager.getText("main.no_source_files_for_package",
807                        modpkg.toString());
808                throw new ToolException(CMDERR, text);
809            } else {
810                result.addAll(files);
811            }
812        }
813    }
814
815    /**
816     * Returns an aggregated list of java file objects from the items
817     * specified on the command line. The packages specified should not
818     * recurse, however sub-packages should recurse into the sub directories.
819     * @return a list of java file objects
820     * @throws IOException if an error occurs
821     */
822    List<JavaFileObject> getFilesToParse() throws ToolException {
823        List<JavaFileObject> result = new ArrayList<>();
824        addFilesForParser(result, cmdLinePackages, false);
825        addFilesForParser(result, subPackages, true);
826        return result;
827    }
828
829    /**
830     * Returns the set of source files for a package.
831     *
832     * @param packageName the specified package
833     * @return the set of file objects for the specified package
834     * @throws ToolException if an error occurs while accessing the files
835     */
836    private List<JavaFileObject> getFiles(ModulePackage modpkg,
837            boolean recurse) throws ToolException {
838        Entry e = getEntry(modpkg);
839        // The files may have been found as a side effect of searching for subpackages
840        if (e.files != null) {
841            return e.files;
842        }
843
844        ListBuffer<JavaFileObject> lb = new ListBuffer<>();
845        List<Location> locs = getLocation(modpkg);
846        if (locs.isEmpty()) {
847            return Collections.emptyList();
848        }
849        String pname = modpkg.packageName;
850        for (Location packageLocn : locs) {
851            for (JavaFileObject fo : fmList(packageLocn, pname, sourceKinds, recurse)) {
852                String binaryName = fm.inferBinaryName(packageLocn, fo);
853                String simpleName = getSimpleName(binaryName);
854                if (isValidClassName(simpleName)) {
855                    lb.append(fo);
856                }
857            }
858        }
859        return lb.toList();
860    }
861
862    private ModuleSymbol findModuleOfPackageName(String packageName) {
863            Name pack = names.fromString(packageName);
864            for (ModuleSymbol msym : modules.allModules()) {
865                PackageSymbol p = syms.getPackage(msym, pack);
866                if (p != null && !p.members().isEmpty()) {
867                    return msym;
868                }
869            }
870            return null;
871    }
872
873    private List<Location> getLocation(ModulePackage modpkg) throws ToolException {
874        if (locations.size() == 1 && !locations.contains(StandardLocation.MODULE_SOURCE_PATH)) {
875            return Collections.singletonList(locations.get(0));
876        }
877
878        if (modpkg.hasModule()) {
879            return getModuleLocation(locations, modpkg.moduleName);
880        }
881        // TODO: handle invalid results better.
882        ModuleSymbol msym = findModuleOfPackageName(modpkg.packageName);
883        if (msym == null) {
884            return Collections.emptyList();
885        }
886        return getModuleLocation(locations, msym.name.toString());
887    }
888
889    boolean haveSourceLocationWithModule = false;
890
891    private List<Location> getModuleLocation(List<Location> locations, String msymName) throws ToolException {
892        List<Location> out = new ArrayList<>();
893        // search in the patch module first, this overrides others
894        if (locations.contains(StandardLocation.PATCH_MODULE_PATH)) {
895            Location loc = getModuleLocation(StandardLocation.PATCH_MODULE_PATH, msymName);
896            if (loc != null)
897                out.add(loc);
898        }
899        for (Location location : locations) {
900            // skip patch module, already done
901            if (location == StandardLocation.PATCH_MODULE_PATH) {
902                continue;
903            } else if (location == StandardLocation.MODULE_SOURCE_PATH) {
904                Location loc = getModuleLocation(location, msymName);
905                if (loc != null)
906                    out.add(loc);
907            } else if (location == StandardLocation.SOURCE_PATH) {
908                haveSourceLocationWithModule = true;
909                out.add(StandardLocation.SOURCE_PATH);
910            }
911        }
912        return out;
913    }
914
915    private Location getModuleLocation(Location location, String msymName) throws ToolException {
916        try {
917            return fm.getLocationForModule(location, msymName);
918        } catch (IOException ioe) {
919            String text = messager.getText("main.doclet_could_not_get_location", msymName);
920            throw new ToolException(ERROR, text, ioe);
921        }
922    }
923
924    private Entry getEntry(String name) {
925        return getEntry(new ModulePackage(name));
926    }
927
928    private Entry getEntry(ModulePackage modpkg) {
929        Entry e = entries.get(modpkg.packageName);
930        if (e == null) {
931            entries.put(modpkg.packageName, e = new Entry(modpkg));
932        }
933        return e;
934    }
935
936    private String getPackageName(String name) {
937        int lastDot = name.lastIndexOf(".");
938        return (lastDot == -1 ? "" : name.substring(0, lastDot));
939    }
940
941    private String getSimpleName(String name) {
942        int lastDot = name.lastIndexOf(".");
943        return (lastDot == -1 ? name : name.substring(lastDot + 1));
944    }
945
946    /**
947     * Adds all inner classes of this class, and their inner classes recursively, to the list
948     */
949    private void addAllClasses(Collection<TypeElement> list, TypeElement typeElement, boolean filtered) {
950        ClassSymbol klass = (ClassSymbol)typeElement;
951        try {
952            // eliminate needless checking, do this first.
953            if (list.contains(klass)) return;
954            // ignore classes with invalid Java class names
955            if (!JavadocTool.isValidClassName(klass.name.toString())) return;
956            if (filtered && !isTypeElementSelected(klass)) return;
957            list.add(klass);
958            for (Symbol sym : klass.members().getSymbols(NON_RECURSIVE)) {
959                if (sym != null && sym.kind == Kind.TYP) {
960                    ClassSymbol s = (ClassSymbol)sym;
961                    addAllClasses(list, s, filtered);
962                }
963            }
964        } catch (CompletionFailure e) {
965            if (e.getMessage() != null)
966                messager.printWarning(e.getMessage());
967            else
968                messager.printWarningUsingKey("main.unexpected.exception", e);
969        }
970    }
971
972    /**
973     * Returns a list of all classes contained in this package, including
974     * member classes of those classes, and their member classes, etc.
975     */
976    private void addAllClasses(Collection<TypeElement> list, PackageElement pkg) {
977        boolean filtered = true;
978        PackageSymbol sym = (PackageSymbol)pkg;
979        for (Symbol isym : sym.members().getSymbols(NON_RECURSIVE)) {
980            addAllClasses(list, (TypeElement)isym, filtered);
981        }
982    }
983
984    private boolean isTypeElementSelected(TypeElement te) {
985        return (xclasses || toolEnv.getFileKind(te) == SOURCE) && isSelected(te);
986    }
987
988    SimpleElementVisitor9<Boolean, Void> visibleElementVisitor = null;
989    /**
990     * Returns true if the element is selected, by applying
991     * the access filter checks. Special treatment is applied to
992     * types, for a top level type the access filter applies completely,
993     * however if is a nested type then it is allowed either  if
994     * the enclosing is a static or the enclosing is also selected.
995     *
996     * @param e the element to be checked
997     * @return true if the element is visible
998     */
999    public boolean isSelected(Element e) {
1000        if (toolEnv.isSynthetic((Symbol) e)) {
1001            return false;
1002        }
1003        if (visibleElementVisitor == null) {
1004            visibleElementVisitor = new SimpleElementVisitor9<Boolean, Void>() {
1005                @Override
1006                public Boolean visitType(TypeElement e, Void p) {
1007                    if (!accessFilter.checkModifier(e)) {
1008                        return false; // it is not allowed
1009                    }
1010                    Element encl = e.getEnclosingElement();
1011
1012                    // check if nested
1013                    if (encl.getKind() == ElementKind.PACKAGE)
1014                        return true; // top-level class, allow it
1015
1016                    // is enclosed static
1017                    if (encl.getModifiers().contains(Modifier.STATIC))
1018                        return true; // allowed
1019
1020                    // check the enclosing
1021                    return visit(encl);
1022                }
1023
1024                @Override
1025                protected Boolean defaultAction(Element e, Void p) {
1026                    return accessFilter.checkModifier(e);
1027                }
1028
1029                @Override
1030                public Boolean visitUnknown(Element e, Void p) {
1031                    throw new AssertionError("unkown element: " + p);
1032                }
1033            };
1034        }
1035        return visibleElementVisitor.visit(e);
1036    }
1037
1038    private class IncludedVisitor extends SimpleElementVisitor9<Boolean, Void> {
1039        final private Set<Element> includedCache;
1040
1041        public IncludedVisitor() {
1042            includedCache = new LinkedHashSet<>();
1043        }
1044
1045        @Override
1046        public Boolean visitModule(ModuleElement e, Void p) {
1047            // deduced by specified and/or requires expansion
1048            return includedModuleElements.contains(e);
1049        }
1050
1051        @Override
1052        public Boolean visitPackage(PackageElement e, Void p) {
1053            // deduced by specified or downward expansions
1054            return includedPackageElements.contains(e);
1055        }
1056
1057        @Override
1058        public Boolean visitType(TypeElement e, Void p) {
1059            if (includedTypeElements.contains(e)) {
1060                return true;
1061            }
1062            if (isTypeElementSelected(e)) {
1063                // Class is nameable from top-level and
1064                // the class and all enclosing classes
1065                // pass the modifier filter.
1066                PackageElement pkg = toolEnv.elements.getPackageOf(e);
1067                if (specifiedPackageElements.contains(pkg)) {
1068                    return true;
1069                }
1070                Element enclosing = e.getEnclosingElement();
1071                if (enclosing != null) {
1072                    switch(enclosing.getKind()) {
1073                        case PACKAGE:
1074                            return specifiedPackageElements.contains((PackageElement)enclosing);
1075                        case CLASS: case INTERFACE: case ENUM: case ANNOTATION_TYPE:
1076                            return visit((TypeElement) enclosing);
1077                        default:
1078                            throw new AssertionError("unknown element: " + enclosing);
1079                    }
1080                }
1081            }
1082            return false;
1083        }
1084
1085        // members
1086        @Override
1087        public Boolean defaultAction(Element e, Void p) {
1088            if (includedCache.contains(e))
1089                return true;
1090            if (visit(e.getEnclosingElement()) && isSelected(e)) {
1091                switch(e.getKind()) {
1092                    case ANNOTATION_TYPE: case CLASS: case ENUM: case INTERFACE:
1093                    case MODULE: case OTHER: case PACKAGE:
1094                        throw new AssertionError("invalid element for this operation: " + e);
1095                    default:
1096                        // the only allowed kinds in the cache are "members"
1097                        includedCache.add(e);
1098                        return true;
1099                }
1100            }
1101            return false;
1102        }
1103
1104        @Override
1105        public Boolean visitUnknown(Element e, Void p) {
1106            throw new AssertionError("unknown element: " + e);
1107        }
1108
1109    }
1110
1111    class Entry {
1112        final ModulePackage modpkg;
1113        Boolean excluded = false;
1114        com.sun.tools.javac.util.List<JavaFileObject> files;
1115
1116        Entry(ModulePackage modpkg) {
1117            this.modpkg = modpkg;
1118        }
1119
1120        Entry(String name) {
1121            modpkg = new ModulePackage(name);
1122        }
1123
1124        boolean isExcluded() {
1125            return excluded;
1126        }
1127
1128        @Override
1129        public String toString() {
1130            return "Entry{" + "modpkg=" + modpkg + ", excluded=" + excluded + ", files=" + files + '}';
1131        }
1132    }
1133
1134    /**
1135     * A container class to retrieve the module and package pair
1136     * from a parsed qualified package name.
1137     */
1138    static class ModulePackage {
1139
1140        public final String moduleName;
1141        public final String packageName;
1142
1143        ModulePackage(String modulename, String packagename) {
1144            this.moduleName = modulename;
1145            this.packageName = packagename;
1146        }
1147
1148        ModulePackage(ModuleElement msym, String packagename) {
1149            this.moduleName = msym.toString();
1150            this.packageName = packagename;
1151        }
1152
1153        ModulePackage(String name) {
1154            String a[] = name.split("/");
1155            if (a.length == 2) {
1156                this.moduleName = a[0];
1157                this.packageName = a[1];
1158            } else {
1159                moduleName = null;
1160                packageName = name;
1161            }
1162        }
1163
1164        boolean hasModule() {
1165            return this.moduleName != null;
1166        }
1167
1168        @Override
1169        public boolean equals(Object obj) {
1170            if (obj instanceof ModulePackage) {
1171                ModulePackage that = (ModulePackage)obj;
1172                return this.toString().equals(that.toString());
1173            }
1174            return false;
1175        }
1176
1177        @Override
1178        public int hashCode() {
1179             return toString().hashCode();
1180        }
1181
1182        @Override
1183        public String toString() {
1184            return moduleName == null ? packageName : moduleName + "/" + packageName;
1185        }
1186    }
1187
1188    /**
1189     * A class which filters the access flags on classes, fields, methods, etc.
1190     *
1191     * @see javax.lang.model.element.Modifier
1192     */
1193
1194    static class ModifierFilter {
1195        /**
1196         * The allowed ElementKind that can be stored.
1197         */
1198        static final EnumSet<ElementKind> ALLOWED_KINDS = EnumSet.of(ElementKind.METHOD,
1199                                                    ElementKind.CLASS,
1200                                                    ElementKind.PACKAGE,
1201                                                    ElementKind.MODULE);
1202
1203        // all possible accesss levels allowed for each element
1204        private final EnumMap<ElementKind, EnumSet<AccessKind>> filterMap =
1205                new EnumMap<>(ElementKind.class);
1206
1207        // the specified access level for each element
1208        private final EnumMap<ElementKind, AccessKind> accessMap =
1209                new EnumMap<>(ElementKind.class);
1210
1211        /**
1212         * Constructor - Specify a filter.
1213         *
1214         * @param accessSet an Access filter.
1215         */
1216        ModifierFilter(Map<ToolOption, Object> opts) {
1217
1218            AccessKind accessValue = null;
1219            for (ElementKind kind : ALLOWED_KINDS) {
1220                switch (kind) {
1221                    case METHOD:
1222                        accessValue  = (AccessKind)opts.get(ToolOption.SHOW_MEMBERS);
1223                        break;
1224                    case CLASS:
1225                        accessValue  = (AccessKind)opts.get(ToolOption.SHOW_TYPES);
1226                        break;
1227                    case PACKAGE:
1228                        accessValue  = (AccessKind)opts.get(ToolOption.SHOW_PACKAGES);
1229                        break;
1230                    case MODULE:
1231                        accessValue  = (AccessKind)opts.get(ToolOption.SHOW_MODULE_CONTENTS);
1232                        break;
1233                    default:
1234                        throw new AssertionError("unknown element: " + kind);
1235
1236                }
1237                accessMap.put(kind, accessValue);
1238                filterMap.put(kind, getFilterSet(accessValue));
1239            }
1240        }
1241
1242        static EnumSet<AccessKind> getFilterSet(AccessKind acccessValue) {
1243            switch (acccessValue) {
1244                case PUBLIC:
1245                    return EnumSet.of(AccessKind.PUBLIC);
1246                case PROTECTED:
1247                default:
1248                    return EnumSet.of(AccessKind.PUBLIC, AccessKind.PROTECTED);
1249                case PACKAGE:
1250                    return EnumSet.of(AccessKind.PUBLIC, AccessKind.PROTECTED, AccessKind.PACKAGE);
1251                case PRIVATE:
1252                    return EnumSet.allOf(AccessKind.class);
1253            }
1254        }
1255
1256        public AccessKind getAccessValue(ElementKind kind) {
1257            if (!ALLOWED_KINDS.contains(kind)) {
1258                throw new IllegalArgumentException("not allowed: " + kind);
1259            }
1260            return accessMap.getOrDefault(kind, AccessKind.PROTECTED);
1261        }
1262
1263        /**
1264         * Returns true if access is allowed.
1265         *
1266         * @param e the element in question
1267         * @return whether the modifiers pass this filter
1268         */
1269        public boolean checkModifier(Element e) {
1270            Set<Modifier> modifiers = e.getModifiers();
1271            AccessKind fflag = AccessKind.PACKAGE;
1272            if (modifiers.contains(Modifier.PUBLIC)) {
1273                fflag = AccessKind.PUBLIC;
1274            } else if (modifiers.contains(Modifier.PROTECTED)) {
1275                fflag = AccessKind.PROTECTED;
1276            } else if (modifiers.contains(Modifier.PRIVATE)) {
1277                fflag = AccessKind.PRIVATE;
1278            }
1279            EnumSet<AccessKind> filterSet = filterMap.get(getAllowedKind(e.getKind()));
1280            return filterSet.contains(fflag);
1281        }
1282
1283        // convert a requested element kind to an allowed access kind
1284        private ElementKind getAllowedKind(ElementKind kind) {
1285            switch (kind) {
1286                case CLASS: case METHOD: case MODULE: case PACKAGE:
1287                    return kind;
1288                case ANNOTATION_TYPE: case ENUM: case INTERFACE:
1289                    return ElementKind.CLASS;
1290                case CONSTRUCTOR: case ENUM_CONSTANT: case EXCEPTION_PARAMETER:
1291                case FIELD: case INSTANCE_INIT: case LOCAL_VARIABLE: case PARAMETER:
1292                case RESOURCE_VARIABLE: case STATIC_INIT: case TYPE_PARAMETER:
1293                    return ElementKind.METHOD;
1294                default:
1295                    throw new AssertionError("unsupported kind: " + kind);
1296            }
1297        }
1298    } // end ModifierFilter
1299}
1300