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