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