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